Opengl renderiza a textura con transparencia parcial (translucidez) y luego renderiza eso en la pantalla

He encontrado algunos lugares donde se ha preguntado esto, pero aún no he encontrado una buena respuesta.

El problema: quiero renderizar a textura, y luego quiero dibujar esa textura renderizada en la pantallaIDÉNTICAMENTE a cómo aparecería si me saltara el paso de renderizar a textura y solo estuviera renderizando directamente a la pantalla. Actualmente estoy usando un modo de fusión glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). También tengo glBlendFuncSeparate para jugar.

Quiero poder representar elementos superpuestos parcialmente transparentes a esta textura. Sé que la función de mezcla actualmente está arruinando los valores RGB basados en Alpha. He visto algunas sugerencias vagas para usar "alfa premultiplicado", pero la descripción es pobre en cuanto a lo que eso significa. Realizo archivos png en Photoshop, sé que tienen un poco de translucidez y no puedes editar fácilmente el canal alfa de forma independiente como puedes hacerlo con TGA. Si es necesario, puedo cambiar a TGA, aunque PNG es más conveniente.

Por ahora, en aras de esta pregunta, suponga que no estamos usando imágenes, en cambio solo estoy usando quads a todo color con alfa.

Una vez que renderizo mi escena a la textura, necesito renderizar esa textura a otra escena, y necesito MEZCLAR la textura asumiendo una transparencia parcial nuevamente. Aquí es donde las cosas se desmoronan. En los pasos de mezcla anteriores, alteré claramente los valores RGB basados en Alpha, hacerlo de nuevo funciona bien, si Alpha es 0 o 1, pero si está en el medio, el resultado es un oscurecimiento adicional de esos píxeles parcialmente translúcidos.

Jugando con modos de mezcla he tenido muy poca suerte. Lo mejor que puedo hacer es renderizar a textura con:

glBlendFuncSeparate (GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);

