import * as THREE from "../../libs/three.js/build/three.module.js"; import {PointCloudSM} from "../utils/PointCloudSM.js"; import {ExtendEyeDomeLightingMaterial} from "../materials/ExtendEyeDomeLightingMaterial.js"; import {SphereVolume} from "../utils/Volume.js"; import {Utils} from "../utils.js"; import {copyShader} from '../materials/shaders/otherShaders.js' import {Features} from "../Features.js"; import {easing} from "../custom/utils/transitions.js"; import Common from "../custom/utils/Common.js"; //import DepthTexSampler from "../custom/utils/DepthTexSampler.js"; export class EDLRenderer{//Eye-Dome Lighting 眼罩照明 constructor(viewer){ this.viewer = viewer; this.edlMaterial = null; //this.rtRegular; this.rtEDLs = new Map this.gl = viewer.renderer.getContext(); //反正也没用到,注释了: //this.shadowMap = new PointCloudSM(this.viewer.pRenderer); viewer.addEventListener('resize',this.resize.bind(this)) this.initEDL(viewer) } initEDL(viewer){ if (this.edlMaterial != null || !Features.EXT_DEPTH.isSupported()){ return; } this.edlMaterial = new ExtendEyeDomeLightingMaterial(); this.edlMaterial.depthTest = true; this.edlMaterial.depthWrite = true; this.edlMaterial.transparent = true; let copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms ); let {vs,fs} = Common.changeShaderToWebgl2(copyShader.vertexShader, copyShader.fragmentShader, 'ShaderMaterial') this.recoverToScreenMat = new THREE.ShaderMaterial({ uniforms: copyUniforms, vertexShader: vs, fragmentShader: fs, transparent: true, defines:{ useDepth: true //开启后,其他物体才能被遮挡 } }) /* let copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms ); this.copyMaterial = new THREE.ShaderMaterial( { uniforms: copyUniforms, vertexShader: copyShader.vertexShader, fragmentShader: copyShader.fragmentShader, //premultipliedAlpha: true, transparent: true, //blending: THREE.AdditiveBlending, depthTest: false, depthWrite: false }); */ if(Potree.settings.useRTskybox != Potree.settings.useRTPoint){//如果两个只开了一个 viewer.images360.addEventListener('endChangeMode',()=>{ this.resize({viewport:viewer.mainViewport}) }) } //this.depthTexSampler = new DepthTexSampler(this); }; resize(e){ if(Features.EXT_DEPTH.isSupported()){ let viewport = e.viewport let size = ( Potree.settings.displayMode == 'showPanos' ? Potree.settings.useRTskybox : Potree.settings.useRTPoint) ? viewport.resolution2 : viewport.resolution; //若要渲染skybox,需要和设备一样精度的rt this.getRtEDL(viewport).setSize( size.x, size.y ); //理论上可以是任意尺寸,但会影响精度,且aspect最好和渲染的一致 } } clearTargets(params={}){ const viewer = this.viewer; const {renderer} = viewer; const oldTarget = renderer.getRenderTarget(); if(params.target){//add renderer.setRenderTarget( params.target); renderer.clear() } if(Features.EXT_DEPTH.isSupported()){ let rtEDL = params.rtEDL || this.getRtEDL(params.viewport) if(rtEDL){ renderer.setRenderTarget( rtEDL ); let oldAlpha = renderer.getClearAlpha() renderer.setClearAlpha(0) renderer.clear( true, true, true ); renderer.setClearAlpha(oldAlpha) } } //renderer.setRenderTarget( this.rtRegular ); //renderer.clear( true, true, false ); renderer.setRenderTarget(oldTarget); } getRtEDL(viewport){//根据不同viewport返回rtEDL的texture if(!viewport){ console.warn('getRtEDL没传viewport!!!! !!!!!!!!!!') viewport = viewer.mainViewport } var rtEDL = this.rtEDLs.get(viewport) if(!rtEDL){ if(Features.EXT_DEPTH.isSupported()){ rtEDL = new THREE.WebGLRenderTarget(viewport.resolution.x, viewport.resolution.y, { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, type: THREE.FloatType, depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType) }); //注: 部分手机在resize时会崩溃,经检验去掉rtEDL的resize可以解决,所以更应该注释掉这个 this.rtEDLs.set(viewport, rtEDL) } } //注:当pc窗口缩小,deviceRatio变小后,resolution2会小于resolution,然后遮挡会出现精度损失而有细微不准,是正常现象。 return rtEDL } renderShadowMap(visiblePointClouds, camera, lights){ const {viewer} = this; const doShadows = lights.length > 0 && !(lights[0].disableShadowUpdates); if(doShadows){ let light = lights[0]; this.shadowMap.setLight(light); let originalAttributes = new Map(); for(let pointcloud of viewer.scene.pointclouds){ // TODO IMPORTANT !!! check originalAttributes.set(pointcloud, pointcloud.material.activeAttributeName); pointcloud.material.disableEvents(); pointcloud.material.activeAttributeName = "depth"; //pointcloud.material.pointColorType = PointColorType.DEPTH; } this.shadowMap.render(viewer.scene.scenePointCloud, camera); for(let pointcloud of visiblePointClouds){ let originalAttribute = originalAttributes.get(pointcloud); // TODO IMPORTANT !!! check pointcloud.material.activeAttributeName = originalAttribute; pointcloud.material.enableEvents(); } viewer.shadowTestCam.updateMatrixWorld(); viewer.shadowTestCam.matrixWorldInverse.copy(viewer.shadowTestCam.matrixWorld).invert(); viewer.shadowTestCam.updateProjectionMatrix(); } } getIfRtEDL_(params={}){ return (Potree.settings.pointEnableRT && Potree.settings.displayMode == 'showPointCloud' || Potree.settings.displayMode == 'showPanos'/* && viewer.images360.currentPano.pointcloud.hasDepthTex */ || viewer.useEDL) && Features.EXT_DEPTH.isSupported() && params.camera.type != "OrthographicCamera" && !params.dontRenderRtEDL && (params.rtEDL || this.getRtEDL(params.viewport)) // 平面相机不用depthTex直接打开depthTest?且不使用edl } getIfuseEdl(params={} ){ return viewer.useEDL && Potree.settings.displayMode != 'showPanos' } canUseRTPoint(){ return Potree.settings.useRTPoint && !viewer.scene.pointclouds.some(e=>e.visible && e.material.opacity < 1) && !viewer.objs.children.some(e=>e.opacity < 1) //透明mesh发黑,透明无效 } render(params={}){ const viewer = this.viewer; let camera = params.camera ? params.camera : viewer.scene.getActiveCamera(); let rtEDL = this.getIfRtEDL_(params) let useEDL = rtEDL && this.getIfuseEdl(params) let target = params.target || null const resolution = (rtEDL && Potree.settings.useRTPoint) ? new THREE.Vector2(rtEDL.width,rtEDL.height) : params.target ? new THREE.Vector2(params.target.width, params.target.height ) : params.viewport ? params.viewport.resolution2 : this.viewer.renderer.getSize(new THREE.Vector2());//截图时需要用target的大小 let renderer = params.renderer || viewer.renderer let pRenderer = params.pRenderer || viewer.pRenderer renderer.setRenderTarget(target); //viewer.dispatchEvent({type: "render.pass.begin",viewer: viewer}); let lights = []; /* viewer.scene.scene.traverse(node => { if(node.type === "SpotLight"){ lights.push(node); } }); */ //skybox 全景图 if(!params.magnifier){ if(Potree.settings.displayMode == 'showPanos' || Potree.settings.testCube ){ Potree.Utils.setCameraLayers(camera, ['skybox']) //注:model等也可能是skybox if((Potree.settings.displayMode == 'showPanos' ) && (viewer.images360.currentPano.pointcloud.hasDepthTex || Potree.settings.modelSkybox && viewer.images360.currentPano.pointcloud.is4dkkModel) && rtEDL){//渲染深度图 renderer.setRenderTarget(rtEDL) //将带有深度图的skybox画在rtEDL一下,这样就不需要绘制后边的点云了 renderer.render(viewer.scene.scene, camera); renderer.setRenderTarget(target); if(Potree.settings.useRTskybox){//直接使用rtEDL,但是会失去抗锯齿,不知在skybox上需要抗锯齿吗 this.recoverToScreenMat.uniforms.depthTex.value = rtEDL.depthTexture this.recoverToScreenMat.uniforms.tDiffuse.value = rtEDL.texture Utils.screenPass.render(renderer, this.recoverToScreenMat, target); }else{ renderer.render(viewer.scene.scene, camera); } }else{ renderer.render(viewer.scene.scene, camera); } if(Potree.settings.displayMode == 'showPanos' ){ if(Potree.settings.fastTran && viewer.images360.fastTranMaskPass.enabled){ viewer.images360.fastTranMaskPass.render() } if( viewer.objs.children.length == 0 || !params.useModelOnRT ) return //没有模型需要绘制遮挡 } } } const pointclouds = viewer.scene.pointclouds.filter(pc => !pc.isSplat) const visiblePointClouds2 = pointclouds.filter(pc => pc.visible/* Potree.Utils.getObjVisiByReason(pc,'datasetSelection') */ ); //需要绘制到rtEDL的 const showPointClouds = params.magnifier ? visiblePointClouds2.length>0 : pointclouds.some(e=>e.visible) //是否有需要绘制到屏幕的 /* visiblePointClouds2.forEach(e=>{//为了绘制到depthTexture,先显示(展示全景图时隐藏了点云,所以需要显示下 ) e.oldVisi = e.visible e.visible = true; }) */ //好难写- -,太多东西了,无depTex时要把不显示的点云和模型绘制到rt上,但是rt又可能直接绘制到画面。有的模型是要在全景模式显示的 //所以决定去掉不显示的点云,无深度图的话就无遮挡 http://192.168.0.21/index.php?m=bug&f=view&bugID=49575 Potree.Utils.setCameraLayers(camera, ['pointcloud']) //设置多少都会渲染出来,因为渲染里没有sort //camera.layers.set(Potree.config.renderLayers.pointcloud); //TODO adapt to multiple lights //this.renderShadowMap(visiblePointClouds2, camera, lights); //??????? { for (let pointcloud of visiblePointClouds2) { let material = pointcloud.material; let octreeSize = pointcloud.pcoGeometry.boundingBox.getSize(new THREE.Vector3()).x; material.fov = THREE.Math.degToRad(camera.fov) material.resolution = resolution material.spacing = pointcloud.pcoGeometry.spacing; // * Math.max(this.scale.x, this.scale.y, this.scale.z); material.near = camera.near; material.far = camera.far; material.uniforms.octreeSize.value = octreeSize if(useEDL ){ material.useEDL = true; //material.fakeEDL = false; //add }else{ material.useEDL = false; //material.fakeEDL = true; //add 使也输出深度 } } if(rtEDL ){ //借用rtEDL存储深度信息 renderer.setRenderTarget( rtEDL ); if(visiblePointClouds2.length>0){ //渲染scenePointCloud到rtEDL pRenderer.render(viewer.scene.scenePointCloud, camera, rtEDL, { shadowMaps: lights.length > 0 ? [this.shadowMap] : null, clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)), transparent: false, // 因透明时不写深度,所以不透明。 这也导致透明时不能直接使用rtEDL绘制到屏幕 notAdditiveBlending: Potree.settings.notAdditiveBlending }); } if(Potree.settings.intersectOnObjs){ Potree.Utils.setCameraLayers(camera, ['model','light']) viewer.objs.traverse(e=>{if(e.material)e._OlddepthWrite = e.material.depthWrite, e.material.depthWrite = true}) //否则半透明的mesh无法遮住测量线 renderer.render(viewer.scene.scene, camera); viewer.objs.traverse(e=>{if(e.material)e.material.depthWrite = e._OlddepthWrite}) } } } //渲染到rtEDL完毕 viewer.dispatchEvent({type: "render.pass.scene", viewer: viewer }); renderer.setRenderTarget( target ); /* if(!params.magnifier)visiblePointClouds2.forEach(e=>{//放大镜显示点云 e.visible = e.oldVisi }) */ {//绘制点云到画布 if(useEDL){ //设置edlMaterial //Features.EXT_DEPTH不支持的话不会到这一块 if(showPointClouds){ const uniforms = this.edlMaterial.uniforms; uniforms.resolution.value.copy(resolution) uniforms.edlStrength.value = viewer.edlStrength; uniforms.radius.value = viewer.edlRadius; uniforms.useEDL.value = 1;//add let proj = camera.projectionMatrix; let projArray = new Float32Array(16); projArray.set(proj.elements); uniforms.uProj.value = projArray; uniforms.uEDLColor.value = rtEDL.texture; uniforms.opacity.value = viewer.edlOpacity; // HACK Utils.screenPass.render(renderer, this.edlMaterial, target); //相当于一个描边后期特效。 缺点: 因为target上的没有抗锯齿,所以点云在晃动镜头时会不稳定地闪烁1px位置。优点:比不打开edl少绘制一次点云,更流畅了?! } }else if(this.canUseRTPoint() && rtEDL ){ //半透明点云在clearAlpha为非1的状态下截图 或 在useRTPoints时绘制到屏幕上透明度不对,亮度变低 。深度值遮挡也不对。 只能在半透明时关闭一下useRTPoint this.recoverToScreenMat.uniforms.tDiffuse.value = rtEDL.texture; if(this.recoverToScreenMat.defines.useDepth){ this.recoverToScreenMat.uniforms.depthTex.value = rtEDL.depthTexture; } Utils.screenPass.render(renderer, this.recoverToScreenMat, target/* , Potree.settings.useFxaa && viewer.composer2 */); //这时候 params_.useModelOnRT 应该和 Potree.settings.intersectOnObjs 相等 否则出错 }else{ //渲染点云 (直接用rtEDL上的会失去抗锯齿, 导致频闪、密集时出现条纹, 自己写抗锯齿也要渲染好几次。另外透明度也要处理下) if(showPointClouds){ let prop = { shadowMaps: lights.length > 0 ? [this.shadowMap] : null, clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)) , notAdditiveBlending: Potree.settings.notAdditiveBlending//add 否则透明的点云会挡住后面的模型。 加上这句后竟然透明不会叠加了! } pRenderer.render(viewer.scene.scenePointCloud, camera, null , prop); } } } /* visiblePointClouds2.forEach(e=>{ e.visible = e.oldVisi }) */ //viewer.dispatchEvent({type: "render.pass.end",viewer: viewer}); } /* 渲染顺序: 底层:背景 -> skybox(也可中间) 中间层(含有深度信息):1 点云、marker等mesh, 2 测量线(现在被做成借用depthTex 顶层:maginifier magnifier的贴图渲染不需要顶层、中间层只需要点云。 */ }