import * as THREE from "../../libs/three.js/build/three.module.js"; import {PointCloudSM} from "../utils/PointCloudSM.js"; import {EyeDomeLightingMaterial} from "../materials/EyeDomeLightingMaterial.js"; import {SphereVolume} from "../utils/Volume.js"; import {Utils} from "../utils.js"; import {copyShader} from '../materials/shaders/otherShaders' export class EDLRenderer{//Eye-Dome Lighting 眼罩照明 constructor(viewer){ this.viewer = viewer; this.edlMaterial = null; this.rtRegular; this.rtEDL; this.gl = viewer.renderer.getContext(); this.shadowMap = new PointCloudSM(this.viewer.pRenderer); viewer.addEventListener('resize',this.resize.bind(this)) this.initEDL() } initEDL(){ if (this.edlMaterial != null) { return; } this.edlMaterial = new EyeDomeLightingMaterial(); this.edlMaterial.depthTest = true; this.edlMaterial.depthWrite = true; this.edlMaterial.transparent = true; this.rtEDL = new THREE.WebGLRenderTarget(1024, 1024, { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, type: THREE.FloatType, depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType) }); this.rtRegular = new THREE.WebGLRenderTarget(1024, 1024, { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType) }); 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 }); }; resize(e){ //width, height /* if(this.screenshot){ width = this.screenshot.target.width; height = this.screenshot.target.height; } */ let resolution = e.viewport.resolution2 this.rtEDL.setSize(resolution.x, resolution.y); this.rtRegular.setSize(resolution.x, resolution.y); } clearTargets(params){ const viewer = this.viewer; const {renderer} = viewer; const oldTarget = renderer.getRenderTarget(); if(params.target){//add renderer.setRenderTarget( params.target); renderer.clear() } if(params.rtEDL){ renderer.setRenderTarget( params.rtEDL); renderer.clear() }else{ renderer.setRenderTarget( this.rtEDL ); renderer.clear( true, true, true ); } renderer.setRenderTarget( this.rtRegular ); renderer.clear( true, true, false ); renderer.setRenderTarget(oldTarget); } clear(params={}){ //this.initEDL(); const viewer = this.viewer; const {renderer, background} = viewer; if(background === "skybox"){ renderer.setClearColor(0x000000, 0); } else if (background === 'gradient') { renderer.setClearColor(0x000000, 0); } else if (background === 'black') { renderer.setClearColor(0x000000, 1); } else if (background === 'white') { renderer.setClearColor(0xFFFFFF, 1); } else { renderer.setClearColor(0x000000, 0); } params.target || renderer.clear(); this.clearTargets(params); } 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={}){ /* 渲染顺序: 底层:背景 -> skybox(也可中间) 中间层(含有深度信息):1 点云、marker等mesh, 2 测量线(现在被做成借用depthTex 顶层:maginifier magnifier的贴图渲染不需要顶层、中间层只需要点云。 */ const viewer = this.viewer; let camera = params.camera ? params.camera : viewer.scene.getActiveCamera(); const {width, height} = params.width ? params : this.viewer.renderer.getSize(new THREE.Vector2()); //viewer.dispatchEvent({type: "render.pass.begin",viewer: viewer}); const visiblePointClouds = viewer.scene.pointclouds.filter(pc => pc.visible ); const visiblePointClouds2 = viewer.scene.pointclouds.filter(pc => pc.isVisible ); const showPointClouds = viewer.scene.pointclouds.some(e=>e.visible) viewer.scene.pointclouds.forEach(e=>{//为了绘制到depthTexture,先显示(展示全景图时隐藏了点云,所以需要显示下。且放大镜需要绘制点云) e.oldVisi = e.visible if(e.isVisible)e.visible = true; }) let lights = []; viewer.scene.scene.traverse(node => { if(node.type === "SpotLight"){ lights.push(node); } }); viewer.renderer.setRenderTarget(params.target || null); {//绘制背景 if(viewer.background === "skybox"){ viewer.skybox.camera.rotation.copy(viewer.scene.cameraP.rotation); viewer.skybox.camera.fov = viewer.scene.cameraP.fov; viewer.skybox.camera.aspect = viewer.scene.cameraP.aspect; viewer.skybox.parent.rotation.x = 0; viewer.skybox.parent.updateMatrixWorld(); viewer.skybox.camera.updateProjectionMatrix(); }else if (viewer.background === 'gradient') { } viewer.renderer.render(viewer.scene.scene, viewer.scene.cameraBG); } //skybox viewer.setCameraLayers(camera, ['skybox']) viewer.renderer.render(viewer.scene.scene, camera); //pointcloud viewer.setCameraLayers(camera, ['pointcloud']) camera.layers.set(Potree.config.renderLayers.pointcloud); //TODO adapt to multiple lights this.renderShadowMap(visiblePointClouds2, camera, lights); //??????? { //scenePointCloud COLOR & DEPTH PASS 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.screenWidth = width; material.screenHeight = height; 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(viewer.useEDL){ /* material.weighted = false; material.useLogarithmicDepthBuffer = false; */ material.useEDL = true; //material.fakeEDL = false; //add }else{ material.useEDL = false; //material.fakeEDL = true; //add 使也输出深度 } } //借用rtEDL存储深度信息 viewer.renderer.setRenderTarget(params.rtEDL || this.rtEDL); //渲染scenePointCloud到rtEDL viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, params.rtEDL || this.rtEDL, { shadowMaps: lights.length > 0 ? [this.shadowMap] : null, clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)), transparent: false, }); } //渲染到rtEDL完毕 viewer.dispatchEvent({type: "render.pass.scene", viewer: viewer, renderTarget: this.rtRegular}); viewer.renderer.setRenderTarget(params.target || null); viewer.scene.pointclouds.forEach(e=>{ e.visible = e.oldVisi }) //设置edlMaterial if(viewer.useEDL && showPointClouds /* || params.magnifier */) { // EDL PASS const uniforms = this.edlMaterial.uniforms; //if(viewer.useEDL){ uniforms.screenWidth.value = width; uniforms.screenHeight.value = height; uniforms.edlStrength.value = viewer.edlStrength; uniforms.radius.value = viewer.edlRadius; uniforms.useEDL.value = 1;//add /* }else{ uniforms.useEDL.value = 0;//add } */ let proj = camera.projectionMatrix; let projArray = new Float32Array(16); projArray.set(proj.elements); uniforms.uProj.value = projArray; uniforms.uEDLColor.value = (params.rtEDL || this.rtEDL).texture; //uniforms.uEDLDepth.value = (params.rtEDL || this.rtEDL).depthTexture; //其实没用到 uniforms.opacity.value = viewer.edlOpacity; // HACK Utils.screenPass.render(viewer.renderer, this.edlMaterial, params.target); }else{ //渲染点云 (直接用rtEDL上的会失去抗锯齿) viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, null , { shadowMaps: lights.length > 0 ? [this.shadowMap] : null, clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)) }); } //viewer.dispatchEvent({type: "render.pass.end",viewer: viewer}); } }