Opengl Rendern auf Textur mit partieller Transparenz (Transluzenz) und anschließendes Rendern auf dem Bildschirm

Ich habe einige Stellen gefunden, an denen dies gefragt wurde, aber ich habe noch keine gute Antwort gefunden.

Das Problem: Ich möchte auf Textur rendern und dann diese gerenderte Textur auf den Bildschirm ziehenIDENTISCH Wie würde es aussehen, wenn ich den Schritt "Rendern auf Textur" überspringe und nur direkt auf dem Bildschirm rendere? Ich verwende derzeit einen Mischmodus glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). Ich habe auch glBlendFuncSeparate zum Herumspielen.

Ich möchte in der Lage sein, teilweise transparente überlappende Elemente für diese Textur zu rendern. Ich weiß, dass die Mischfunktion derzeit die auf dem Alpha basierenden RGB-Werte durcheinander bringt. Ich habe einige vage Vorschläge zur Verwendung von "vormultipliziertem Alpha" gesehen, aber die Beschreibung, was das bedeutet, ist unzureichend. Ich erstelle PNG-Dateien in Photoshop. Ich weiß, dass sie ein transluzentes Bit haben und dass Sie den Alphakanal nicht so einfach unabhängig bearbeiten können wie mit TGA. Bei Bedarf kann ich auf TGA umsteigen, obwohl PNG praktischer ist.

Um diese Frage zu beantworten, nehmen wir an, dass wir keine Bilder verwenden. Stattdessen verwende ich nur Vollfarben-Quads mit Alpha.

Sobald ich meine Szene auf die Textur gerendert habe, muss ich diese Textur auf eine andere Szene rendern und die Textur unter der Annahme einer teilweisen Transparenz wieder mischen. Hier fallen die Dinge auseinander. In den vorherigen Mischschritten ändere ich die RGB-Werte deutlich basierend auf Alpha. Wenn Alpha 0 oder 1 ist, funktioniert dies erneut. Wenn es jedoch dazwischen liegt, führt dies zu einer weiteren Abdunkelung dieser teilweise durchscheinenden Pixel.

Beim Spielen mit Mischmodi hatte ich sehr wenig Glück. Das Beste, was ich tun kann, ist das Rendern von Texturen mit:

glBlendFuncSeparate (GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);

Ich habe festgestellt, dass das mehrfache Rendern mit glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) die richtige Farbe annähert (sofern sich die Dinge nicht überschneiden). Aber das ist nicht gerade perfekt (wie Sie in der folgenden Abbildung sehen können, werden die Bereiche, in denen sich die grün / rot / blauen Felder überlappen, dunkler oder akkumulieren Alpha.) Wenn Sie die Textur einmal rendern, verschwindet das Alpha-Akkumulationsproblem und es funktioniert, aber warum? Ich möchte nicht die gleiche Textur hunderte Male auf dem Bildschirm rendern müssen, damit sie richtig akkumuliert.

Im Folgenden sind einige Bilder aufgeführt, in denen das Problem detailliert beschrieben wird (die mehreren Render-Durchgänge werden mit einer einfachen Überblendung (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ausgeführt. Die 3 Kästchen auf der rechten Seite werden zu 100% rot, grün oder blau gerendert (0-255) aber bei Alpha-Werten von 50% für Blau, 25% für Rot und 75% für Grün:

Also, eine Aufschlüsselung dessen, was ich wissen möchte:

Ich stelle den Mischmodus auf:X?Ich rendere meine Szene zu einer Textur.(Vielleicht muss ich mit ein paar Mischmodi oder mehrmals rendern?)Ich stelle meinen Mischmodus auf:Y?Ich rendere meine Textur auf dem Bildschirm über einer vorhandenen Szene.(Vielleicht brauche ich einen anderen Shader? Vielleicht muss ich die Textur ein paar Mal rendern?)

Gewünschtes Verhalten ist, dass am Ende dieses Schritts das endgültige Pixelergebnis identisch ist mit dem, was ich nur tun würde:

Ich setze meinen Mischmodus auf: (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)Ich rendere meine Szene auf dem Bildschirm.

Und der Vollständigkeit halber hier ein Teil meines Codes mit meinem ursprünglichen naiven Versuch (nur reguläres Mischen):

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

Hier ist der Code, mit dem ich die Szene einrichte:

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

Und der Code zum Rendern der Szene:

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

Und hier ist meine Bildschirmzeichnung:

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

* EDIT: FRAMEBUFFER SETUP / TEARDOWN CODE:

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

Und mein klarer Bildschirmcode:

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

Antworten auf die Frage(4)

Ihre Antwort auf die Frage