import * as THREE from "../../../libs/three.js/build/three.module.js"; import Common from '../utils/Common.js' import math from '../utils/math.js' import {Features} from "../../Features.js"; import {ExtendPointCloudMaterial} from "../../materials/ExtendPointCloudMaterial.js"; import {Gradients} from "../../materials/Gradients.js"; const prefixVertex ="precision highp float;\nprecision highp int;\n\nuniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\n attribute vec3 position;\n attribute vec3 normal;\n attribute vec2 uv;\n" const prefixFragment ="#extension GL_EXT_frag_depth : enable \n precision highp float;\nprecision highp int;\n\nuniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n" // otherwise error: 'GL_EXT_frag_depth' : extension is disabled let getClassificationLUT = ()=>{ const [width, height] = [256, 1]; let data = new Uint8Array(width * 4); let tex = new THREE.DataTexture(data, width, height, THREE.RGBAFormat); tex.magFilter = THREE.NearestFilter; tex.needsUpdate = true; return tex } let shader = { uniforms: { opacity: { type: "f", // value: 1 }, progress: { type: "f", value: 0 }, pano0Map: { type: "t", value: null }, pano1Map: { type: "t", value: null }, depthMap0: { type: "t", value: null }, depthMap1: { type: "t", value: null }, pano0ClassMap: { type: "t", value: null }, pano1ClassMap: { type: "t", value: null }, pano0TempMap: { type: "t", value: null }, pano1TempMap: { type: "t", value: null }, gradient: { type: "t", value: null }, classificationLUT: { type: "t", value: null }, pano0Position: { type: "v3", value: new THREE.Vector3 }, pano0Matrix: { type: "m4", value: new THREE.Matrix4 }, pano1Position: { type: "v3", value: new THREE.Vector3 }, pano1Matrix: { type: "m4", value: new THREE.Matrix4 }, /* pano1Matrix2: { type: "m4", value: new THREE.Matrix4 }, */ inverseProjectionMatrix: { value: new THREE.Matrix4 }, /* projectionMatrix:{//需要再写一遍吗 value: new THREE.Matrix4 }, */ viewport: { value: new THREE.Vector4 }, //如 {x: 0, y: 0, z: 428, w: 969} xy应该是offset, zw是宽高 cameraHeight0: { type: "f", value: 1 }, cameraHeight1: { type: "f", value: 1 }, ceilHeight0:{ type: "f", value: 2 }, ceilHeight1:{ type: "f", value: 2 }, temperRange:{//温度范围 type:'vec2', value: new THREE.Vector2(math.getKelvinFromCelsius(15), math.getKelvinFromCelsius(60) ) } }, vertexShader: prefixVertex + ` uniform vec3 pano0Position; uniform mat4 pano0Matrix; uniform vec3 pano1Position; uniform mat4 pano1Matrix; varying vec2 vUv; varying vec3 vWorldPosition0; varying vec3 vWorldPosition1; varying vec3 vWorldPosition12; vec3 transformAxis( vec3 direction ) //navvis->4dkk { float y = direction.y; direction.y = direction.z; direction.z = -y; return direction; } void main() { vUv = uv; vec4 worldPosition = modelMatrix * vec4(position, 1.0); vec3 positionLocalToPanoCenter0 = worldPosition.xyz - pano0Position; vWorldPosition0 = (vec4(positionLocalToPanoCenter0, 1.0) * pano0Matrix).xyz; vWorldPosition0.x *= -1.0; vWorldPosition0 = transformAxis(vWorldPosition0); vec3 positionLocalToPanoCenter1 = worldPosition.xyz - pano1Position; vWorldPosition1 = (vec4(positionLocalToPanoCenter1, 1.0) * pano1Matrix).xyz; vWorldPosition1.x *= -1.0; vWorldPosition1 = transformAxis(vWorldPosition1); /* vec3 positionLocalToPanoCenter12 = worldPosition.xyz - pano1Position; vWorldPosition12 = (vec4(positionLocalToPanoCenter12, 1.0) * pano1Matrix2).xyz; vWorldPosition12.x *= -1.0; vWorldPosition12 = transformAxis(vWorldPosition12); */ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); } `, fragmentShader: prefixFragment + ` #define PI 3.141592653 uniform float modelAlpha; uniform float opacity; uniform float progress; uniform int tranType; uniform vec3 pano0Position; uniform vec3 pano1Position; uniform float maxDistance; uniform float minDistance; uniform float minOpa; uniform samplerCube pano0Map; uniform samplerCube pano1Map; #if defined(useTempMap0) || defined(useTempMap1) uniform sampler2D gradient; uniform vec2 temperRange; #endif #if defined(useClassMap0) || defined(useClassMap1) uniform sampler2D classificationLUT; #endif #if defined(useTempMap0) uniform sampler2D pano0TempMap; #endif #if defined(useTempMap1) uniform sampler2D pano1TempMap; #endif #if defined(useClassMap0) uniform sampler2D pano0ClassMap; #endif #if defined(useClassMap1) uniform sampler2D pano1ClassMap; #endif varying vec2 vUv; varying vec3 vWorldPosition0; varying vec3 vWorldPosition1; /* vec2 getSamplerCoord( vec3 direction ) { direction = normalize(direction); float tx=atan(direction.x,-direction.y)/(PI*2.0)+0.5; float ty=acos(direction.z)/PI; return vec2(tx,ty); } */ vec2 getSamplerCoord2( vec3 direction ) { direction = normalize(direction); float tx=atan(direction.x,direction.z)/(PI*2.0)+0.5; float ty=acos(direction.y)/PI; return vec2(tx,ty); } #if defined(GL_EXT_frag_depth) && defined(hasDepthTex) uniform sampler2D depthMap0; uniform sampler2D depthMap1; uniform mat4 inverseProjectionMatrix; uniform mat4 projectionMatrix; uniform vec4 viewport; uniform float cameraHeight0; uniform float cameraHeight1; uniform float ceilHeight0; uniform float ceilHeight1; vec2 getDepth(vec3 dir, sampler2D depthMap, float heightDown, float heightUp, vec4 eyePos){ vec2 depthValue = vec2(0.0, 0.0); vec2 uv2 = getSamplerCoord2( dir.xyz); //暂时只用基于目标漫游点的方向 uv2.x -= 0.25; //全景图和Cube的水平采样起始坐标相差90度,这里矫正 0.25 个采样偏移 vec4 depth = texture2D(depthMap, uv2); //float distance = depth.r + 256. * (depth.g + 256. * depth.b); //distance *= 255. * .001; // distance is now in meters //更改 float distance = (depth.g + depth.r / 256.) * 255.; if(distance == 0.0){//漫游点底部识别不到的区域,给一个地板高度 if(uv2.y < depthTexUVyLimit) distance = heightUp / dir.y; else if(uv2.y > 1.0 - depthTexUVyLimit) distance = heightDown / -dir.y; else distance = 100000.0;//给个超级远的值 } if(distance == 0.0)distance = 100000.0;//给个超级远的值 depthValue.x = distance; distance += .1; // add a safety margin vec4 eyePos2 = vec4(normalize(eyePos.xyz) * distance, 1.); vec4 clipPos2 = projectionMatrix * eyePos2; vec4 ndcPos2 = clipPos2 * 1. / clipPos2.w; depthValue.y = 0.5 * ((gl_DepthRange.far - gl_DepthRange.near) * ndcPos2.z + gl_DepthRange.near + gl_DepthRange.far); #if defined(depth_background) //后排的 skybox 不能挡住chunk depthValue.y += 0.3; #endif return depthValue; } //注:未加载好的话,depth为0,导致第一次漫游过去的时候许多mesh会立刻被遮挡,所以要确保加载完 #endif int unpack16(vec2 channels){ return int(round(channels.y * 255.0)) + (int(round(channels.x * 255.0)) << 8) ; //some phone needs round,such as vivo x30 } int getIntFromColor(sampler2D map, vec3 dir){//uint16-> int 0-65536 //float texWidth = 1024.; vec2 uv = getSamplerCoord2(dir.xyz); uv.x -= 0.25; vec4 color = texture2D(map, uv); return unpack16(color.rg); /* if( fract(uv.x * texWidth) > 0.5){ return unpack16(color.rg); }else{ return unpack16(color.ba); } */ } /*vec4 getColorFromMap(sampler2D map, vec3 dir){ vec2 uv = getSamplerCoord2(dir.xyz); uv.x -= 0.25; vec4 color = texture2D(map, uv); return color; }*/ //ir热成像 #if defined(useTempMap0) || defined(useTempMap1) vec4 getTemperature(sampler2D map, vec3 dir){ float temperature = float(getIntFromColor(map, dir))/ 10.0 ; float w = (temperature - temperRange.x) / (temperRange.y - temperRange.x); w = clamp(w,0.0,1.0); vec4 color = vec4(texture2D(gradient, vec2(w,1.0-w)).rgb, 1.0); return color; } #endif #if defined(useClassMap0) || defined(useClassMap1) /*vec4 getClassification(sampler2D map, vec3 dir, vec4 originColor){ vec4 color = getColorFromMap(map, dir); float v = color.r * 255.0; if(v<=2.01&&v>=1.99){ color = vec4(1.0,0.0,0.0,1.0); } else{ color = vec4(0.0,0.0,0.0,1.0); } return color; } */ vec4 getClassification(sampler2D map, vec3 dir, vec4 originColor){ int classIndex = getIntFromColor(map, dir); vec2 uv = vec2(float(classIndex) / 255.0, 0.5); //copy from pointcloud_new.vs vec4 classColor = texture2D(classificationLUT, uv); float blendRatio = 0.5; float classAlpha = classColor.a * blendRatio; vec4 color = vec4(classColor.rgb * classAlpha + originColor.rgb * (1.0-classAlpha), 1.0); //mix with old rgba return color; } #endif void main() { vec3 vWorldPosition0N = normalize(vWorldPosition0); vec3 vWorldPosition1N = normalize(vWorldPosition1); float progress_ = progress; vec4 colorFromPano0 = vec4(0.0,0.0,0.0,0.0); #if defined(usePanoMap0) //即progress < 1.0 通常是1 #if defined(useTempMap0) colorFromPano0 = getTemperature(pano0TempMap,vWorldPosition0N.xyz); #else colorFromPano0 = textureCube(pano0Map,vWorldPosition0N.xyz); #if defined(useClassMap0) colorFromPano0 = getClassification(pano0ClassMap,vWorldPosition0N.xyz, colorFromPano0); #endif #endif #else progress_ = 1.0; #endif vec4 colorFromPano1 = vec4(0.0,0.0,0.0,0.0); #if defined(useTempMap1) colorFromPano1 = getTemperature(pano1TempMap,vWorldPosition1N.xyz); #else colorFromPano1 = textureCube(pano1Map,vWorldPosition1N.xyz); #if defined(useClassMap1) colorFromPano1 = getClassification(pano1ClassMap,vWorldPosition1N.xyz, colorFromPano1); #endif #endif gl_FragColor = mix(colorFromPano0,colorFromPano1,progress_); //深度图修改深度 #if defined(GL_EXT_frag_depth) && defined(hasDepthTex) vec4 ndcPos; ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1.; ndcPos.z = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far) / (gl_DepthRange.far - gl_DepthRange.near); ndcPos.w = 1.0; vec4 clipPos = ndcPos / gl_FragCoord.w; vec4 eyePos = inverseProjectionMatrix * clipPos; vec2 depth0 = vec2(0.0,0.0); vec2 depth1 = vec2(0.0,0.0); float depth = 0.0; bool useDepth0 = false; bool useDepth1 = true; #if defined(usePanoMap0) useDepth0 = true; #if defined(UnableMixTwoDepth) if(progress_<0.5){ useDepth1 = false; }else{ useDepth0 = false; } #endif if(useDepth0) depth0 = getDepth(vWorldPosition0N, depthMap0, cameraHeight0, ceilHeight0, eyePos); #endif if(useDepth1) depth1 = getDepth(vWorldPosition1N, depthMap1, cameraHeight1, ceilHeight1, eyePos); #if defined(UnableMixTwoDepth) depth = useDepth0 ? depth0.y : depth1.y;//不支持叠加,只能用其中一个,过渡时无法渐变 #else depth = mix(depth0.y,depth1.y,progress_); #endif gl_FragDepthEXT = clamp(depth, 0.0, 1.0); //防止部分手机出现黑块。ios 16 。 因为我给的超远值超出范围 #endif } ` } //注:gl_FragDepthEXT 修改了确实能像真实mesh那样遮挡住在后面的物体。但是为过渡时不能直接像有模型那样,和角度有关。 export default class ModelTextureMaterial extends THREE.RawShaderMaterial { constructor( ){ let defines = {depthTexUVyLimit: Potree.config.depthTexUVyLimit} if(Potree.browser.maybeQilin()){ defines.UnableMixTwoDepth = 1 //该系统在开启硬件加速后,webgl容易出bug。如过渡时黑屏报错,因无法将两个depth叠加。见bug记录 } let {vs,fs} = Common.changeShaderToWebgl2(shader.vertexShader, shader.fragmentShader, 'RawShaderMaterial') if(!Potree.settings.isWebgl2){ defines['round(x)'] = 'floor(x + 0.5)' //webgl1 unsupport round fs = fs.replace('int(round(channels.x * 255.0)) << 8', 'int(round(channels.x * 255.0 * pow(2.0, 8.0)))') //unsupport << } super({ fragmentShader: fs, vertexShader: vs, uniforms: THREE.UniformsUtils.clone(shader.uniforms), side:THREE.DoubleSide, name: "ModelTextureMaterial", defines, }) this.glslVersion = Potree.settings.isWebgl2 && '300 es' this.uniforms.classificationLUT.value = this.classificationTexture = getClassificationLUT() let setSize = (e)=>{ let viewport = e.viewport //let viewportOffset = viewport.offset || new Vector2() let resolution = viewport.resolution2 //this.uniforms.viewport.value.set(viewportOffset.x, viewportOffset.y, resolution.x, resolution.y) this.uniforms.viewport.value.set(0,0, resolution.x, resolution.y);// xy是在viewport中的left和bottom,和整个窗口没有关系,所以不是viewportOffset。几乎都是0,0 } let viewport = viewer.mainViewport; setSize({viewport}) viewer.addEventListener('resize',(e)=>{ if(e.viewport.name != "MainView")return setSize(e) }) //var supportExtDepth = !!Features.EXT_DEPTH.isSupported() { //add viewer.addEventListener('camera_changed', (e)=>{ if(e.viewport.name != "MainView")return //this.uniforms.projectionMatrix.value.copy(e.camera.projectionMatrix) e.camera && this.uniforms.inverseProjectionMatrix.value.copy(e.camera.projectionMatrixInverse) }) let setClass = ()=>{ this.classification = viewer.classifications ExtendPointCloudMaterial.prototype.recomputeClassification.call(this) } viewer.addEventListener('classifications_changed',setClass) setClass() this._gradient = Gradients.ir //海拔贴图种类,火灾也是这个 this.uniforms.gradient.value = ExtendPointCloudMaterial.generateGradientTexture(this._gradient); } let progress = 0 Object.defineProperty(this.uniforms.progress, 'value', { get: function () { return progress }, set: e => { if (e < 1 && !Potree.settings.fastTran ) { if (!('usePanoMap0' in this.defines)) { this.defines.usePanoMap0 = '' this.needsUpdate = true } } else { if ('usePanoMap0' in this.defines) { delete this.defines.usePanoMap0 this.needsUpdate = true } } progress = e }, }) //------------------------------------- } setProjectedPanos(pano0, pano1, progressValue ){ progressValue!=void 0 && (this.uniforms.progress.value = progressValue); //pano0.ensureSkyboxReadyForRender(); if(pano0){ this.uniforms.pano0Map.value = pano0.getSkyboxTexture();//pano0.texture this.uniforms.pano0Position.value.copy(pano0.position) this.uniforms.pano0Matrix.value.copy(pano0.panoMatrix/* pano0.mesh.matrixWorld */ ); //pano1.ensureSkyboxReadyForRender(); } this.uniforms.pano1Map.value = pano1.getSkyboxTexture()//pano1.texture; this.uniforms.pano1Position.value.copy(pano1.position) this.uniforms.pano1Matrix.value.copy(pano1.panoMatrix /* pano1.mesh.matrixWorld */ ); this.pano0 = pano0 this.pano1 = pano1 this.updateDepthTex(pano0) this.updateDepthTex(pano1) this.updateTempEnable() this.updateClassEnable() //console.log('setProjectedPanos', pano0&&pano0.id, pano1&&pano1.id) this.needsUpdate = true; } updateDepthTex(pano, extra){ if( !Potree.settings.useDepthTex || !pano || !pano.depthTex || pano!=this.pano0 && pano!=this.pano1)return //console.log('updateDepthTex', pano.id, this.pano0 && this.pano0.id, this.pano1 && this.pano1.id) if(this.pano0){ this.uniforms.depthMap0.value = this.pano0.entered ? this.pano0.depthTex : null; //dispose了就不要赋值否则dispose会失败 this.uniforms.cameraHeight0.value = this.pano0.floorPosition.distanceTo(this.pano0.position) this.uniforms.ceilHeight0.value = this.pano0.getCeilHeight() - this.pano0.position.z } if(this.pano1){ this.uniforms.depthMap1.value = this.pano1.depthTex //pano1还没entered时也需要,可能在飞入 this.uniforms.cameraHeight1.value = this.pano1.floorPosition.distanceTo(this.pano1.position) this.uniforms.ceilHeight1.value = this.pano1.getCeilHeight() - this.pano1.position.z } if(this.dontChangeDepth)return let hasDepthTex = this.pano0 && this.pano1 && this.pano0.pointcloud.hasDepthTex && this.pano1.pointcloud.hasDepthTex //暂时不知道一个有图一个没图怎么写所以 Potree.Utils.addOrRemoveDefine(this, 'hasDepthTex', hasDepthTex?'add':'remove' ) } updateTempEnable(){ let tex0 = Potree.settings.showHotTemp && this.pano0.tempTex || Potree.settings.showHotIr && this.pano0.irTex let tex1 = Potree.settings.showHotTemp && this.pano1.tempTex || Potree.settings.showHotIr && this.pano1.irTex this.uniforms.pano0TempMap.value = tex0 this.uniforms.pano1TempMap.value = tex1 Potree.Utils.addOrRemoveDefine(this, 'useTempMap0', tex0?'add':'remove' ) Potree.Utils.addOrRemoveDefine(this, 'useTempMap1', tex1?'add':'remove' ) viewer.dispatchEvent('content_changed') } updateClassEnable(){ let hasClassTex0 = Potree.settings.showClass && this.pano0.segTex let hasClassTex1 = Potree.settings.showClass && this.pano1.segTex this.uniforms.pano0ClassMap.value = this.pano0.segTex this.uniforms.pano1ClassMap.value = this.pano1.segTex Potree.Utils.addOrRemoveDefine(this, 'useClassMap0', hasClassTex0?'add':'remove' ) Potree.Utils.addOrRemoveDefine(this, 'useClassMap1', hasClassTex1?'add':'remove' ) viewer.dispatchEvent('content_changed') } } ModelTextureMaterial.prototype.setTempRange = ExtendPointCloudMaterial.prototype.setTempRange