多通道渲染以分离纹理

Multiple passes render to separate textures

我正在使用渲染到纹理的方法来创建多着色器程序。并且出于各种原因。我需要先使用一个着色器程序将模型渲染为纹理。然后使用不同的着色器程序再次将其渲染成不同的纹理。然后我有一个最终的 post 处理着色器,它结合了两者的结果。我的问题是第二个纹理似乎覆盖了第一个纹理。有没有办法移动纹理?或者渲染到不同的纹理而不覆盖它。 (提前感谢您的帮助!)

附上我的代码供参考:

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

}

您正在检查 the JavaScript console 中的错误吗?

LUMINANCE_ALPHA 不保证可渲染

能够呈现为 gl.LUMINANCE_ALPHA 的格式不能保证有效。在 WebGL 1 中,只有 gl.RGBA/gl.UNSIGNED_BYTE 可以保证工作。所有其他 format/type 组合都不是。您可以致电

查看
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
const canRender = status === gl.FRAMEBUFFER_COMPLETE;

虽然通常不支持对 LUMINANCE_ALPHA 的渲染,但您可能只想避免它。 MacOS 肯定不支持它。

gl.viewportWidth、gl.viewportHeight不存在

gl.viewportWidthgl.viewportHeight 不是东西,它们不存在。它们不属于任何规范。第一批 WebGL 教程之一(不再在网上)编造了这些内容并使成千上万的开发人员感到困惑。

代码从不打开属性缓冲区

您需要为您创建的每个属性调用 gl.enableVertexAttribArray 想从缓冲区中获取它的值。或者换句话说,每次调用 gl.vertexAttribPointer 您还需要调用 gl.enableVertexAttribArray

采样器统一设置为纹理单位而不是纹理

这个代码

 gl.uniform1i(prog.uTextureUniform, texture);

没有意义。纹理绑定到一组纹理单元。您 select 使用 gl.activeTexture 的单位,然后使用 gl.bindTexture 将纹理绑定到该单位。然后,您告诉着色器您使用 gl.uniform1i 为纹理分配了哪个单元。参见 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);

其他评论

  • 解除绑定纹理几乎不重要

  • 在渲染时创建和删除资源并不常见

    创建和删除纹理和帧缓冲区很慢。会更常见 在初始化时创建它们并在渲染时使用它们。

  • 以后尝试post一个可运行的snippet

  • 不确定您使用的是什么数学库,但它看起来有点像 gl-matrix?许多对 mat4.xxx 的调用与 gl-matrix 的当前 API 不匹配,但如果没有矩阵库的来源,很难判断这些调用是否正确。