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 * You can call the constructor with either, width and height, or "system". When 14 * invoking the constructor with "system", the framebuffer will refer to the 15 * system/default framebuffer. 16 * 17 * @param {int} 18 * width 19 * @param {int} 20 * height 21 * @class create and handle FramebufferObjects 22 * @see <a href="http://learningwebgl.com/blog/?p=1786">WebGL Lesson 16</a> 23 */ 24 function Framebuffer(width, height) { 25 if (arguments[0] === inheriting) 26 return; 27 if (arguments[0] == "system") { 28 this.framebuffer = null; 29 this.initOtherStuff(); 30 } else { 31 this.initBufferStuff(width, height); 32 this.initOtherStuff(); 33 } 34 } 35 36 /** 37 * 38 * @returns the system framebuffer 39 */ 40 Framebuffer.getSystemBuffer = function() { 41 if (Framebuffer.systemBuffer == null) { 42 Framebuffer.systemBuffer = new Framebuffer("system"); 43 } 44 45 return Framebuffer.systemBuffer; 46 }; 47 48 Framebuffer.getActiveBuffer = function() { 49 if (Framebuffer.activeBuffer == null) { 50 Framebuffer.activeBuffer = Framebuffer.getSystemBuffer(); 51 } 52 53 return Framebuffer.activeBuffer; 54 }; 55 56 Framebuffer.setActiveBuffer = function(buffer) { 57 Framebuffer.activeBuffer = buffer; 58 }; 59 60 /** 61 * change size of the framebuffer. 62 * 63 * @param {int} 64 * width 65 * @param {int} 66 * height 67 */ 68 Framebuffer.prototype.setSize = function(width, height) { 69 this.initBufferStuff(width, height); 70 }; 71 72 /** 73 * Initialize stuff like a vbo for screen quads. 74 */ 75 Framebuffer.prototype.initOtherStuff = function() { 76 77 this.vbo = gl.createBuffer(); 78 this.texcoordvbo = gl.createBuffer(); 79 gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo); 80 var vertices = new Float32Array([ 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 81 0, 0, 1, 0 ]); 82 gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); 83 84 gl.bindBuffer(gl.ARRAY_BUFFER, this.texcoordvbo); 85 var vertices = new Float32Array([ 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1 ]); 86 gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); 87 88 }; 89 90 /** 91 * change size of screenQuad. 92 * 93 * @param {[x,y]} 94 * start x and y must be values between 0 and 1 95 * @param {[x,y]} 96 * end x and y must be values between 0 and 1 97 */ 98 Framebuffer.prototype.updateScreenQuad = function(start, end) { 99 gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo); 100 var vertices = new Float32Array([ start[0], start[1], 0, end[0], start[1], 101 0, end[0], end[1], 0, start[0], start[1], 0, end[0], end[1], 0, 102 start[0], end[1], 0 ]); 103 gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); 104 }; 105 106 /** 107 * removes existing webgl color/render/frame-buffers and creates new ones 108 * 109 * @param {int} 110 * width 111 * @param {int} 112 * height 113 */ 114 Framebuffer.prototype.initBufferStuff = function(width, height) { 115 if (this.width == width && this.height == height) { 116 return; 117 } 118 119 this.width = width; 120 this.height = height; 121 122 // remove exiting buffers 123 gl.bindFramebuffer(gl.FRAMEBUFFER, null); 124 if (this.texture != null) { 125 gl.deleteTexture(this.texture.glid); 126 this.texture = null; 127 } 128 if (this.renderbuffer != null) { 129 gl.deleteRenderbuffer(this.renderbuffer); 130 this.renderbuffer = null; 131 } 132 if (this.framebuffer != null) { 133 gl.deleteFramebuffer(this.framebuffer); 134 this.framebuffer = null; 135 } 136 137 // create new buffers 138 this.framebuffer = gl.createFramebuffer(); 139 this.texture = new Texture(); 140 this.texture.glid = gl.createTexture(); 141 this.renderbuffer = gl.createRenderbuffer(); 142 143 // WEBGL_depth_texture not supported in firefox/ANGLE 144 // this.depthTexture = new Texture(); 145 // this.depthTexture.glid = gl.createTexture(); 146 147 // framebuffer 148 gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); 149 this.framebuffer.width = width; 150 this.framebuffer.height = height; 151 152 // colorbuffer 153 this.initColorbuffer(); 154 155 // depthbuffer 156 gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderbuffer); 157 gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 158 this.framebuffer.width, this.framebuffer.height); 159 160 // WEBGL_depth_texture not supported in firefox/ANGLE 161 // gl.bindTexture(gl.TEXTURE_2D, this.depthTexture.glid); 162 // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 163 // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 164 // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 165 // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 166 // gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, this.framebuffer.width, this.framebuffer.height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_BYTE, null); 167 168 169 // assemble buffers 170 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 171 gl.TEXTURE_2D, this.texture.glid, 0); 172 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, 173 gl.RENDERBUFFER, this.renderbuffer); 174 175 // WEBGL_depth_texture not supported in firefox/ANGLE 176 // gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.depthTexture.glid, 0); 177 178 this.checkBuffer(); 179 180 gl.bindTexture(gl.TEXTURE_2D, null); 181 gl.bindRenderbuffer(gl.RENDERBUFFER, null); 182 gl.bindFramebuffer(gl.FRAMEBUFFER, null); 183 }; 184 185 /** 186 * initializes the colourbuffer that'll be used as COLOR_ATTACHMENT0 187 */ 188 Framebuffer.prototype.initColorbuffer = function() { 189 gl.bindTexture(gl.TEXTURE_2D, this.texture.glid); 190 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 191 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 192 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 193 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 194 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.framebuffer.width, 195 this.framebuffer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 196 }; 197 198 /** 199 * check if framebuffer was successfully created. Throws an exception if the 200 * buffer is invalid. 201 */ 202 Framebuffer.prototype.checkBuffer = function checkBuffer() { 203 gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); 204 205 var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); 206 switch (status) { 207 case gl.FRAMEBUFFER_COMPLETE: 208 break; 209 case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 210 Logger 211 .error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); 212 throw ""; 213 break; 214 case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 215 Logger 216 .error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); 217 throw ""; 218 break; 219 case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 220 Logger 221 .error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS"); 222 throw ""; 223 break; 224 case gl.FRAMEBUFFER_UNSUPPORTED: 225 Logger.error("Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED"); 226 throw ""; 227 break; 228 default: 229 Logger.error("Incomplete framebuffer: " + status); 230 throw ""; 231 } 232 }; 233 234 Framebuffer.bindDefault = function() { 235 Framebuffer.activeBuffer = Framebuffer.getSystemBuffer(); 236 gl.bindFramebuffer(gl.FRAMEBUFFER, null); 237 }; 238 239 /** 240 * binds this framebuffer which makes it the target for all drawCalls and 241 * framebuffer related calls. 242 */ 243 Framebuffer.prototype.bind = function() { 244 Framebuffer.activeBuffer = this; 245 gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); 246 }; 247 248 /** 249 * 250 * @param {Texture} 251 * texture 252 * @param {[x,y]} 253 * start 254 * @param {[x,y]} 255 * end 256 * 257 * @example drawTexture(abc, [0,0], [1,1]) 258 */ 259 Framebuffer.prototype.drawTexture = function(texture, start, end) { 260 this.bind(); 261 262 var mat = ShaderManager.getShader("drawTexture"); 263 gl.useProgram(mat.program); 264 265 this.updateScreenQuad(start, end); 266 267 // uniforms 268 if (this.framebuffer == null) { 269 var canvas = document.getElementById("canvas"); 270 gl.uniform1f(mat.uWidth, canvas.width); 271 gl.uniform1f(mat.uHeight, canvas.height); 272 } else { 273 gl.uniform1f(mat.uWidth, this.width); 274 gl.uniform1f(mat.uHeight, this.height); 275 } 276 // texture 277 gl.activeTexture(gl.TEXTURE0); 278 gl.bindTexture(gl.TEXTURE_2D, texture.glid); 279 gl.uniform1i(mat.uTexture, 0); 280 // depth 281 gl.activeTexture(gl.TEXTURE1); 282 gl.bindTexture(gl.TEXTURE_2D, this.renderbuffer); 283 gl.uniform1i(mat.uDepth, 0); 284 285 // vertex attributes 286 gl.enableVertexAttribArray(mat.aVertexPosition); 287 gl.enableVertexAttribArray(mat.aTexcoords); 288 289 gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo); 290 gl.vertexAttribPointer(mat.aVertexPosition, 3, gl.FLOAT, false, 0, 0); 291 292 gl.bindBuffer(gl.ARRAY_BUFFER, this.texcoordvbo); 293 gl.vertexAttribPointer(mat.aTexcoords, 2, gl.FLOAT, false, 0, 0); 294 295 gl.disable(gl.DEPTH_TEST); 296 gl.drawArrays(gl.TRIANGLES, 0, 6); 297 298 gl.disableVertexAttribArray(mat.aVertexPosition); 299 gl.disableVertexAttribArray(mat.aTexcoords); 300 }; 301 302 /** 303 * draws a quad over the whole framebuffer using the provided material. 304 * 305 * @param {Material} 306 * mat 307 */ 308 Framebuffer.prototype.drawFullscreenQuad = function(mat) { 309 310 gl.useProgram(mat.program); 311 312 this.updateScreenQuad([ -1, -1 ], [ 1, 1 ]); 313 314 // vertex attributes 315 gl.enableVertexAttribArray(mat.attributes.aVertexPosition); 316 gl.enableVertexAttribArray(mat.attributes.aTexcoord); 317 318 gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo); 319 gl.vertexAttribPointer(mat.attributes.aVertexPosition, 3, gl.FLOAT, false, 0, 0); 320 321 gl.bindBuffer(gl.ARRAY_BUFFER, this.texcoordvbo); 322 gl.vertexAttribPointer(mat.attributes.aTexcoord, 2, gl.FLOAT, false, 0, 0); 323 324 gl.drawArrays(gl.TRIANGLES, 0, 6); 325 326 gl.disableVertexAttribArray(mat.attributes.aVertexPosition); 327 gl.disableVertexAttribArray(mat.attributes.aTexcoord); 328 }; 329