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);
}
Are you checking for errors in the JavaScript console?
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
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.
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
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.