Skip to content

Multiple passes render to separate textures

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.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();
    var format; var internalFormat;
        internalFormat= gl.RGBA;
        format = gl.RGBA;
        internalFormat= gl.LUMINANCE_ALPHA;
        format = gl.LUMINANCE_ALPHA;

    //set out of bounds accesses to clamp and set sub pixel accesses to lerp

    const fb = gl.createFramebuffer();

    //set frame buffer to first attacthment position
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D,textOuput,0);

    var depthBuffer;
        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.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.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);
     // render to the canvas
    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.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();

    //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
    setPostprocessingUniforms(postProcessProgram,sceneAsTexture,normalsAsTexture, gl.viewportWidth, gl.viewportHeight);

    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
      postProcessProgram.vertexPositionAttribute, size, type, normalize, stride, offset);

    gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
      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);


    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);





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 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.

User contributions licensed under: CC BY-SA
6 People found this is helpful