Descubrí que renderizar varias veces con glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) se aproximará al color correcto (a menos que las cosas se superpongan). Pero eso no es exactamente perfecto (como puede ver en la siguiente imagen, las partes donde se superponen los cuadros verde / rojo / azul se oscurecen o acumulan alfa. (EDITAR: si hago los múltiples dibujos en la parte de renderizado a pantalla y solo renderizar una vez a la textura, el problema de acumulación alfa desaparece y funciona, pero ¿por qué? No quiero tener que renderizar la misma textura cientos de veces en la pantalla para que se acumule correctamente)

Aquí hay algunas imágenes que detallan el problema (los pases de renderizado múltiple tienen una combinación básica (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), y se representan varias veces en el paso de renderizado de textura. Los 3 cuadros de la derecha se muestran 100% rojo, verde o azul (0-255) pero con valores alfa del 50% para azul, 25% para rojo y 75% para verde:

Entonces, un desglose de lo que quiero saber:

Configuré el modo de mezcla en:X?Renderizo mi escena a una textura.(¿Tal vez tengo que renderizar con algunos modos de mezcla o varias veces?)Configuré mi modo de fusión para:Y?Represento mi textura en la pantalla sobre una escena existente.(¿Quizás necesito un sombreador diferente? ¿Quizás necesito renderizar la textura varias veces?)

El comportamiento deseado es que al final de ese paso, el resultado final del píxel es idéntico a si solo hiciera esto:

Configuré mi modo de fusión en: (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)Renderizo mi escena a la pantalla.

Y, para completar, aquí hay algo de mi código con mi intento ingenuo original (solo una mezcla regular):

    //RENDER TO TEXTURE.
    void Clipped::refreshTexture(bool a_forceRefresh) {
        if(a_forceRefresh || dirtyTexture){
            auto pointAABB = basicAABB();
            auto textureSize = castSize<int>(pointAABB.size());
            clippedTexture = DynamicTextureDefinition::make("", textureSize, {0.0f, 0.0f, 0.0f, 0.0f});
            dirtyTexture = false;
            texture(clippedTexture->makeHandle(Point<int>(), textureSize));
            framebuffer = renderer->makeFramebuffer(castPoint<int>(pointAABB.minPoint), textureSize, clippedTexture->textureId());
            {
                renderer->setBlendFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                SCOPE_EXIT{renderer->defaultBlendFunction(); };

                renderer->modelviewMatrix().push();
                SCOPE_EXIT{renderer->modelviewMatrix().pop(); };
                renderer->modelviewMatrix().top().makeIdentity();

                framebuffer->start();
                SCOPE_EXIT{framebuffer->stop(); };

                const size_t renderPasses = 1; //Not sure?
                if(drawSorted){
                    for(size_t i = 0; i < renderPasses; ++i){
                        sortedRender();
                    }
                } else{
                    for(size_t i = 0; i < renderPasses; ++i){
                        unsortedRender();
                    }
                }
            }
            alertParent(VisualChange::make(shared_from_this()));
        }
    }

Aquí está el código que estoy usando para configurar la escena:

    bool Clipped::preDraw() {
        refreshTexture();

        pushMatrix();
        SCOPE_EXIT{popMatrix(); };

        renderer->setBlendFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        SCOPE_EXIT{renderer->defaultBlendFunction();};
        defaultDraw(GL_TRIANGLE_FAN);

        return false; //returning false blocks the default rendering steps for this node.
    }

Y el código para renderizar la escena:

test = MV::Scene::Rectangle::make(&renderer, MV::BoxAABB({0.0f, 0.0f}, {100.0f, 110.0f}), false);
test->texture(MV::FileTextureDefinition::make("Assets/Images/dogfox.png")->makeHandle());

box = std::shared_ptr<MV::TextBox>(new MV::TextBox(&textLibrary, MV::size(110.0f, 106.0f)));
box->setText(UTF_CHAR_STR("ABCDE FGHIJKLM NOPQRS TUVWXYZ"));
box->scene()->make<MV::Scene::Rectangle>(MV::size(65.0f, 36.0f))->color({0, 0, 1, .5})->position({80.0f, 10.0f})->setSortDepth(100);
box->scene()->make<MV::Scene::Rectangle>(MV::size(65.0f, 36.0f))->color({1, 0, 0, .25})->position({80.0f, 40.0f})->setSortDepth(101);
box->scene()->make<MV::Scene::Rectangle>(MV::size(65.0f, 36.0f))->color({0, 1, 0, .75})->position({80.0f, 70.0f})->setSortDepth(102);
test->make<MV::Scene::Rectangle>(MV::size(65.0f, 36.0f))->color({.0, 0, 1, .5})->position({110.0f, 10.0f})->setSortDepth(100);
test->make<MV::Scene::Rectangle>(MV::size(65.0f, 36.0f))->color({1, 0, 0, .25})->position({110.0f, 40.0f})->setSortDepth(101);
test->make<MV::Scene::Rectangle>(MV::size(65.0f, 36.0f))->color({.0, 1, 0, .75})->position({110.0f, 70.0f})->setSortDepth(102);

Y aquí está mi dibujo de pantalla:

renderer.clearScreen();
test->draw(); //this is drawn directly to the screen.
box->scene()->draw(); //everything in here is in a clipped node with a render texture.
renderer.updateScreen();

* EDITAR: CONFIGURACIÓN DEL MARCO / CÓDIGO DE DESPLAZAMIENTO:

void glExtensionFramebufferObject::startUsingFramebuffer(std::shared_ptr<Framebuffer> a_framebuffer, bool a_push){
    savedClearColor = renderer->backgroundColor();
    renderer->backgroundColor({0.0, 0.0, 0.0, 0.0});

    require(initialized, ResourceException("StartUsingFramebuffer failed because the extension could not be loaded"));
    if(a_push){
        activeFramebuffers.push_back(a_framebuffer);
    }

    glBindFramebuffer(GL_FRAMEBUFFER, a_framebuffer->framebuffer);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, a_framebuffer->texture, 0);
    glBindRenderbuffer(GL_RENDERBUFFER, a_framebuffer->renderbuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, roundUpPowerOfTwo(a_framebuffer->frameSize.width), roundUpPowerOfTwo(a_framebuffer->frameSize.height));

    glViewport(a_framebuffer->framePosition.x, a_framebuffer->framePosition.y, a_framebuffer->frameSize.width, a_framebuffer->frameSize.height);
    renderer->projectionMatrix().push().makeOrtho(0, static_cast<MatrixValue>(a_framebuffer->frameSize.width), 0, static_cast<MatrixValue>(a_framebuffer->frameSize.height), -128.0f, 128.0f);

    GLenum buffers[] = {GL_COLOR_ATTACHMENT0};
    //pglDrawBuffersEXT(1, buffers);


    renderer->clearScreen();
}

void glExtensionFramebufferObject::stopUsingFramebuffer(){
    require(initialized, ResourceException("StopUsingFramebuffer failed because the extension could not be loaded"));
    activeFramebuffers.pop_back();
    if(!activeFramebuffers.empty()){
        startUsingFramebuffer(activeFramebuffers.back(), false);
    } else {
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glBindRenderbuffer(GL_RENDERBUFFER, 0);

        glViewport(0, 0, renderer->window().width(), renderer->window().height());
        renderer->projectionMatrix().pop();
        renderer->backgroundColor(savedClearColor);
    }
}

Y mi código de pantalla clara:

void Draw2D::clearScreen(){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}

Respuestas a la pregunta(4)

Su respuesta a la pregunta