import * as THREE from "../libs/three.js/build/three.module.js"; import {PointCloudTree} from "./PointCloudTree.js"; import {PointCloudOctreeNode} from "./PointCloudOctree.js"; import {PointCloudArena4DNode} from "./arena4d/PointCloudArena4D.js"; import {PointSizeType, ClipTask, ElevationGradientRepeat} from "./defines.js"; // Copied from three.js: WebGLRenderer.js function paramThreeToGL(_gl, p) { let extension; if (p === THREE.RepeatWrapping) return _gl.REPEAT; if (p === THREE.ClampToEdgeWrapping) return _gl.CLAMP_TO_EDGE; if (p === THREE.MirroredRepeatWrapping) return _gl.MIRRORED_REPEAT; if (p === THREE.NearestFilter) return _gl.NEAREST; if (p === THREE.NearestMipMapNearestFilter) return _gl.NEAREST_MIPMAP_NEAREST; if (p === THREE.NearestMipMapLinearFilter) return _gl.NEAREST_MIPMAP_LINEAR; if (p === THREE.LinearFilter) return _gl.LINEAR; if (p === THREE.LinearMipMapNearestFilter) return _gl.LINEAR_MIPMAP_NEAREST; if (p === THREE.LinearMipMapLinearFilter) return _gl.LINEAR_MIPMAP_LINEAR; if (p === THREE.UnsignedByteType) return _gl.UNSIGNED_BYTE; if (p === THREE.UnsignedShort4444Type) return _gl.UNSIGNED_SHORT_4_4_4_4; if (p === THREE.UnsignedShort5551Type) return _gl.UNSIGNED_SHORT_5_5_5_1; if (p === THREE.UnsignedShort565Type) return _gl.UNSIGNED_SHORT_5_6_5; if (p === THREE.ByteType) return _gl.BYTE; if (p === THREE.ShortType) return _gl.SHORT; if (p === THREE.UnsignedShortType) return _gl.UNSIGNED_SHORT; if (p === THREE.IntType) return _gl.INT; if (p === THREE.UnsignedIntType) return _gl.UNSIGNED_INT; if (p === THREE.FloatType) return _gl.FLOAT; if (p === THREE.HalfFloatType) { extension = extensions.get('OES_texture_half_float'); if (extension !== null) return extension.HALF_FLOAT_OES; } if (p === THREE.AlphaFormat) return _gl.ALPHA; if (p === THREE.RGBFormat) return _gl.RGB; if (p === THREE.RGBAFormat) return _gl.RGBA; if (p === THREE.LuminanceFormat) return _gl.LUMINANCE; if (p === THREE.LuminanceAlphaFormat) return _gl.LUMINANCE_ALPHA; if (p === THREE.DepthFormat) return _gl.DEPTH_COMPONENT; if (p === THREE.DepthStencilFormat) return _gl.DEPTH_STENCIL; if (p === THREE.AddEquation) return _gl.FUNC_ADD; if (p === THREE.SubtractEquation) return _gl.FUNC_SUBTRACT; if (p === THREE.ReverseSubtractEquation) return _gl.FUNC_REVERSE_SUBTRACT; if (p === THREE.ZeroFactor) return _gl.ZERO; if (p === THREE.OneFactor) return _gl.ONE; if (p === THREE.SrcColorFactor) return _gl.SRC_COLOR; if (p === THREE.OneMinusSrcColorFactor) return _gl.ONE_MINUS_SRC_COLOR; if (p === THREE.SrcAlphaFactor) return _gl.SRC_ALPHA; if (p === THREE.OneMinusSrcAlphaFactor) return _gl.ONE_MINUS_SRC_ALPHA; if (p === THREE.DstAlphaFactor) return _gl.DST_ALPHA; if (p === THREE.OneMinusDstAlphaFactor) return _gl.ONE_MINUS_DST_ALPHA; if (p === THREE.DstColorFactor) return _gl.DST_COLOR; if (p === THREE.OneMinusDstColorFactor) return _gl.ONE_MINUS_DST_COLOR; if (p === THREE.SrcAlphaSaturateFactor) return _gl.SRC_ALPHA_SATURATE; if (p === THREE.RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === THREE.RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format) { extension = extensions.get('WEBGL_compressed_texture_s3tc'); if (extension !== null) { if (p === THREE.RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if (p === THREE.RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if (p === THREE.RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if (p === THREE.RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } } if (p === THREE.RGB_PVRTC_4BPPV1_Format || p === THREE.RGB_PVRTC_2BPPV1_Format || p === THREE.RGBA_PVRTC_4BPPV1_Format || p === THREE.RGBA_PVRTC_2BPPV1_Format) { extension = extensions.get('WEBGL_compressed_texture_pvrtc'); if (extension !== null) { if (p === THREE.RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if (p === THREE.RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if (p === THREE.RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if (p === THREE.RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } } if (p === THREE.RGB_ETC1_Format) { extension = extensions.get('WEBGL_compressed_texture_etc1'); if (extension !== null) return extension.COMPRESSED_RGB_ETC1_WEBGL; } if (p === THREE.MinEquation || p === THREE.MaxEquation) { extension = extensions.get('EXT_blend_minmax'); if (extension !== null) { if (p === THREE.MinEquation) return extension.MIN_EXT; if (p === THREE.MaxEquation) return extension.MAX_EXT; } } if (p === UnsignedInt248Type) { extension = extensions.get('WEBGL_depth_texture'); if (extension !== null) return extension.UNSIGNED_INT_24_8_WEBGL; } return 0; }; let attributeLocations = { "position": {name: "position", location: 0}, "color": {name: "color", location: 1}, "rgba": {name: "color", location: 1}, "intensity": {name: "intensity", location: 2}, "classification": {name: "classification", location: 3}, "returnNumber": {name: "returnNumber", location: 4}, "return number": {name: "returnNumber", location: 4}, "returns": {name: "returnNumber", location: 4}, "numberOfReturns": {name: "numberOfReturns", location: 5}, "number of returns": {name: "numberOfReturns", location: 5}, "pointSourceID": {name: "pointSourceID", location: 6}, "source id": {name: "pointSourceID", location: 6}, "point source id": {name: "pointSourceID", location: 6}, "indices": {name: "indices", location: 7}, "normal": {name: "normal", location: 8}, "spacing": {name: "spacing", location: 9}, "gps-time": {name: "gpsTime", location: 10}, "aExtra": {name: "aExtra", location: 11}, }; class Shader { constructor(gl, name, vsSource, fsSource) { this.gl = gl; this.name = name; this.vsSource = vsSource; this.fsSource = fsSource; this.cache = new Map(); this.vs = null; this.fs = null; this.program = null; this.uniformLocations = {}; this.attributeLocations = {}; this.uniformBlockIndices = {}; this.uniformBlocks = {}; this.uniforms = {}; this.update(vsSource, fsSource); } update(vsSource, fsSource) { this.vsSource = vsSource; this.fsSource = fsSource; this.linkProgram(); } compileShader(shader, source){ let gl = this.gl; gl.shaderSource(shader, source); gl.compileShader(shader); let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (!success) { let info = gl.getShaderInfoLog(shader); let numberedSource = source.split("\n").map((a, i) => `${i + 1}`.padEnd(5) + a).join("\n"); throw `could not compile shader ${this.name}: ${info}, \n${numberedSource}`; } } linkProgram() { const tStart = performance.now(); let gl = this.gl; this.uniformLocations = {}; this.attributeLocations = {}; this.uniforms = {}; gl.useProgram(null); let cached = this.cache.get(`${this.vsSource}, ${this.fsSource}`); if (cached) { this.program = cached.program; this.vs = cached.vs; this.fs = cached.fs; this.attributeLocations = cached.attributeLocations; this.uniformLocations = cached.uniformLocations; this.uniformBlocks = cached.uniformBlocks; this.uniforms = cached.uniforms; return; } else { this.vs = gl.createShader(gl.VERTEX_SHADER); this.fs = gl.createShader(gl.FRAGMENT_SHADER); this.program = gl.createProgram(); if( !gl.isProgram(this.program )){//创建失败 开启多个页面可能会,原因是webglcontextlost //console.error('创建program失败'); viewer.emit('webglError', 'potreeRenderer创建program失败') console.log(this.vs) console.log(this.fs) return; } for(let name of Object.keys(attributeLocations)){ let location = attributeLocations[name].location; let glslName = attributeLocations[name].name; gl.bindAttribLocation(this.program, location, glslName); } this.compileShader(this.vs, this.vsSource); this.compileShader(this.fs, this.fsSource); let program = this.program; gl.attachShader(program, this.vs); gl.attachShader(program, this.fs); gl.linkProgram(program); gl.detachShader(program, this.vs); gl.detachShader(program, this.fs); // 检测当前程序链接状态 let success = gl.getProgramParameter(program, gl.LINK_STATUS); if (!success) { let info = gl.getProgramInfoLog(program); throw `could not link program ${this.name}: ${info}`; } { // attribute locations let numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (let i = 0; i < numAttributes; i++) { let attribute = gl.getActiveAttrib(program, i); let location = gl.getAttribLocation(program, attribute.name); this.attributeLocations[attribute.name] = location; } } { // uniform locations let numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < numUniforms; i++) { let uniform = gl.getActiveUniform(program, i); let location = gl.getUniformLocation(program, uniform.name); this.uniformLocations[uniform.name] = location; this.uniforms[uniform.name] = { location: location, value: null, }; } } // uniform blocks if( typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext){ //WebGL2RenderingContext在mac的safari14以下是没有定义的 let numBlocks = gl.getProgramParameter(program, gl.ACTIVE_UNIFORM_BLOCKS); for (let i = 0; i < numBlocks; i++) { let blockName = gl.getActiveUniformBlockName(program, i); let blockIndex = gl.getUniformBlockIndex(program, blockName); this.uniformBlockIndices[blockName] = blockIndex; gl.uniformBlockBinding(program, blockIndex, blockIndex); let dataSize = gl.getActiveUniformBlockParameter(program, blockIndex, gl.UNIFORM_BLOCK_DATA_SIZE); let uBuffer = gl.createBuffer(); gl.bindBuffer(gl.UNIFORM_BUFFER, uBuffer); gl.bufferData(gl.UNIFORM_BUFFER, dataSize, gl.DYNAMIC_READ); gl.bindBufferBase(gl.UNIFORM_BUFFER, blockIndex, uBuffer); gl.bindBuffer(gl.UNIFORM_BUFFER, null); this.uniformBlocks[blockName] = { name: blockName, index: blockIndex, dataSize: dataSize, buffer: uBuffer }; } } let cached = { program: this.program, vs: this.vs, fs: this.fs, attributeLocations: this.attributeLocations, uniformLocations: this.uniformLocations, uniforms: this.uniforms, uniformBlocks: this.uniformBlocks, }; this.cache.set(`${this.vsSource}, ${this.fsSource}`, cached); } const tEnd = performance.now(); const duration = tEnd - tStart; //console.log(`shader compile duration: ${duration.toFixed(3)}`); } setUniformMatrix4(name, value) { const gl = this.gl; const location = this.uniformLocations[name]; if (location == null) { return; } let tmp = new Float32Array(value.elements); gl.uniformMatrix4fv(location, false, tmp); } setUniform1f(name, value) { const gl = this.gl; const uniform = this.uniforms[name]; if (uniform === undefined) { return; } if(uniform.value === value){ return; } uniform.value = value; gl.uniform1f(uniform.location, value); } setUniformBoolean(name, value) { const gl = this.gl; const uniform = this.uniforms[name]; if (uniform === undefined) { return; } if(uniform.value === value){ return; } uniform.value = value; gl.uniform1i(uniform.location, value); } setUniformTexture(name, value) { const gl = this.gl; const location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform1i(location, value); } setUniform2f(name, value) { const gl = this.gl; const location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform2f(location, value[0], value[1]); } setUniform3f(name, value) { const gl = this.gl; const location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform3f(location, value[0], value[1], value[2]); } setUniform(name, value) { if (value.constructor === THREE.Matrix4) { this.setUniformMatrix4(name, value); } else if (typeof value === "number") { this.setUniform1f(name, value); } else if (typeof value === "boolean") { this.setUniformBoolean(name, value); } else if (value instanceof WebGLTexture) { this.setUniformTexture(name, value); } else if (value instanceof Array) { if (value.length === 2) { this.setUniform2f(name, value); } else if (value.length === 3) { this.setUniform3f(name, value); } } else { console.error("unhandled uniform type: ", name, value); } } setUniform1i(name, value) { let gl = this.gl; let location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform1i(location, value); } }; class WebGLTexture { constructor(gl, texture) { this.gl = gl; this.texture = texture; this.id = gl.createTexture(); this.target = gl.TEXTURE_2D; this.version = -1; this.update(texture); } update() { if (!this.texture.image) { this.version = this.texture.version; return; } let gl = this.gl; let texture = this.texture; if (this.version === texture.version) { return; } this.target = gl.TEXTURE_2D; gl.bindTexture(this.target, this.id); let level = 0; let internalFormat = paramThreeToGL(gl, texture.format); let width = texture.image.width; let height = texture.image.height; let border = 0; let srcFormat = internalFormat; let srcType = paramThreeToGL(gl, texture.type); let data; gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); gl.pixelStorei(gl.UNPACK_ALIGNMENT, texture.unpackAlignment); if (texture instanceof THREE.DataTexture) { data = texture.image.data; gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, paramThreeToGL(gl, texture.magFilter)); gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, paramThreeToGL(gl, texture.minFilter)); gl.texImage2D(this.target, level, internalFormat, width, height, border, srcFormat, srcType, data); }/* else if(texture instanceof THREE.CubeTexture){//add } */else if ((texture instanceof THREE.CanvasTexture) || (texture instanceof THREE.Texture)) { data = texture.image; gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, paramThreeToGL(gl, texture.wrapS)); gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, paramThreeToGL(gl, texture.wrapT)); gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, paramThreeToGL(gl, texture.magFilter)); gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, paramThreeToGL(gl, texture.minFilter)); gl.texImage2D(this.target, level, internalFormat, internalFormat, srcType, data); if (texture instanceof THREE.Texture) {gl.generateMipmap(gl.TEXTURE_2D);} } gl.bindTexture(this.target, null); this.version = texture.version; } }; class WebGLBuffer { constructor() { this.numElements = 0; this.vao = null; this.vbos = new Map(); } }; export class Renderer { constructor(threeRenderer) { this.threeRenderer = threeRenderer; this.gl = this.threeRenderer.getContext(); this.buffers = new Map(); this.shaders = new Map(); this.textures = new Map(); this.glTypeMapping = new Map(); this.glTypeMapping.set(Float32Array, this.gl.FLOAT); this.glTypeMapping.set(Uint8Array, this.gl.UNSIGNED_BYTE); this.glTypeMapping.set(Uint16Array, this.gl.UNSIGNED_SHORT); this.toggle = 0; } deleteBuffer(geometry) { let gl = this.gl; let webglBuffer = this.buffers.get(geometry); if (webglBuffer != null) { for (let attributeName in geometry.attributes) { gl.deleteBuffer(webglBuffer.vbos.get(attributeName).handle); } this.buffers.delete(geometry); } } createBuffer(geometry){ let gl = this.gl; let webglBuffer = new WebGLBuffer(); webglBuffer.vao = gl.createVertexArray(); webglBuffer.numElements = geometry.attributes.position.count; gl.bindVertexArray(webglBuffer.vao); for(let attributeName in geometry.attributes){ let bufferAttribute = geometry.attributes[attributeName]; let vbo = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vbo); gl.bufferData(gl.ARRAY_BUFFER, bufferAttribute.array, gl.STATIC_DRAW); let normalized = bufferAttribute.normalized; let type = this.glTypeMapping.get(bufferAttribute.array.constructor); if(attributeLocations[attributeName] === undefined){ //attributeLocation = attributeLocations["aExtra"]; }else{ let attributeLocation = attributeLocations[attributeName].location; gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0); gl.enableVertexAttribArray(attributeLocation); } webglBuffer.vbos.set(attributeName, { handle: vbo, name: attributeName, count: bufferAttribute.count, itemSize: bufferAttribute.itemSize, type: geometry.attributes.position.array.constructor, version: 0 }); } gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindVertexArray(null); let disposeHandler = (event) => { this.deleteBuffer(geometry); geometry.removeEventListener("dispose", disposeHandler); }; geometry.addEventListener("dispose", disposeHandler); return webglBuffer; } updateBuffer(geometry){ let gl = this.gl; let webglBuffer = this.buffers.get(geometry); gl.bindVertexArray(webglBuffer.vao); for(let attributeName in geometry.attributes){ let bufferAttribute = geometry.attributes[attributeName]; let normalized = bufferAttribute.normalized; let type = this.glTypeMapping.get(bufferAttribute.array.constructor); let vbo = null; if(!webglBuffer.vbos.has(attributeName)){ vbo = gl.createBuffer(); webglBuffer.vbos.set(attributeName, { handle: vbo, name: attributeName, count: bufferAttribute.count, itemSize: bufferAttribute.itemSize, type: geometry.attributes.position.array.constructor, version: bufferAttribute.version }); }else{ vbo = webglBuffer.vbos.get(attributeName).handle; webglBuffer.vbos.get(attributeName).version = bufferAttribute.version; } gl.bindBuffer(gl.ARRAY_BUFFER, vbo); gl.bufferData(gl.ARRAY_BUFFER, bufferAttribute.array, gl.STATIC_DRAW); if(attributeLocations[attributeName] === undefined){ //attributeLocation = attributeLocations["aExtra"]; }else{ let attributeLocation = attributeLocations[attributeName].location; gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0); gl.enableVertexAttribArray(attributeLocation); } } gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindVertexArray(null); } traverse(scene) { let octrees = []; let stack = [scene]; while (stack.length > 0) { let node = stack.pop(); if (node instanceof PointCloudTree) { octrees.push(node); continue; } let visibleChildren = node.children.filter(c => c.visible); stack.push(...visibleChildren); } let result = { octrees: octrees }; return result; } renderNodes(octree, nodes, visibilityTextureData, camera, target, shader, params) { if (exports.measureTimings) performance.mark("renderNodes-start"); let gl = this.gl; let material = params.material ? params.material : octree.material; let shadowMaps = params.shadowMaps == null ? [] : params.shadowMaps; let view = camera.matrixWorldInverse; if(params.viewOverride){ view = params.viewOverride; } let worldView = new THREE.Matrix4(); let mat4holder = new Float32Array(16); let i = 0; for (let node of nodes) { if(exports.debug.allowedNodes !== undefined){ if(!exports.debug.allowedNodes.includes(node.name)){ continue; } } let world = node.sceneNode.matrixWorld; worldView.multiplyMatrices(view, world); if (visibilityTextureData) { let vnStart = visibilityTextureData.offsets.get(node); shader.setUniform1f("uVNStart", vnStart); } let level = node.getLevel(); if(node.debug){ shader.setUniform("uDebug", true); }else{ shader.setUniform("uDebug", false); } // let isLeaf = false; // if(node instanceof PointCloudOctreeNode){ // isLeaf = Object.keys(node.children).length === 0; // }else if(node instanceof PointCloudArena4DNode){ // isLeaf = node.geometryNode.isLeaf; // } // shader.setUniform("uIsLeafNode", isLeaf); // let isLeaf = node.children.filter(n => n != null).length === 0; // if(!isLeaf){ // continue; // } // TODO consider passing matrices in an array to avoid uniformMatrix4fv overhead const lModel = shader.uniformLocations["modelMatrix"]; if (lModel) { mat4holder.set(world.elements); gl.uniformMatrix4fv(lModel, false, mat4holder); } const lModelView = shader.uniformLocations["modelViewMatrix"]; //mat4holder.set(worldView.elements); // faster then set in chrome 63 for(let j = 0; j < 16; j++){ mat4holder[j] = worldView.elements[j]; } gl.uniformMatrix4fv(lModelView, false, mat4holder); { // Clip Polygons if(material.clipPolygons && material.clipPolygons.length > 0){ let clipPolygonVCount = []; let worldViewProjMatrices = []; for(let clipPolygon of material.clipPolygons){ let view = clipPolygon.viewMatrix; let proj = clipPolygon.projMatrix; let worldViewProj = proj.clone().multiply(view).multiply(world); clipPolygonVCount.push(clipPolygon.markers.length); worldViewProjMatrices.push(worldViewProj); } let flattenedMatrices = [].concat(...worldViewProjMatrices.map(m => m.elements)); let flattenedVertices = new Array(8 * 3 * material.clipPolygons.length); for(let i = 0; i < material.clipPolygons.length; i++){ let clipPolygon = material.clipPolygons[i]; for(let j = 0; j < clipPolygon.markers.length; j++){ flattenedVertices[i * 24 + (j * 3 + 0)] = clipPolygon.markers[j].position.x; flattenedVertices[i * 24 + (j * 3 + 1)] = clipPolygon.markers[j].position.y; flattenedVertices[i * 24 + (j * 3 + 2)] = clipPolygon.markers[j].position.z; } } const lClipPolygonVCount = shader.uniformLocations["uClipPolygonVCount[0]"]; gl.uniform1iv(lClipPolygonVCount, clipPolygonVCount); const lClipPolygonVP = shader.uniformLocations["uClipPolygonWVP[0]"]; gl.uniformMatrix4fv(lClipPolygonVP, false, flattenedMatrices); const lClipPolygons = shader.uniformLocations["uClipPolygonVertices[0]"]; gl.uniform3fv(lClipPolygons, flattenedVertices); } } //shader.setUniformMatrix4("modelMatrix", world); //shader.setUniformMatrix4("modelViewMatrix", worldView); shader.setUniform1f("uLevel", level); shader.setUniform1f("uNodeSpacing", node.geometryNode.estimatedSpacing); shader.setUniform1f("uPCIndex", i); // uBBSize if (shadowMaps.length > 0) { const lShadowMap = shader.uniformLocations["uShadowMap[0]"]; shader.setUniform3f("uShadowColor", material.uniforms.uShadowColor.value); let bindingStart = 5; let bindingPoints = new Array(shadowMaps.length).fill(bindingStart).map((a, i) => (a + i)); gl.uniform1iv(lShadowMap, bindingPoints); for (let i = 0; i < shadowMaps.length; i++) { let shadowMap = shadowMaps[i]; let bindingPoint = bindingPoints[i]; let glTexture = this.threeRenderer.properties.get(shadowMap.target.texture).__webglTexture; gl.activeTexture(gl[`TEXTURE${bindingPoint}`]); gl.bindTexture(gl.TEXTURE_2D, glTexture); } { let worldViewMatrices = shadowMaps .map(sm => sm.camera.matrixWorldInverse) .map(view => new THREE.Matrix4().multiplyMatrices(view, world)) let flattenedMatrices = [].concat(...worldViewMatrices.map(c => c.elements)); const lWorldView = shader.uniformLocations["uShadowWorldView[0]"]; gl.uniformMatrix4fv(lWorldView, false, flattenedMatrices); } { let flattenedMatrices = [].concat(...shadowMaps.map(sm => sm.camera.projectionMatrix.elements)); const lProj = shader.uniformLocations["uShadowProj[0]"]; gl.uniformMatrix4fv(lProj, false, flattenedMatrices); } } const geometry = node.geometryNode.geometry; if(geometry.attributes["gps-time"]){ const bufferAttribute = geometry.attributes["gps-time"]; const attGPS = octree.getAttribute("gps-time"); let initialRange = attGPS.initialRange; let initialRangeSize = initialRange[1] - initialRange[0]; let globalRange = attGPS.range; let globalRangeSize = globalRange[1] - globalRange[0]; let scale = initialRangeSize / globalRangeSize; let offset = -(globalRange[0] - initialRange[0]) / initialRangeSize; scale = Number.isNaN(scale) ? 1 : scale; offset = Number.isNaN(offset) ? 0 : offset; shader.setUniform1f("uGpsScale", scale); shader.setUniform1f("uGpsOffset", offset); //shader.setUniform2f("uFilterGPSTimeClipRange", [-Infinity, Infinity]); let uFilterGPSTimeClipRange = material.uniforms.uFilterGPSTimeClipRange.value; // let gpsCliPRangeMin = uFilterGPSTimeClipRange[0] // let gpsCliPRangeMax = uFilterGPSTimeClipRange[1] // shader.setUniform2f("uFilterGPSTimeClipRange", [gpsCliPRangeMin, gpsCliPRangeMax]); let normalizedClipRange = [ (uFilterGPSTimeClipRange[0] - globalRange[0]) / globalRangeSize, (uFilterGPSTimeClipRange[1] - globalRange[0]) / globalRangeSize, ]; shader.setUniform2f("uFilterGPSTimeClipRange", normalizedClipRange); // // ranges in full gps coordinate system // const globalRange = attGPS.range; // const bufferRange = bufferAttribute.potree.range; // // ranges in [0, 1] // // normalizedGlobalRange = [0, 1] // // normalizedBufferRange: norm buffer within norm global range e.g. [0.2, 0.8] // const globalWidth = globalRange[1] - globalRange[0]; // const normalizedBufferRange = [ // (bufferRange[0] - globalRange[0]) / globalWidth, // (bufferRange[1] - globalRange[0]) / globalWidth, // ]; // shader.setUniform2f("uNormalizedGpsBufferRange", normalizedBufferRange); // let uFilterGPSTimeClipRange = material.uniforms.uFilterGPSTimeClipRange.value; // let gpsCliPRangeMin = uFilterGPSTimeClipRange[0] // let gpsCliPRangeMax = uFilterGPSTimeClipRange[1] // shader.setUniform2f("uFilterGPSTimeClipRange", [gpsCliPRangeMin, gpsCliPRangeMax]); // shader.setUniform1f("uGpsScale", bufferAttribute.potree.scale); // shader.setUniform1f("uGpsOffset", bufferAttribute.potree.offset); } { let uFilterReturnNumberRange = material.uniforms.uFilterReturnNumberRange.value; let uFilterNumberOfReturnsRange = material.uniforms.uFilterNumberOfReturnsRange.value; let uFilterPointSourceIDClipRange = material.uniforms.uFilterPointSourceIDClipRange.value; shader.setUniform2f("uFilterReturnNumberRange", uFilterReturnNumberRange); shader.setUniform2f("uFilterNumberOfReturnsRange", uFilterNumberOfReturnsRange); shader.setUniform2f("uFilterPointSourceIDClipRange", uFilterPointSourceIDClipRange); } let webglBuffer = null; if(!this.buffers.has(geometry)){ webglBuffer = this.createBuffer(geometry); this.buffers.set(geometry, webglBuffer); }else{ webglBuffer = this.buffers.get(geometry); for(let attributeName in geometry.attributes){ let attribute = geometry.attributes[attributeName]; if(attribute.version > webglBuffer.vbos.get(attributeName).version){ this.updateBuffer(geometry); } } } gl.bindVertexArray(webglBuffer.vao); let isExtraAttribute = attributeLocations[material.activeAttributeName] === undefined && Object.keys(geometry.attributes).includes(material.activeAttributeName); if(isExtraAttribute){ const attributeLocation = attributeLocations["aExtra"].location; for(const attributeName in geometry.attributes){ const bufferAttribute = geometry.attributes[attributeName]; const vbo = webglBuffer.vbos.get(attributeName); gl.bindBuffer(gl.ARRAY_BUFFER, vbo.handle); gl.disableVertexAttribArray(attributeLocation); } const attName = material.activeAttributeName; const bufferAttribute = geometry.attributes[attName]; const vbo = webglBuffer.vbos.get(attName); if(bufferAttribute !== undefined && vbo !== undefined){ let type = this.glTypeMapping.get(bufferAttribute.array.constructor); let normalized = bufferAttribute.normalized; gl.bindBuffer(gl.ARRAY_BUFFER, vbo.handle); gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0); gl.enableVertexAttribArray(attributeLocation); } { const attExtra = octree.pcoGeometry.pointAttributes.attributes .find(a => a.name === attName); let range = material.getRange(attName); if(!range){ range = attExtra.range; } if(!range){ range = [0, 1]; } let initialRange = attExtra.initialRange; let initialRangeSize = initialRange[1] - initialRange[0]; let globalRange = range; let globalRangeSize = globalRange[1] - globalRange[0]; let scale = initialRangeSize / globalRangeSize; let offset = -(globalRange[0] - initialRange[0]) / initialRangeSize; scale = Number.isNaN(scale) ? 1 : scale; offset = Number.isNaN(offset) ? 0 : offset; shader.setUniform1f("uExtraScale", scale); shader.setUniform1f("uExtraOffset", offset); } }else{ for(const attributeName in geometry.attributes){ const bufferAttribute = geometry.attributes[attributeName]; const vbo = webglBuffer.vbos.get(attributeName); if(attributeLocations[attributeName] !== undefined){ const attributeLocation = attributeLocations[attributeName].location; let type = this.glTypeMapping.get(bufferAttribute.array.constructor); let normalized = bufferAttribute.normalized; gl.bindBuffer(gl.ARRAY_BUFFER, vbo.handle); gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0); gl.enableVertexAttribArray(attributeLocation); } } } let numPoints = webglBuffer.numElements; gl.drawArrays(gl.POINTS, 0, numPoints); //gl.drawArrays(gl.TRIANGLES, 0, numPoints); i++; } gl.bindVertexArray(null); if (exports.measureTimings) { performance.mark("renderNodes-end"); performance.measure("render.renderNodes", "renderNodes-start", "renderNodes-end"); } } renderOctree(octree, nodes, camera, target, params = {}){ let gl = this.gl; let material = params.material ? params.material : octree.material; let shadowMaps = params.shadowMaps == null ? [] : params.shadowMaps; let view = camera.matrixWorldInverse; let viewInv = camera.matrixWorld; if(params.viewOverride){ view = params.viewOverride; viewInv = view.clone().invert(); } let proj = camera.projectionMatrix; let projInv = proj.clone().invert(); //let worldView = new THREE.Matrix4(); let shader = null; let visibilityTextureData = null; let currentTextureBindingPoint = 0; if (material.pointSizeType >= 0) { if (material.pointSizeType === PointSizeType.ADAPTIVE || material.activeAttributeName === "level of detail") { let vnNodes = (params.vnTextureNodes != null) ? params.vnTextureNodes : nodes; visibilityTextureData = octree.computeVisibilityTextureData(vnNodes, camera); const vnt = material.visibleNodesTexture; const data = vnt.image.data; data.set(visibilityTextureData.data); vnt.needsUpdate = true; } } { // UPDATE SHADER AND TEXTURES if (!this.shaders.has(material)) { let [vs, fs] = [material.vertexShader, material.fragmentShader]; let shader = new Shader(gl, "pointcloud", vs, fs); this.shaders.set(material, shader); } shader = this.shaders.get(material); //if(material.needsUpdate){ { let [vs, fs] = [material.vertexShader, material.fragmentShader]; let numSnapshots = material.snapEnabled ? material.numSnapshots : 0; let numClipBoxes = (material.clipBoxes && material.clipBoxes.length) ? material.clipBoxes.length : 0; let numClipSpheres = (params.clipSpheres && params.clipSpheres.length) ? params.clipSpheres.length : 0; let numClipPolygons = (material.clipPolygons && material.clipPolygons.length) ? material.clipPolygons.length : 0; let defines = [ `#define num_shadowmaps ${shadowMaps.length}`, `#define num_snapshots ${numSnapshots}`, `#define num_clipboxes ${numClipBoxes}`, `#define num_clipspheres ${numClipSpheres}`, `#define num_clippolygons ${numClipPolygons}`, ]; //add:----------- if(material.usePanoMap){ defines.push("#define usePanoMap"); } if(material.useFilterByNormal){ defines.push("#define use_filter_by_normal"); defines.push("#define attenuated_opacity"); } //--------------- if(octree.pcoGeometry.root.isLoaded()){ let attributes = octree.pcoGeometry.root.geometry.attributes; if(attributes["gps-time"]){ defines.push("#define clip_gps_enabled"); } if(attributes["return number"]){ defines.push("#define clip_return_number_enabled"); } if(attributes["number of returns"]){ defines.push("#define clip_number_of_returns_enabled"); } if(attributes["source id"] || attributes["point source id"]){ defines.push("#define clip_point_source_id_enabled"); } } let definesString = defines.join("\n"); let vsVersionIndex = vs.indexOf("#version "); let fsVersionIndex = fs.indexOf("#version "); if(vsVersionIndex >= 0){ vs = vs.replace(/(#version .*)/, `$1\n${definesString}`) }else{ vs = `${definesString}\n${vs}`; } if(fsVersionIndex >= 0){ fs = fs.replace(/(#version .*)/, `$1\n${definesString}`) }else{ fs = `${definesString}\n${fs}`; } shader.update(vs, fs); material.needsUpdate = false; } for (let uniformName of Object.keys(material.uniforms)) { let uniform = material.uniforms[uniformName]; if (uniform.type == "t") { let texture = uniform.value; if (!texture) { continue; } //add if(uniformName == 'pano0Map' || uniformName == 'pano1Map' ){ //属于cubeTex,另外设置 continue } if (!this.textures.has(texture)) { let webglTexture = new WebGLTexture(gl, texture); this.textures.set(texture, webglTexture); } let webGLTexture = this.textures.get(texture); webGLTexture.update(); } } } gl.useProgram(shader.program); let transparent = false; if(params.transparent !== undefined){ transparent = params.transparent && material.opacity < 1; }else{ transparent = material.usePanoMap ? false : (material.useFilterByNormal || material.opacity < 1); //add useFilterByNormal } if (transparent){ gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE); gl.depthMask(false); gl.disable(gl.DEPTH_TEST); } else { gl.disable(gl.BLEND); gl.depthMask(true); gl.enable(gl.DEPTH_TEST); } if(params.blendFunc !== undefined){ gl.enable(gl.BLEND); gl.blendFunc(...params.blendFunc); } if(params.depthTest !== undefined){ if(params.depthTest === true){ gl.enable(gl.DEPTH_TEST); }else{ gl.disable(gl.DEPTH_TEST); } } if(params.depthWrite !== undefined){ if(params.depthWrite === true){ gl.depthMask(true); }else{ gl.depthMask(false); } } { // UPDATE UNIFORMS shader.setUniformMatrix4("projectionMatrix", proj); shader.setUniformMatrix4("viewMatrix", view); shader.setUniformMatrix4("uViewInv", viewInv); shader.setUniformMatrix4("uProjInv", projInv); /* let screenWidth = target ? target.width : material.screenWidth; let screenHeight = target ? target.height : material.screenHeight; shader.setUniform1f("uScreenWidth", screenWidth); shader.setUniform1f("uScreenHeight", screenHeight); */ shader.setUniform2f('resolution', material.resolution.toArray()) shader.setUniform1f("fov", Math.PI * camera.fov / 180); shader.setUniform1f("near", camera.near); shader.setUniform1f("far", camera.far); if(camera instanceof THREE.OrthographicCamera){ shader.setUniform("uUseOrthographicCamera", true); shader.setUniform("uOrthoWidth", camera.right - camera.left); shader.setUniform("uOrthoHeight", camera.top - camera.bottom); }else{ shader.setUniform("uUseOrthographicCamera", false); } if(material.clipBoxes.length + material.clipPolygons.length === 0){ shader.setUniform1i("clipTask", ClipTask.NONE); }else{ shader.setUniform1i("clipTask", material.clipTask); } shader.setUniform1i("clipMethod", material.clipMethod); if (material.clipBoxes && material.clipBoxes.length > 0) { //let flattenedMatrices = [].concat(...material.clipBoxes.map(c => c.inverse.elements)); //const lClipBoxes = shader.uniformLocations["clipBoxes[0]"]; //gl.uniformMatrix4fv(lClipBoxes, false, flattenedMatrices); const lClipBoxes = shader.uniformLocations["clipBoxes[0]"]; gl.uniformMatrix4fv(lClipBoxes, false, material.uniforms.clipBoxes.value); } // TODO CLIPSPHERES if(params.clipSpheres && params.clipSpheres.length > 0){ let clipSpheres = params.clipSpheres; let matrices = []; for(let clipSphere of clipSpheres){ //let mScale = new THREE.Matrix4().makeScale(...clipSphere.scale.toArray()); //let mTranslate = new THREE.Matrix4().makeTranslation(...clipSphere.position.toArray()); //let clipToWorld = new THREE.Matrix4().multiplyMatrices(mTranslate, mScale); let clipToWorld = clipSphere.matrixWorld; let viewToWorld = camera.matrixWorld let worldToClip = clipToWorld.clone().invert(); let viewToClip = new THREE.Matrix4().multiplyMatrices(worldToClip, viewToWorld); matrices.push(viewToClip); } let flattenedMatrices = [].concat(...matrices.map(matrix => matrix.elements)); const lClipSpheres = shader.uniformLocations["uClipSpheres[0]"]; gl.uniformMatrix4fv(lClipSpheres, false, flattenedMatrices); //const lClipSpheres = shader.uniformLocations["uClipSpheres[0]"]; //gl.uniformMatrix4fv(lClipSpheres, false, material.uniforms.clipSpheres.value); } shader.setUniform1f("size", material.usePanoMap ? Potree.config.material.absolutePanoramaSize * Math.min(window.devicePixelRatio,2) : material.size);//usePanoMap时控制在不大不小的范围内感觉较好,考虑到有的点云稀疏,用大一点的点 shader.setUniform1f("maxSize", material.uniforms.maxSize.value); shader.setUniform1f("minSize", material.uniforms.minSize.value); // uniform float uPCIndex shader.setUniform1f("uOctreeSpacing", material.spacing); shader.setUniform("uOctreeSize", material.uniforms.octreeSize.value); //uniform vec3 uColor; shader.setUniform3f("uColor", material.color.toArray()); //uniform float opacity; shader.setUniform1f("uOpacity", material.usePanoMap ? 1: material.opacity); shader.setUniform2f("elevationRange", material.elevationRange); shader.setUniform2f("intensityRange", material.intensityRange); shader.setUniform3f("uIntensity_gbc", [ material.intensityGamma, material.intensityBrightness, material.intensityContrast ]); shader.setUniform3f("uRGB_gbc", [ material.rgbGamma, material.rgbBrightness, material.rgbContrast ]); shader.setUniform1f("uTransition", material.transition); shader.setUniform1f("wRGB", material.weightRGB); shader.setUniform1f("wIntensity", material.weightIntensity); shader.setUniform1f("wElevation", material.weightElevation); shader.setUniform1f("wClassification", material.weightClassification); shader.setUniform1f("wReturnNumber", material.weightReturnNumber); shader.setUniform1f("wSourceID", material.weightSourceID); shader.setUniform("backfaceCulling", material.uniforms.backfaceCulling.value); //========================== //gl.TEXTURE_CUBE_MAP: 34067 //gl.TEXTURE0=33984 , vnWebGLTexture.target=gl.TEXTURE_2D = 3353 let vnWebGLTexture = this.textures.get(material.visibleNodesTexture); if(vnWebGLTexture){ shader.setUniform1i("visibleNodesTexture", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(vnWebGLTexture.target, vnWebGLTexture.id); currentTextureBindingPoint++; } let gradientTexture = this.textures.get(material.gradientTexture); shader.setUniform1i("gradient", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(gradientTexture.target, gradientTexture.id); const repeat = material.elevationGradientRepeat; if(repeat === ElevationGradientRepeat.REPEAT){ gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.REPEAT); }else if(repeat === ElevationGradientRepeat.MIRRORED_REPEAT){ gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT); gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT); }else{ gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); } currentTextureBindingPoint++; let classificationTexture = this.textures.get(material.classificationTexture); shader.setUniform1i("classificationLUT", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(classificationTexture.target, classificationTexture.id); currentTextureBindingPoint++; let matcapTexture = this.textures.get(material.matcapTexture); shader.setUniform1i("matcapTextureUniform", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(matcapTexture.target, matcapTexture.id); currentTextureBindingPoint++; if (material.snapEnabled === true) { { const lSnapshot = shader.uniformLocations["uSnapshot[0]"]; const lSnapshotDepth = shader.uniformLocations["uSnapshotDepth[0]"]; let bindingStart = currentTextureBindingPoint; let lSnapshotBindingPoints = new Array(5).fill(bindingStart).map((a, i) => (a + i)); let lSnapshotDepthBindingPoints = new Array(5) .fill(1 + Math.max(...lSnapshotBindingPoints)) .map((a, i) => (a + i)); currentTextureBindingPoint = 1 + Math.max(...lSnapshotDepthBindingPoints); gl.uniform1iv(lSnapshot, lSnapshotBindingPoints); gl.uniform1iv(lSnapshotDepth, lSnapshotDepthBindingPoints); for (let i = 0; i < 5; i++) { let texture = material.uniforms[`uSnapshot`].value[i]; let textureDepth = material.uniforms[`uSnapshotDepth`].value[i]; if (!texture) { break; } let snapTexture = this.threeRenderer.properties.get(texture).__webglTexture; let snapTextureDepth = this.threeRenderer.properties.get(textureDepth).__webglTexture; let bindingPoint = lSnapshotBindingPoints[i]; let depthBindingPoint = lSnapshotDepthBindingPoints[i]; gl.activeTexture(gl[`TEXTURE${bindingPoint}`]); gl.bindTexture(gl.TEXTURE_2D, snapTexture); gl.activeTexture(gl[`TEXTURE${depthBindingPoint}`]); gl.bindTexture(gl.TEXTURE_2D, snapTextureDepth); } } { let flattenedMatrices = [].concat(...material.uniforms.uSnapView.value.map(c => c.elements)); const lSnapView = shader.uniformLocations["uSnapView[0]"]; gl.uniformMatrix4fv(lSnapView, false, flattenedMatrices); } { let flattenedMatrices = [].concat(...material.uniforms.uSnapProj.value.map(c => c.elements)); const lSnapProj = shader.uniformLocations["uSnapProj[0]"]; gl.uniformMatrix4fv(lSnapProj, false, flattenedMatrices); } { let flattenedMatrices = [].concat(...material.uniforms.uSnapProjInv.value.map(c => c.elements)); const lSnapProjInv = shader.uniformLocations["uSnapProjInv[0]"]; gl.uniformMatrix4fv(lSnapProjInv, false, flattenedMatrices); } { let flattenedMatrices = [].concat(...material.uniforms.uSnapViewInv.value.map(c => c.elements)); const lSnapViewInv = shader.uniformLocations["uSnapViewInv[0]"]; gl.uniformMatrix4fv(lSnapViewInv, false, flattenedMatrices); } } //=============add=========== if(material.usePanoMap){//为什么pointsize失效 shader.setUniform1f("progress", material.uniforms.progress.value); shader.setUniform1f("easeInOutRatio", material.uniforms.easeInOutRatio.value); shader.setUniform3f("pano0Position", material.uniforms.pano0Position.value.toArray()); shader.setUniform3f("pano1Position", material.uniforms.pano1Position.value.toArray()); shader.setUniform('pano0Matrix', material.uniforms.pano0Matrix.value); shader.setUniform('pano1Matrix', material.uniforms.pano1Matrix.value); let pano0Map = material.uniforms.pano0Map.value if(pano0Map){ this.threeRenderer._textures.safeSetTextureCube( pano0Map, ++currentTextureBindingPoint ); shader.setUniform1i('pano0Map', currentTextureBindingPoint); } let pano1Map = material.uniforms.pano1Map.value if(pano1Map){ this.threeRenderer._textures.safeSetTextureCube( pano1Map, ++currentTextureBindingPoint ); shader.setUniform1i('pano1Map', currentTextureBindingPoint); } //注: three.js我添加了个 _textures, safeSetTextureCube里主要就是activeTexture和bindTexture } } this.renderNodes(octree, nodes, visibilityTextureData, camera, target, shader, params); gl.activeTexture(gl.TEXTURE2); gl.bindTexture(gl.TEXTURE_2D, null); gl.activeTexture(gl.TEXTURE0); //add 恢复为不透明(否则renderToCubeMap时的贴图会被渲染成高亮的颜色) gl.disable(gl.BLEND); gl.depthMask(true); gl.enable(gl.DEPTH_TEST); //DEPTH_TEST等需要恢复吗 } render(scene, camera, target = null, params = {}) { const gl = this.gl; // PREPARE if (target != null) { this.threeRenderer.setRenderTarget(target); } //camera.updateProjectionMatrix(); // camera.matrixWorldInverse.invert(camera.matrixWorld); const traversalResult = this.traverse(scene); // RENDER for (const octree of traversalResult.octrees) { let nodes = octree.visibleNodes; this.renderOctree(octree, nodes, camera, target, params); } // CLEANUP gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, null); gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindVertexArray(null); this.threeRenderer.resetState(); } }; /* 中东的链接http://indoor.popsmart.cn:8094/zdoblh-yz/?vlon=5.14&vlat=-0.13&fov=100.0&pc=true&lon=121.61136592&lat=29.87855579&z=16.577 geometry: 有的attributes: 属性是: classification: color: indices: normal: position: 最好有个spacing */