I am using the render to texture method for creating a multi shader program. And for various reasons. I need to first render a model to a texture with one shader program. Then render it again with a different shader program into a different texture. Then I have one final post processing shader that combines the results of the two. My problem is it seems that the second texture is overwriting the first texture. Is there a way to move textures? Or to render to a different texture without overwriting it. (Thanks for any help in advance!)
I have attached my code for reference:
// Basic rendering parameters var mvMatrix = mat4.create(); // Model-view matrix for the main object var pMatrix = mat4.create(); // Projection matrix // Lighting control var lightMatrix = mat4.create(); // Model-view matrix for the point light source var lightPos = vec3.create(); // Camera-space position of the light source var lightPower = 5.0; // "Power" of the light source // Common parameters for shading models var diffuseColor = [0.2392, 0.5216, 0.7765]; // Diffuse color var specularColor = [1.0, 1.0, 1.0]; // Specular color var ambientIntensity = 0.1; // Ambient // Animation related variables var rotY = 0.0; // object rotation var rotY_light = 0.0; // light position rotation //Set the shader variables from pMat (projection matrix) and // from mMat which is the model and view transforms function setUniforms(prog,pMat,mMat) { gl.uniformMatrix4fv(prog.pMatrixUniform, false, pMat); gl.uniformMatrix4fv(prog.mvMatrixUniform, false, mMat); var nMatrix = mat4.transpose(mat4.inverse(mMat)); gl.uniformMatrix4fv(prog.nMatrixUniform, false, nMatrix); gl.uniform3fv(prog.lightPosUniform, lightPos); gl.uniform1f(prog.lightPowerUniform, lightPower); gl.uniform3fv(prog.kdUniform, diffuseColor); gl.uniform3fv(prog.ksUniform, specularColor); gl.uniform1f(prog.ambientUniform, ambientIntensity); } function setLightPosition() { mat4.identity(lightMatrix); mat4.translate(lightMatrix, [0.0, -1.0, -7.0]); mat4.rotateX(lightMatrix, 0.3); mat4.rotateY(lightMatrix, rotY_light); lightPos.set([0.0, 2.5, 3.0]); mat4.multiplyVec3(lightMatrix, lightPos); } var draw_edge = true; var draw_light = false; //will need to be updated to allow for multiple meshes //Does the toon rendering of the scene to a texture so that we can post process on it function renderSceneToTexture(shaderProg,mesh,color,depth,mMat,width,height) { var textOuput = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D,textOuput); var format; var internalFormat; if(color) { internalFormat= gl.RGBA; format = gl.RGBA; } else { internalFormat= gl.LUMINANCE_ALPHA; format = gl.LUMINANCE_ALPHA; } gl.texImage2D(gl.TEXTURE_2D,0,internalFormat,width,height,0,format,gl.UNSIGNED_BYTE,null); //set out of bounds accesses to clamp and set sub pixel accesses to lerp gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE); const fb = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER,fb); //set frame buffer to first attacthment position gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D,textOuput,0); var depthBuffer; if(depth) { depthBuffer = gl.createRenderbuffer(); gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height); gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer); } var pMat = mat4.create(); mat4.perspective(35, width/height, 0.1, 1000.0, pMat); // Tell WebGL how to convert from clip space to pixels gl.viewport(0, 0, width, height); // Clear the attachment(s). gl.clearColor(0.3, 0.3, 0.3, 1.0); // clear to black gl.enable(gl.DEPTH_TEST); gl.clear(gl.COLOR_BUFFER_BIT| gl.DEPTH_BUFFER_BIT); gl.useProgram(shaderProg); setUniforms(shaderProg,pMat,mMat); gl.bindBuffer(gl.ARRAY_BUFFER, mesh.vertexBuffer); gl.vertexAttribPointer(shaderProg.vertexPositionAttribute, mesh.vertexBuffer.itemSize, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, mesh.normalBuffer); gl.vertexAttribPointer(shaderProg.vertexNormalAttribute, mesh.normalBuffer.itemSize, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.indexBuffer); gl.drawElements(gl.TRIANGLES, mesh.indexBuffer.numItems, gl.UNSIGNED_SHORT, 0); if ( draw_light ) { gl.useProgram(lightProgram); gl.uniformMatrix4fv(lightProgram.pMatrixUniform, false, pMat); gl.bindBuffer(gl.ARRAY_BUFFER, lightPositionBuffer); gl.bufferData(gl.ARRAY_BUFFER, Float32Array.from(lightPos), gl.DYNAMIC_DRAW); gl.vertexAttribPointer(lightProgram.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); gl.drawArrays(gl.POINTS, 0, 1); } //should I unbind texture? gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.deleteFramebuffer(fb); // render to the canvas //gl.useProgram(null); gl.bindTexture(gl.TEXTURE_2D,null); return textOuput; } //mat4.copy was giving me errors, so I just copied the source code in here lol function copy(out, a) { out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; out[4] = a[4]; out[5] = a[5]; out[6] = a[6]; out[7] = a[7]; out[8] = a[8]; out[9] = a[9]; out[10] = a[10]; out[11] = a[11]; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } //Set the shader variables from pMat (projection matrix) and // from mMat which is the model and view transforms function setPostprocessingUniforms(prog,texture,normalTexture, width, height) { gl.activeTexture(gl.TEXTURE0); //Do I need this? gl.bindTexture(gl.TEXTURE_2D, texture); //and this? gl.uniform1i(prog.uTextureUniform, texture); gl.activeTexture(gl.TEXTURE1); //Do I need this? gl.bindTexture(gl.TEXTURE_2D, normalTexture); //and this? gl.uniform1i(prog.normImageTextureUniform, normalTexture); gl.uniform2fv(prog.uTextureSizeUniform, [width,height]); gl.uniform2fv(prog.uResolutionUniform, [width,height]); } function setRectangle(gl, x, y, width, height) { var x1 = x; var x2 = x + width; var y1 = y; var y2 = y + height; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ x1, y1, x2, y1, x1, y2, x1, y2, x2, y1, x2, y2, ]), gl.STATIC_DRAW); } function drawScene() { mat4.identity(mvMatrix); mat4.translate(mvMatrix, [0.0, -1.0, -7.0]); mat4.rotateX(mvMatrix, 0.3); mat4.rotateY(mvMatrix, rotY); mat4.multiply(mvMatrix, currentTransform); var cpy = mat4.create(); copy(cpy,mvMatrix); setLightPosition(); //consumes cpy matrix //actual shader but writes to a texture var normalsAsTexture = renderSceneToTexture(normalPassProgram,currentMesh,true,true,mvMatrix,gl.viewportWidth,gl.viewportHeight); var sceneAsTexture = renderSceneToTexture(currentProgram,currentMesh,true,true,cpy,gl.viewportWidth,gl.viewportHeight); // Create a buffer to put three 2d clip space points in var positionBuffer = gl.createBuffer(); // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer) gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // Set a rectangle the same size as the image. setRectangle(gl, 0, 0, gl.viewportWidth, gl.viewportHeight); //screen is just 2 triangles, so we will post process on that var texcoordBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer); //bug is probably here and in set rectangle gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, ]), gl.STATIC_DRAW); //Post-process shader gl.useProgram(postProcessProgram); setPostprocessingUniforms(postProcessProgram,sceneAsTexture,normalsAsTexture, gl.viewportWidth, gl.viewportHeight); //gl.activeTexture(texture); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER) var size = 2; // 2 components per iteration var type = gl.FLOAT; // the data is 32bit floats var normalize = false; // don't normalize the data var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position var offset = 0; // start at the beginning of the buffer gl.vertexAttribPointer( postProcessProgram.vertexPositionAttribute, size, type, normalize, stride, offset); gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer); gl.vertexAttribPointer( postProcessProgram.texturePositionAttribute, size, type, normalize, stride, offset); // Draw the rectangle. var primitiveType = gl.TRIANGLES; var offset = 0; var count = 6; gl.drawArrays(primitiveType, offset, count); /* setUniforms(postProcessProgram,pMatrix,mvMatrix); gl.bindBuffer(gl.ARRAY_BUFFER, currentMesh.vertexBuffer); gl.vertexAttribPointer(postProcessProgram.vertexPositionAttribute, currentMesh.vertexBuffer.itemSize, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, currentMesh.normalBuffer); gl.vertexAttribPointer(postProcessProgram.vertexNormalAttribute, currentMesh.normalBuffer.itemSize, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, currentMesh.indexBuffer); gl.drawElements(gl.TRIANGLES, currentMesh.indexBuffer.numItems, gl.UNSIGNED_SHORT, 0); */ gl.bindTexture(gl.TEXTURE_2D,null); gl.deleteTexture(sceneAsTexture); gl.deleteTexture(normalsAsTexture); }
Advertisement
Answer
Are you checking for errors in the JavaScript console?
LUMINANCE_ALPHA is not guaranteed to be renderable
Being able to render to gl.LUMINANCE_ALPHA
is not a format that is guaranteed to work. In WebGL 1 only gl.RGBA/gl.UNSIGNED_BYTE
is guaranteed to work. All other format/type combos are not. You can check by calling
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); const canRender = status === gl.FRAMEBUFFER_COMPLETE;
Though given rendering to LUMINANCE_ALPHA
is commonly not supported you might want to just avoid it. It’s not supported on MacOS for sure.
gl.viewportWidth, gl.viewportHeight do not exist
gl.viewportWidth
and gl.viewportHeight
are not a thing, they don’t exist. They are not part of any spec. One of the first WebGL tutorials (no longer on the net) made those up and confused thousands of devs.
the code never turns on buffers on attributes
You need to call gl.enableVertexAttribArray
for each attribute that you
want get its values from a buffer. Or to put it another way, for each call to gl.vertexAttribPointer
you also need to call gl.enableVertexAttribArray
sampler uniforms are set to texture units not textures
This code
gl.uniform1i(prog.uTextureUniform, texture);
makes no sense. Textures are bound to an array of texture units. You select the unit with gl.activeTexture
and then bind the texture to that unit with gl.bindTexture
. You then tell the shader which unit you assigned the texture with gl.uniform1i
. See this
const unit = 6; // pick a unit // bind some texture to that unit gl.activeTexture(gl.TEXTURE0 + unit); gl.bindTexture(gl.TEXTURE_2D, someTexture); // tell the shader to look at that unit for the texture gl.uniform1i(someSamplerLocation, unit);
Other comments
it’s pretty much never important to unbind a texture
it’s not common to create and delete resources while rendering
creating and deleting textures and framebuffers is slow. It would be more common to create them at init time and just use them at render time.
Try to post a runnable snippet in the future
not sure what math library you’re using but it looks kind of like gl-matrix? Many calls to
mat4.xxx
do not match the current API for gl-matrix but without the source of your matrix library it’s hard to tell if those calls are correct.