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