【问题标题】:Multiple passes render to separate textures多次渲染以分离纹理
【发布时间】:2021-03-26 13:17:15
【问题描述】:

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

我已附上我的代码供参考:

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

}

【问题讨论】:

    标签: javascript webgl textures render-to-texture


    【解决方案1】:

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

    LUMINANCE_ALPHA 不保证可渲染

    能够呈现到gl.LUMINANCE_ALPHA 并不是一种保证可以工作的格式。在 WebGL 1 中,只有 gl.RGBA/gl.UNSIGNED_BYTE 保证可以工作。所有其他格式/类型组合都不是。您可以拨打电话查看

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

    没有意义。纹理绑定到一组纹理单元。您使用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);
    

    其他cmets

    • 解绑纹理几乎从来都不重要

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

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

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

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

    【讨论】:

    • 感谢您抽出宝贵的时间来响应并深入了解我的初学者代码。我确实手动定义了 gl.viewportWidth 和 gl.viewportHeight,所以这不是问题。我会尝试您的建议,如果可行,我会将您的回复标记为答案。
    • 您不应手动定义gl.viewportWidthgl.viewportHeight。他们只是令人困惑。如果您想要画布的大小,请使用gl.canvas.widthgl.canvas.heightgl.drawingbufferWidthgl.drawingbufferHeight。那些是定义的。没有理由为自己做更多的工作并继续混乱。我不在乎你是否标记我的答案。我更关心你问一个更好的问题。事实是我应该投票结束,因为您没有提供足够的代码来重现该问题,因此您的问题是题外话。虽然有明显的问题所以我回答了
    • 是的,我只是没有完全理解纹理单元和gl.uniform1i(someSamplerLocation, unit); 所以,谢谢你的帮助。像你这样的人帮助我更好地理解事物!再次感谢您!
    • 您可能会发现this 有助于查找错误
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-27
    相关资源
    最近更新 更多