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 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 ); this.recoverToScreenMat = new THREE.ShaderMaterial({ uniforms: copyUniforms, vertexShader:copyShader.vertexShader, fragmentShader: copyShader.fragmentShader, 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){ 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.useRTskybox && Potree.settings.displayMode == 'showPanos' ? 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()){ if(params.rtEDL){ renderer.setRenderTarget( params.rtEDL); renderer.clear() }else{ var rtEDL = this.getRtEDL(params.viewport) if(rtEDL){ renderer.setRenderTarget( rtEDL ); renderer.clear( true, true, true ); } } } //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) } } 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(); } } render(params={}){ const viewer = this.viewer; let camera = params.camera ? params.camera : viewer.scene.getActiveCamera(); const resolution = params.viewport ? params.viewport.resolution : this.viewer.renderer.getSize(new THREE.Vector2());//突然发现mobile用resolution2点云会放大 let rtEDL = Features.EXT_DEPTH.isSupported() && camera.type != "OrthographicCamera" && !params.dontRenderRtEDL && (params.rtEDL || this.getRtEDL(params.viewport)) // 平面相机不用depthTex直接打开depthTest?且不使用edl let useEDL = viewer.useEDL && rtEDL && Potree.settings.displayMode != 'showPanos' let target = params.target || null viewer.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){ Potree.Utils.setCameraLayers(camera, ['skybox']) let useDepthTex if(Potree.settings.displayMode == 'showPanos' && viewer.images360.currentPano.pointcloud.hasDepthTex && rtEDL){//渲染深度图 useDepthTex = true viewer.renderer.setRenderTarget(rtEDL) //将带有深度图的skybox画在rtEDL一下,这样就不需要绘制后边的点云了 viewer.renderer.render(viewer.scene.scene, camera); viewer.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(viewer.renderer, this.recoverToScreenMat, target); }else{ viewer.renderer.render(viewer.scene.scene, camera); } }else{ viewer.renderer.render(viewer.scene.scene, camera); } if(useDepthTex)return } const visiblePointClouds2 = viewer.scene.pointclouds.filter(pc => Potree.Utils.getObjVisiByReason(pc,'datasetSelection') ); //需要绘制到rtEDL的 const showPointClouds = params.magnifier ? visiblePointClouds2.length>0 : viewer.scene.pointclouds.some(e=>e.visible) //是否有需要绘制到屏幕的 visiblePointClouds2.forEach(e=>{//为了绘制到depthTexture,先显示(展示全景图时隐藏了点云,所以需要显示下。且放大镜需要绘制点云) e.oldVisi = e.visible e.visible = true; }) Potree.Utils.setCameraLayers(camera, ['pointcloud']) 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/* Features.EXT_DEPTH.isSupported() && !params.dontRenderRtEDL */){ //借用rtEDL存储深度信息 viewer.renderer.setRenderTarget( rtEDL ); if(visiblePointClouds2.length>0){ //渲染scenePointCloud到rtEDL viewer.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, }); } if(Potree.settings.intersectOnObjs){// model也要渲染到rtEDL camera.layers.set(Potree.config.renderLayers.model); viewer.objs.traverse(e=>{if(e.material)e._OlddepthWrite = e.material.depthWrite, e.material.depthWrite = true}) //否则半透明的mesh无法遮住测量线 viewer.renderer.render(viewer.scene.scene, camera); viewer.objs.traverse(e=>{if(e.material)e.material.depthWrite = e._OlddepthWrite}) //缺点:半透明的model 就算完全透明, 也会遮住测量线 } } } //渲染到rtEDL完毕 viewer.dispatchEvent({type: "render.pass.scene", viewer: viewer }); viewer.renderer.setRenderTarget( target ); if(!params.magnifier)visiblePointClouds2.forEach(e=>{//放大镜显示点云 e.visible = e.oldVisi }) if(showPointClouds){ //绘制点云到画布 if(useEDL) { //设置edlMaterial //Features.EXT_DEPTH不支持的话不会到这一块 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(viewer.renderer, this.edlMaterial, target); //相当于一个描边后期特效。 缺点: 因为target上的没有抗锯齿,所以点云在晃动镜头时会不稳定地闪烁1px位置。优点:比不打开edl少绘制一次点云,更流畅了?! }else{ //渲染点云 (直接用rtEDL上的会失去抗锯齿, 导致频闪、密集时出现条纹, 自己写抗锯齿也要渲染好几次。另外透明度也要处理下) let prop = { shadowMaps: lights.length > 0 ? [this.shadowMap] : null, clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)) , notAdditiveBlending: Potree.settings.notAdditiveBlending//add 否则透明的点云会挡住后面的模型。 加上这句后竟然透明不会叠加了! } viewer.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的贴图渲染不需要顶层、中间层只需要点云。 */ }