Как передать трафарет из кадрового буфера в другой

Я изо всех сил пытаюсь понять, почему в приведенном ниже коде трафаретные тесты работают так, как ожидалось, когда я рендерился прямо на экран, и нет, когда я пытался использовать кадровые буферы. У меня нет проблем с использованием этих кадровых буферов для многопроходной постобработки текстур, и поэтому я подозреваю, что что-то не так с моими буферами глубины и трафарета.

Сначала я инициализирую контекст:

        gl = canvas.getContext("webgl",{stencil:true});

затем

function initFBO()
{

for (var i = 0; i < shaderPrograms.length; i++) 
{

    var texture = createTexture(imgWidth,imgHeight);
    textures.push(texture);

    var fbo = gl.createFramebuffer();
    framebuffers.push(fbo);

    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);

    var renderbuffer = gl.createRenderbuffer();

    gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, imgWidth, imgHeight);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);

}
}


function drawScene() 
{
gl.enable(gl.STENCIL_TEST);
gl.enable(gl.DEPTH_TEST);

if(!useFBO)
{
    computeProgFBO(9,null,imgHeight,imgHeight,textureId,null,null,false);
    computeProgFBO(8,null,imgHeight,imgHeight,textureId,null,null,false);
}else{
    computeProgFBO(9,framebuffers[0],imgHeight,imgHeight,textureId,null,null,false);
    computeProgFBO(8,null,imgHeight,imgHeight,textures[0],null,null,false);
}

// Restore the original matrix
mvPopMatrix();

gl.disable(gl.STENCIL_TEST);
gl.disable(gl.DEPTH_TEST);

}

function computeProgFBO(progId,fboId,w,h,textureId0,textureId1,textureId2,flip)
{

    currentProgram=shaderPrograms[progId];
    gl.useProgram(currentProgram);

    if(progId==9)
    {
        transform(w,h,0.0);
        gl.stencilMask(1);
        gl.stencilFunc(gl.ALWAYS,1,0);
        gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE);
        gl.colorMask(true, true, true, true);
        gl.depthMask(false);
        setMatrixUniformsView();
    }else{
        gl.colorMask(true, true, true, true);
        gl.depthMask(true);
        gl.stencilFunc(gl.EQUAL,0,1);
        gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
        setMatrixUniforms();
    }

    setMyUniforms(progId,flip);
    setMyTexture(textureId0, textureId1,textureId2);

    gl.activeTexture(gl.TEXTURE1);
    gl.bindFramebuffer(gl.FRAMEBUFFER, fboId);
//the below is commented out for now as i'm only testing the stencil buffer for now        
//  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[progId], 0);




    gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);

    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    gl.bindTexture(gl.TEXTURE_2D , null);
}

Я не думаю, что есть смысл показывать различные формы и функции текстур, но, пожалуйста, дайте мне знать иначе. Кстати, progId 9 рисует маленький зеленый квадрат, а progId 8 - больший красный квадрат поверх него. Итак, я ожидаю увидеть зеленый квадрат, окруженный какими-то красными краями, как он сейчас показывает, когда я рендерим прямо на экран.

**** ДАЛЕЕ к 1-му ответу я изменил ниже *****

if(!useFBO)
{
    computeProgFBO(9,null,imgHeight,imgHeight,textureId,null,null,false,null);
    computeProgFBO(8,null,imgHeight,imgHeight,textureId,null,null,false,null);
    computeProgFBO(6,null,imgHeight,imgHeight,textureId,null,null,false,null);
}else{
    computeProgFBO(9,framebuffers[0],imgHeight,imgHeight,textureId,null,null,false,null);
    computeProgFBO(8,framebuffers[1],imgHeight,imgHeight,textures[0],null,null,false,renderbuffers[0]);
    computeProgFBO(6,null,imgHeight,imgHeight,textures[1],null,null,false,null);
}

и я изменил аргументы функции на

computeProgFBO(progId,fboId,w,h,textureId0,textureId1,textureId2,flip,render)

и там после комментария ниже

//the below is commented out for now as i'm only testing the stencil buffer for now        
//  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[progId], 0);

я добавил

if(render!=null)
    {
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, render);
    }

Результат немного лучше, но не совсем то, что я ожидал, то есть: я получаю большой красный квадрат и вместо того, чтобы иметь маленький зеленый квадрат внутри, я получаю белый квадрат. Я чувствую, что это что-то тривиальное, но пока не могу разобраться. Рендеринг прямо на экран по-прежнему работает нормально.

***** ДАЛЕЕ к комментариям от других я добавил ******

я думаю, что понимаю проблему и нашел обходной путь для нее [не очень элегантно, хотя]. По сути, первый проход записывает с использованием трафарета в текстуры [0] кадрового буфера [0], а второй проход записывает в текстуры [1] кадрового буфера [1], используя renderbuffers[0] кадрового буфера [0], но не текстуры [0]. ]. Следовательно, я получаю небольшой белый [вместо зеленого] квадрат, окруженный красным. С тех пор, как я не могу сыграть, я понял, что могу просто рисовать дважды в первом проходе, как показано ниже:

....
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);

if(progId==9) 
    {
        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[1]);
        gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
    }

Это работает, но выглядит очень некрасиво! Есть ли другой путь?

Когда дело доходит до визуализации окончательной текстуры на экране, мне кажется, что мне не нужно много делать, то есть: я отправляю только буфер цвета на экран (а не буфер трафарета и глубины), и вся окончательная текстура отображается как ожидается.

Кроме того, обратите внимание, что мои фрагментные шейдеры только делают ниже

shader9: gl_FragColor = vec4(0.0,1.0,0.0,1.0);

shader8: gl_FragColor = vec4(1.0,vec2(0.0),1.0);

shader6: gl_FragColor = texture2D(u_Texture0,vTextureCoord);

1 ответ

Вы не можете скопировать содержимое буферов в webgl, но вы можете разделить буфер глубины трафарета между многими FBOS, вызвав gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer); на том же рендер-буфере.

Если вы хотите выполнить рендеринг в кадровый буфер по умолчанию с включенным тестом трафарета, вы должны сначала выполнить рендеринг в FBO с желаемым вложением буфера рендеринга трафарета. Затем просто нарисуйте кадровый буфер по умолчанию, используя цветовую текстуру этого FBO.

Другие вопросы по тегам