|
83 | 83 | let xrButton = null; |
84 | 84 | let xrRefSpace = null; |
85 | 85 |
|
| 86 | + // WebGL scene globals. |
| 87 | + let gl = null; |
| 88 | + let renderer = null; |
| 89 | + |
| 90 | + function createFramebuffer(texture, arrayLayer) { |
| 91 | + const framebuffer = gl.createFramebuffer(); |
| 92 | + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); |
| 93 | + if(arrayLayer !== null) { |
| 94 | + gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture, 0, arrayLayer); |
| 95 | + } else { |
| 96 | + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); |
| 97 | + } |
| 98 | + return framebuffer; |
| 99 | + } |
| 100 | + |
| 101 | + let scene = new Scene(); |
| 102 | + scene.addNode(new Gltf2Node({url: 'media/gltf/space/space.gltf'})); |
| 103 | + scene.addNode(new SkyboxNode({url: 'media/textures/milky-way-4k.png'})); |
| 104 | + |
86 | 105 | /* @test: 2d canvas setup */ |
87 | 106 |
|
88 | 107 | const test = { |
89 | 108 | multipleUpload: true, |
90 | | - multipleTextures: false, |
| 109 | + multipleTextures: true, |
91 | 110 | useMultipleCanvases: false, |
92 | 111 | useTexture2D: false, |
93 | | - canvasSize: [512, 512], |
| 112 | + useBlit: true, |
| 113 | + tempBuffer: false, |
| 114 | + subTexture: false, |
| 115 | + canvasSize: [1024, 1024], |
94 | 116 | atlasSize: 1024, |
95 | 117 | layers: 1 |
96 | 118 | }; |
|
100 | 122 | .map(_ => { |
101 | 123 | if(test.useTexture2D) { |
102 | 124 | return new Custom2DTexture(test.atlasSize, test.atlasSize); |
| 125 | + } else { |
| 126 | + return new ArrayTexture(test.atlasSize, test.atlasSize, test.layers); |
103 | 127 | } |
104 | | - return new ArrayTexture(test.atlasSize, test.atlasSize, test.layers); |
105 | 128 | }); |
106 | 129 |
|
| 130 | + const buffers = new Array(2); |
| 131 | + |
107 | 132 | const canvases = new Array(2) |
108 | 133 | .fill() |
109 | 134 | .map(_ => { |
|
114 | 139 | return canvas; |
115 | 140 | }); |
116 | 141 |
|
117 | | - // WebGL scene globals. |
118 | | - let gl = null; |
119 | | - let renderer = null; |
120 | | - let scene = new Scene(); |
121 | | - scene.addNode(new Gltf2Node({url: 'media/gltf/space/space.gltf'})); |
122 | | - scene.addNode(new SkyboxNode({url: 'media/textures/milky-way-4k.png'})); |
123 | 142 | const boxMaterial = new PbrMaterial(); |
124 | 143 | boxMaterial.baseColorFactor.value = [1.0, 1.0, 1.0, 1.0]; |
125 | 144 | boxMaterial.baseColor.texture = textures[0]; |
126 | 145 | boxMaterial.arrayLayer.value = 5.0; |
127 | 146 |
|
| 147 | + function createBlitProgram() { |
| 148 | + const vsSource = ` |
| 149 | + attribute vec2 a_position; |
| 150 | + attribute vec2 a_texCoord; |
| 151 | + varying vec2 v_texCoord; |
| 152 | + void main() { |
| 153 | + gl_Position = vec4(a_position, 0, 1); |
| 154 | + v_texCoord = a_texCoord; |
| 155 | + }`; |
| 156 | + const fsSource = ` |
| 157 | + precision mediump float; |
| 158 | + uniform sampler2D u_texture; |
| 159 | + varying vec2 v_texCoord; |
| 160 | + void main() { |
| 161 | + gl_FragColor = texture2D(u_texture, v_texCoord); |
| 162 | + gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); |
| 163 | + }`; |
| 164 | + |
| 165 | + function compileShader(type, source) { |
| 166 | + const shader = gl.createShader(type); |
| 167 | + gl.shaderSource(shader, source); |
| 168 | + gl.compileShader(shader); |
| 169 | + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { |
| 170 | + console.error(gl.getShaderInfoLog(shader)); |
| 171 | + return null; |
| 172 | + } |
| 173 | + return shader; |
| 174 | + } |
| 175 | + |
| 176 | + const vs = compileShader(gl.VERTEX_SHADER, vsSource); |
| 177 | + const fs = compileShader(gl.FRAGMENT_SHADER, fsSource); |
| 178 | + const program = gl.createProgram(); |
| 179 | + gl.attachShader(program, vs); |
| 180 | + gl.attachShader(program, fs); |
| 181 | + gl.linkProgram(program); |
| 182 | + return program; |
| 183 | + } |
| 184 | + |
| 185 | + function quadVertexBuffer() { |
| 186 | + // Fullscreen quad (2 triangles forming a rectangle) |
| 187 | + // Interleaved: [posX, posY, texU, texV] |
| 188 | + const quadVertices = new Float32Array([ |
| 189 | + -1, -1, 0, 0, |
| 190 | + 1, -1, 1, 0, |
| 191 | + -1, 1, 0, 1, |
| 192 | + 1, 1, 1, 1, |
| 193 | + ]); |
| 194 | + const quadBuffer = gl.createBuffer(); |
| 195 | + gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer); |
| 196 | + gl.bufferData(gl.ARRAY_BUFFER, quadBuffer, gl.STATIC_DRAW); |
| 197 | + return quadBuffer; |
| 198 | + } |
| 199 | + |
| 200 | + let blitProgram = null; |
| 201 | + let blitQuad = null; |
| 202 | + |
| 203 | + function blitTexture(framebuffer, texture, width, height) { |
| 204 | + if(!blitProgram) { |
| 205 | + blitProgram = createBlitProgram(); |
| 206 | + blitQuad = quadVertexBuffer(); |
| 207 | + } |
| 208 | + const a_position = gl.getAttribLocation(blitProgram, 'a_position'); |
| 209 | + const a_texCoord = gl.getAttribLocation(blitProgram, 'a_texCoord'); |
| 210 | + |
| 211 | + gl.viewport(0, 0, width, height); |
| 212 | + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); |
| 213 | + gl.clearColor(0, 0, 0, 1); |
| 214 | + gl.clear(gl.COLOR_BUFFER_BIT); |
| 215 | + |
| 216 | + gl.useProgram(blitProgram); |
| 217 | + gl.bindBuffer(gl.ARRAY_BUFFER, blitQuad); |
| 218 | + |
| 219 | + gl.enableVertexAttribArray(a_position); |
| 220 | + gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 16, 0); |
| 221 | + |
| 222 | + gl.enableVertexAttribArray(a_texCoord); |
| 223 | + gl.vertexAttribPointer(a_texCoord, 2, gl.FLOAT, false, 16, 8); |
| 224 | + |
| 225 | + gl.activeTexture(gl.TEXTURE0); |
| 226 | + gl.bindTexture(gl.TEXTURE_2D, texture); |
| 227 | + gl.uniform1i(gl.getUniformLocation(blitProgram, 'u_texture'), 0); |
| 228 | + |
| 229 | + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); |
| 230 | + } |
| 231 | + |
| 232 | + function createBlitTexture(img) { |
| 233 | + let tempTexture = gl.createTexture(); |
| 234 | + gl.bindTexture(gl.TEXTURE_2D, tempTexture); |
| 235 | + gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, img.width, img.height) |
| 236 | + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); |
| 237 | + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); |
| 238 | + gl.bindTexture(gl.TEXTURE_2D, null); |
| 239 | + return tempTexture; |
| 240 | + } |
| 241 | + |
| 242 | + let blitTexturePing = null; |
| 243 | + let blitTexturePong = null; |
| 244 | + let blitTextureNext = null; |
| 245 | + |
128 | 246 | function uploadTextureLayer(texture, layer, img, offsets = [0, 0]) { |
129 | | - const textureGL = renderer._getRenderTexture(texture); |
130 | | - if(test.useTexture2D) { |
131 | | - gl.bindTexture(gl.TEXTURE_2D, textureGL._texture); |
| 247 | + const textureGL = renderer._getRenderTexture(texture)._texture; |
| 248 | + if(test.useBlit) { |
| 249 | + if(!blitTextureNext) { |
| 250 | + blitTexturePing = createBlitTexture(img); |
| 251 | + blitTexturePong = createBlitTexture(img); |
| 252 | + blitTextureNext = blitTexturePing; |
| 253 | + } |
| 254 | + gl.bindTexture(gl.TEXTURE_2D, blitTextureNext); |
| 255 | + gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, img.width, img.height, |
| 256 | + gl.RGBA, gl.UNSIGNED_BYTE, img); |
| 257 | + gl.bindTexture(gl.TEXTURE_2D, null); |
| 258 | + if(!texture.framebuffers) { |
| 259 | + texture.framebuffers = new Array(test.layers) |
| 260 | + .fill() |
| 261 | + .map((value, index) => { |
| 262 | + if(test.useTexture2D) { |
| 263 | + return createFramebuffer(textureGL, null); |
| 264 | + } else { |
| 265 | + return createFramebuffer(textureGL, index); |
| 266 | + } |
| 267 | + }); |
| 268 | + } |
| 269 | + blitTexture(texture.framebuffers[layer], blitTextureNext, img.width, img.height); |
| 270 | + blitTextureNext = blitTextureNext === blitTexturePing ? blitTexturePong : blitTexturePing; |
| 271 | + } |
| 272 | + else if(test.useTexture2D) { |
| 273 | + gl.bindTexture(gl.TEXTURE_2D, textureGL); |
132 | 274 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); |
133 | 275 | gl.texSubImage2D( |
134 | 276 | gl.TEXTURE_2D, |
|
142 | 284 | img |
143 | 285 | ); |
144 | 286 | } else { |
145 | | - gl.bindTexture(gl.TEXTURE_2D_ARRAY, textureGL._texture); |
| 287 | + gl.bindTexture(gl.TEXTURE_2D_ARRAY, textureGL); |
146 | 288 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); |
147 | 289 | gl.texSubImage3D( |
148 | 290 | gl.TEXTURE_2D_ARRAY, |
|
332 | 474 |
|
333 | 475 | // console.log(`Drawing to image ${nextTexture.textureKey}, displaying image ${boxMaterial.baseColor.texture.textureKey}`); |
334 | 476 |
|
335 | | - uploadTextureLayer(nextTexture, currentLayerToUpdate++, canvases[0]); |
| 477 | + if(test.tempBuffer) { |
| 478 | + const ctx = canvases[0].getContext('2d'); |
| 479 | + buffers[0] = ctx.getImageData(0, 0, canvases[0].width, canvases[0].height); |
| 480 | + uploadTextureLayer(nextTexture, currentLayerToUpdate++, buffers[0]); |
| 481 | + } else { |
| 482 | + uploadTextureLayer(nextTexture, currentLayerToUpdate++, canvases[0]); |
| 483 | + } |
336 | 484 | currentLayerToUpdate = currentLayerToUpdate%test.layers; |
337 | 485 | if(test.multipleUpload) { |
338 | 486 | const canvas = test.useMultipleCanvases ? canvases[1] : canvases[0]; |
339 | | - const offsets = [canvas.width*0.5, canvas.height*0.5]; |
340 | | - uploadTextureLayer(nextTexture, currentLayerToUpdate, canvas, offsets); |
| 487 | + uploadTextureLayer(nextTexture, currentLayerToUpdate, canvas); |
| 488 | + if(test.tempBuffer) { |
| 489 | + const ctx = canvas.getContext('2d'); |
| 490 | + buffers[1] = ctx.getImageData(0, 0, canvas.width, canvas.height); |
| 491 | + uploadTextureLayer(nextTexture, currentLayerToUpdate++, buffers[1]); |
| 492 | + } else { |
| 493 | + uploadTextureLayer(nextTexture, currentLayerToUpdate++, canvas); |
| 494 | + } |
341 | 495 | currentLayerToUpdate = currentLayerToUpdate%test.layers; |
342 | 496 | } |
343 | 497 |
|
|
0 commit comments