1 /** 2 * potree.js 3 * http://potree.org 4 * 5 * Copyright 2012, Markus Sch�tz 6 * Licensed under the GPL Version 2 or later. 7 * - http://potree.org/wp/?page_id=7 8 * - http://www.gnu.org/licenses/gpl-3.0.html 9 * 10 */ 11 12 /** 13 * Implementation of the "High-Quality Splatting on Today's GPUs" paper. 14 * 15 * @class 16 * @see http://graphics.ucsd.edu/~matthias/Papers/HighQualitySplattingOnGPUs.pdf 17 * 18 */ 19 function FilteredSplatsMaterial(name){ 20 Material.call(this, name); 21 this.depthShader = new Shader(name + "_depth", "filteredSplats/filteredSplatsDepthPass.vs", "filteredSplats/filteredSplatsDepthPass.fs"); 22 this.normalShader = new Shader(name + "_filtered_normal", "filteredSplats/filteredSplatsAttributePass.vs", "filteredSplats/filteredSplatsNormalAttributePass.fs"); 23 this.colorShader = new Shader(name + "_filtered_color", "filteredSplats/filteredSplatsAttributePass.vs", "filteredSplats/filteredSplatsColorAttributePass.fs"); 24 this.normalizationShader = new Shader(name + "normalization", "drawTexture.vs", "filteredSplats/filteredSplatsShadingPass.fs"); 25 this.depthFBO = new FramebufferFloat32(Potree.canvas.width, Potree.canvas.height); 26 this.normalFBO = new FramebufferFloat32(Potree.canvas.width, Potree.canvas.height); 27 this.colorFBO = new FramebufferFloat32(Potree.canvas.width, Potree.canvas.height); 28 29 this.illuminationMode = IlluminationMode.PHONG; 30 this.pointSize = 1.0; 31 this.blendDepth = 0.1; 32 } 33 34 FilteredSplatsMaterial.prototype = new Material(inheriting); 35 36 FilteredSplatsMaterial.prototype.render = function(sceneNode, camera, lights){ 37 var transform = sceneNode.globalTransformation; 38 var pointClouds = new Array(); 39 40 if(sceneNode instanceof PointCloudSceneNode){ 41 pointClouds.push(sceneNode.pointCloud); 42 }else if(sceneNode instanceof PointcloudOctreeSceneNode){ 43 var renderQueue = sceneNode.mno.renderQueue; 44 for(var i = 0; i < renderQueue.length; i++){ 45 var node = renderQueue.get(i); 46 pointClouds.push(node.pointCloud); 47 } 48 } 49 this.renderPointClouds(transform, pointClouds, camera, lights); 50 }; 51 52 FilteredSplatsMaterial.prototype.renderPointClouds = function(transform, pointClouds, camera, lights){ 53 var oldBuffer = Framebuffer.getActiveBuffer(); 54 55 // depth and position 56 this.depthPass(transform, pointClouds, camera); 57 58 // attributes 59 this.colorPass(transform, pointClouds, camera); 60 if(this.illuminationMode == IlluminationMode.PHONG || this.illuminationMode == IlluminationMode.NORMALS){ 61 this.normalPass(transform, pointClouds, camera); 62 } 63 64 // normalization and shading 65 this.shadingPass(oldBuffer, camera, lights); 66 67 // oldBuffer.drawTexture(this.colorFBO.texture, [0,0], [1,1]); 68 // oldBuffer.drawTexture(this.normalFBO.texture, [-1,0], [0,1]); 69 // oldBuffer.drawTexture(this.depthFBO.texture, [-1,0], [0,1]); 70 // oldBuffer.drawTexture(this.depthFBO.texture, [-1,-1], [1,1]); 71 }; 72 73 /** 74 * create depth map. 75 * this pass renders into the depthFBO which is defined as a floating point texture. 76 * linear depth is stored in the first value of each pixel. 77 * 78 */ 79 FilteredSplatsMaterial.prototype.depthPass = function(transform, pointClouds, camera){ 80 81 this.depthFBO.bind(); 82 this.depthFBO.setSize(Potree.canvas.clientWidth, Potree.canvas.clientHeight); 83 gl.viewport(0, 0, Potree.canvas.clientWidth, Potree.canvas.clientHeight); 84 gl.clearColor(0,0,0,0); 85 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 86 87 gl.disable(gl.BLEND); 88 gl.enable(gl.DEPTH_TEST); 89 gl.depthMask(true); 90 91 {// uniforms 92 gl.useProgram(this.depthShader.program); 93 gl.uniformMatrix4fv(this.depthShader.uniforms.uWorld, false, transform); 94 gl.uniformMatrix4fv(this.depthShader.uniforms.uView, false, camera.viewMatrix); 95 gl.uniformMatrix4fv(this.depthShader.uniforms.uProj, false, camera.projectionMatrix); 96 gl.uniform1f(this.depthShader.uniforms.uPointSize, this.pointSize); 97 gl.uniform1f(this.depthShader.uniforms.uNear, camera.nearClipPlane); 98 gl.uniform1f(this.depthShader.uniforms.uFar, camera.farClipPlane); 99 gl.uniform1f(this.depthShader.uniforms.uBlendDepth, this.blendDepth); 100 gl.uniform2f(this.depthShader.uniforms.uWindowSize, Potree.canvas.clientWidth, Potree.canvas.clientHeight); 101 102 var nearHeight = 2*camera.nearClipPlane*Math.tan(camera.fieldOfView*Math.PI/360); 103 var nearWidth = nearHeight*camera.aspectRatio; 104 gl.uniform2f(this.depthShader.uniforms.uNearWindowSize, nearWidth, nearHeight); 105 106 } 107 108 for(var i = 0; i < pointClouds.length; i++){ 109 var pointCloud = pointClouds[i]; 110 var pointAttributes = pointCloud.pointAttributes; 111 112 gl.bindBuffer(gl.ARRAY_BUFFER, pointCloud.vbo); 113 var offset = 0; 114 for(var j = 0; j < pointAttributes.size; j++){ 115 var attribute = pointAttributes.attributes[j]; 116 117 if(attribute == PointAttribute.POSITION_CARTESIAN){ 118 gl.enableVertexAttribArray(this.depthShader.attributes.aVertexPosition); 119 gl.vertexAttribPointer(this.depthShader.attributes.aVertexPosition, 3, gl.FLOAT, false,pointAttributes.byteSize, offset); 120 }else if(attribute == PointAttribute.RGBA_PACKED){ 121 if(this.depthShader.attributes.aVertexColour != null){ 122 gl.enableVertexAttribArray(this.depthShader.attributes.aVertexColour); 123 gl.vertexAttribPointer(this.depthShader.attributes.aVertexColour, 3, gl.UNSIGNED_BYTE, false,pointAttributes.byteSize, offset); 124 } 125 }else if(attribute == PointAttribute.NORMAL_FLOATS){ 126 if(this.depthShader.attributes.aNormal != null){ 127 gl.enableVertexAttribArray(this.depthShader.attributes.aNormal); 128 gl.vertexAttribPointer(this.depthShader.attributes.aNormal, 3, gl.FLOAT, false,pointAttributes.byteSize, offset); 129 } 130 } 131 offset += attribute.byteSize; 132 } 133 134 gl.drawArrays(gl.POINTS, 0, pointCloud.size); 135 gl.disableVertexAttribArray(this.depthShader.attributes.aVertexPosition); 136 gl.disableVertexAttribArray(this.depthShader.attributes.aVertexColour); 137 gl.disableVertexAttribArray(this.depthShader.attributes.aNormal); 138 } 139 140 }; 141 142 FilteredSplatsMaterial.prototype.colorPass = function(transform, pointClouds, camera){ 143 // color fbo 144 this.colorFBO.bind(); 145 this.colorFBO.setSize(Potree.canvas.clientWidth, Potree.canvas.clientHeight); 146 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthFBO.renderbuffer); 147 this.colorFBO.checkBuffer(); 148 gl.viewport(0, 0, Potree.canvas.clientWidth, Potree.canvas.clientHeight); 149 gl.clearColor(0,0,0,0); 150 gl.clear(gl.COLOR_BUFFER_BIT); 151 gl.enable(gl.BLEND); 152 gl.blendFunc(gl.ONE, gl.ONE); 153 gl.enable(gl.DEPTH_TEST); 154 gl.depthFunc(gl.LESS); 155 gl.depthMask(false); 156 157 { // color uniforms 158 gl.useProgram(this.colorShader.program); 159 gl.uniformMatrix4fv(this.colorShader.uniforms.uWorld, false, transform); 160 gl.uniformMatrix4fv(this.colorShader.uniforms.uView, false, camera.viewMatrix); 161 gl.uniformMatrix4fv(this.colorShader.uniforms.uProj, false, camera.projectionMatrix); 162 gl.uniform1f(this.colorShader.uniforms.uPointSize, this.pointSize); 163 gl.uniform1f(this.colorShader.uniforms.uNear, camera.nearClipPlane); 164 gl.uniform1f(this.colorShader.uniforms.uFar, camera.farClipPlane); 165 gl.uniform2f(this.depthShader.uniforms.uWindowSize, Potree.canvas.clientWidth, Potree.canvas.clientHeight); 166 167 var nearHeight = 2*camera.nearClipPlane*Math.tan(camera.fieldOfView*Math.PI/360); 168 var nearWidth = nearHeight*camera.aspectRatio; 169 gl.uniform2f(this.depthShader.uniforms.uNearWindowSize, nearWidth, nearHeight); 170 } 171 172 for(var i = 0; i < pointClouds.length; i++){ 173 var pointCloud = pointClouds[i]; 174 var pointAttributes = pointCloud.pointAttributes; 175 176 gl.bindBuffer(gl.ARRAY_BUFFER, pointCloud.vbo); 177 var offset = 0; 178 for(var j = 0; j < pointAttributes.size; j++){ 179 var attribute = pointAttributes.attributes[j]; 180 181 if(attribute == PointAttribute.POSITION_CARTESIAN){ 182 gl.enableVertexAttribArray(this.colorShader.attributes.aVertexPosition); 183 gl.vertexAttribPointer(this.colorShader.attributes.aVertexPosition, 3, gl.FLOAT, false,pointAttributes.byteSize, offset); 184 }else if(attribute == PointAttribute.RGBA_PACKED){ 185 if(this.colorShader.attributes.aVertexColour != null){ 186 gl.enableVertexAttribArray(this.colorShader.attributes.aVertexColour); 187 gl.vertexAttribPointer(this.colorShader.attributes.aVertexColour, 3, gl.UNSIGNED_BYTE, false,pointAttributes.byteSize, offset); 188 } 189 }else if(attribute == PointAttribute.NORMAL_FLOATS){ 190 if(this.colorShader.attributes.aNormal != null){ 191 gl.enableVertexAttribArray(this.colorShader.attributes.aNormal); 192 gl.vertexAttribPointer(this.colorShader.attributes.aNormal, 3, gl.FLOAT, false,pointAttributes.byteSize, offset); 193 } 194 } 195 offset += attribute.byteSize; 196 } 197 198 gl.drawArrays(gl.POINTS, 0, pointCloud.size); 199 200 gl.disableVertexAttribArray(this.depthShader.attributes.aVertexPosition); 201 gl.disableVertexAttribArray(this.depthShader.attributes.aVertexColour); 202 gl.disableVertexAttribArray(this.depthShader.attributes.aNormal); 203 } 204 gl.depthFunc(gl.LESS); 205 gl.depthMask(true); 206 }; 207 208 FilteredSplatsMaterial.prototype.normalPass = function(transform, pointClouds, camera){ 209 // normal fbo 210 this.normalFBO.bind(); 211 this.normalFBO.setSize(Potree.canvas.clientWidth, Potree.canvas.clientHeight); 212 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthFBO.renderbuffer); 213 this.normalFBO.checkBuffer(); 214 gl.viewport(0, 0, Potree.canvas.clientWidth, Potree.canvas.clientHeight); 215 gl.clearColor(0,0,0,0); 216 gl.clear(gl.COLOR_BUFFER_BIT); 217 gl.enable(gl.BLEND); 218 gl.blendFunc(gl.ONE, gl.ONE); 219 gl.enable(gl.DEPTH_TEST); 220 gl.depthFunc(gl.LESS); 221 gl.depthMask(false); 222 223 { // normal uniforms 224 gl.useProgram(this.normalShader.program); 225 gl.uniformMatrix4fv(this.normalShader.uniforms.uWorld, false, transform); 226 gl.uniformMatrix4fv(this.normalShader.uniforms.uView, false, camera.viewMatrix); 227 gl.uniformMatrix4fv(this.normalShader.uniforms.uProj, false, camera.projectionMatrix); 228 gl.uniform1f(this.normalShader.uniforms.uPointSize, this.pointSize); 229 gl.uniform1f(this.normalShader.uniforms.uNear, camera.nearClipPlane); 230 gl.uniform1f(this.normalShader.uniforms.uFar, camera.farClipPlane); 231 232 gl.uniform2f(this.depthShader.uniforms.uWindowSize, Potree.canvas.clientWidth, Potree.canvas.clientHeight); 233 234 var nearHeight = 2*camera.nearClipPlane*Math.tan(camera.fieldOfView*Math.PI/360); 235 var nearWidth = nearHeight*camera.aspectRatio; 236 gl.uniform2f(this.depthShader.uniforms.uNearWindowSize, nearWidth, nearHeight); 237 } 238 239 for(var i = 0; i < pointClouds.length; i++){ 240 var pointCloud = pointClouds[i]; 241 var pointAttributes = pointCloud.pointAttributes; 242 243 gl.bindBuffer(gl.ARRAY_BUFFER, pointCloud.vbo); 244 var offset = 0; 245 for(var j = 0; j < pointAttributes.size; j++){ 246 var attribute = pointAttributes.attributes[j]; 247 248 if(attribute == PointAttribute.POSITION_CARTESIAN){ 249 gl.enableVertexAttribArray(this.normalShader.attributes.aVertexPosition); 250 gl.vertexAttribPointer(this.normalShader.attributes.aVertexPosition, 3, gl.FLOAT, false,pointAttributes.byteSize, offset); 251 }else if(attribute == PointAttribute.RGBA_PACKED){ 252 if(this.normalShader.attributes.aVertexColour != null){ 253 gl.enableVertexAttribArray(this.normalShader.attributes.aVertexColour); 254 gl.vertexAttribPointer(this.normalShader.attributes.aVertexColour, 3, gl.UNSIGNED_BYTE, false,pointAttributes.byteSize, offset); 255 } 256 }else if(attribute == PointAttribute.NORMAL_FLOATS){ 257 if(this.normalShader.attributes.aNormal != null){ 258 gl.enableVertexAttribArray(this.normalShader.attributes.aNormal); 259 gl.vertexAttribPointer(this.normalShader.attributes.aNormal, 3, gl.FLOAT, false,pointAttributes.byteSize, offset); 260 } 261 } 262 offset += attribute.byteSize; 263 } 264 265 gl.drawArrays(gl.POINTS, 0, pointCloud.size); 266 267 gl.disableVertexAttribArray(this.depthShader.attributes.aVertexPosition); 268 gl.disableVertexAttribArray(this.depthShader.attributes.aVertexColour); 269 gl.disableVertexAttribArray(this.depthShader.attributes.aNormal); 270 } 271 gl.depthFunc(gl.LESS); 272 gl.depthMask(true); 273 }; 274 275 var startTime = new Date().getTime(); 276 277 /** 278 * normalize output from second pass. 279 * rgb values are divided by the weight(stored in the alpha component) and alpha is set to 1. 280 * renders into the buffer which was active before this material was used. 281 * 282 */ 283 FilteredSplatsMaterial.prototype.shadingPass = function(oldBuffer, camera, lights){ 284 oldBuffer.bind(); 285 286 // var cColor = Potree.Settings.backgroundColor; 287 // gl.clearColor(cColor[0], cColor[1], cColor[2], cColor[3]); 288 gl.clearColor(0,0,0,1.0); 289 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 290 291 gl.useProgram(this.normalizationShader.program); 292 gl.disable(gl.DEPTH_TEST); 293 gl.enable(gl.BLEND); 294 gl.blendFunc(gl.ONE, gl.ONE); 295 296 // normal 297 // if(this.illuminationMode == IlluminationMode.PHONG || this.illuminationMode == IlluminationMode.NORMALS){ 298 gl.activeTexture(gl.TEXTURE0); 299 gl.bindTexture(gl.TEXTURE_2D, this.normalFBO.texture.glid); 300 gl.uniform1i(this.normalizationShader.uniforms.uNormal, 0); 301 // } 302 303 // color 304 // if(this.illuminationMode == IlluminationMode.FLAT || this.illuminationMode == IlluminationMode.PHONG){ 305 gl.activeTexture(gl.TEXTURE0 + 1); 306 gl.bindTexture(gl.TEXTURE_2D, this.colorFBO.texture.glid); 307 gl.uniform1i(this.normalizationShader.uniforms.uColor, 1); 308 // } 309 310 // position 311 // if(this.illuminationMode == IlluminationMode.PHONG || this.illuminationMode == IlluminationMode.POSITIONS){ 312 gl.activeTexture(gl.TEXTURE0 + 2); 313 gl.bindTexture(gl.TEXTURE_2D, this.depthFBO.texture.glid); 314 gl.uniform1i(this.normalizationShader.uniforms.uPosition, 2); 315 // } 316 317 gl.uniform1i(this.normalizationShader.uniforms.uIlluminationMode, this.illuminationMode.value); 318 319 if(this.illuminationMode == IlluminationMode.PHONG){ 320 for(var i = 0; i < lights.length; i++){ 321 var light = lights[i]; 322 var pos = light.globalPosition; 323 324 gl.uniform3f(this.normalizationShader.uniforms.uLightPos, pos.x, pos.y, pos.z); 325 gl.uniform3f(this.normalizationShader.uniforms.uLightColor, light.red, light.green, light.blue); 326 oldBuffer.drawFullscreenQuad(this.normalizationShader); 327 } 328 }else{ 329 oldBuffer.drawFullscreenQuad(this.normalizationShader); 330 } 331 }; 332