OpenGL рендеринг в текстуру с частичной прозрачностью (прозрачность) и затем рендеринг это на экран

Я нашел несколько мест, где об этом спрашивали, но пока не нашел хорошего ответа.

Проблема: я хочу визуализировать в текстуру, а затем я хочу нарисовать эту визуализированную текстуру на экранеодинаково как бы это выглядело, если бы я пропустил шаг рендеринга к текстуре и просто рендерил прямо на экран. В настоящее время я использую режим смешивания glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). У меня есть glBlendFuncSeparate, чтобы поиграть также.

Я хочу иметь возможность визуализировать частично прозрачные элементы перекрытия с этой текстурой. Я знаю, что функция смешивания в настоящее время портит значения RGB на основе альфы. Я видел некоторые расплывчатые предложения использовать «предварительно умноженную альфу», но описание плохое относительно того, что это значит. Я делаю PNG-файлы в фотошопе, я знаю, что у них есть бит прозрачности, и вы не можете легко редактировать альфа-канал независимо, как вы можете с TGA. При необходимости я могу переключиться на TGA, хотя PNG более удобен.

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

Как только я рендерил свою сцену в текстуру, мне нужно рендерить эту текстуру в другую сцену, и мне снова нужно СЛИВАТЬ текстуру, предполагая частичную прозрачность снова. Здесь вещи разваливаются. В предыдущих шагах наложения я четко изменял значения RGB на основе альфа-канала, повторяя его, все в порядке, если альфа-значение равно 0 или 1, но если оно находится между ними, результатом является дальнейшее затемнение этих частично полупрозрачных пикселей.

Играя с режимами смешивания, мне очень мало повезло. Лучшее, что я могу сделать, это рендерить текстуру с помощью:

glBlendFuncSeparate (GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);

Я обнаружил, что рендеринг несколько раз с помощью glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) будет приближаться к правильному цвету (если вещи не перекрываются). Но это не совсем идеально (как вы можете видеть на следующем изображении, участки, где зеленые / красные / синие прямоугольники перекрываются, становятся темнее или накапливают альфа. (РЕДАКТИРОВАТЬ: если я делаю многократное рисование на рендере на часть экрана и только При рендеринге один раз в текстуру проблема накопления альфа исчезает, и это работает, но почему ?! Я не хочу, чтобы одна и та же текстура сотни раз выводилась на экран, чтобы заставить ее накапливаться правильно)

Вот некоторые изображения, детализирующие проблему (несколько проходов рендеринга с базовым смешиванием (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), и они визуализируются несколько раз на этапе рендеринга текстуры. 3 поля справа отображаются на 100% красным, зеленым или синим цветом (0-255), но при альфа-значениях 50% для синего, 25% для красного и 75% для зеленого:

Итак, разбивка того, что я хочу знать:

Я установил режим смешивания на:X?Я отрисовываю свою сцену в текстуру.(Может быть, мне нужно сделать несколько режимов смешивания или несколько раз?)Я установил режим смешивания на:Y?Я рендерил свою текстуру на экран поверх существующей сцены.(Может быть, мне нужен другой шейдер? Может быть, мне нужно визуализировать текстуру несколько раз?)

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

Я установил режим смешивания на: (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)Я отрисовываю свою сцену на экране.

И, для полноты, вот мой код с моей первоначальной наивной попыткой (просто регулярное смешивание):

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

Вот код, который я использую для настройки сцены:

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

И код для рендеринга сцены:

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

И вот мой скриншот:

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

* РЕДАКТИРОВАТЬ: НАСТРОЙКА РАМНОГО БУФЕРА / КОД TEARDOWN:

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

И мой четкий код экрана:

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

Ответы на вопрос(4)

Ваш ответ на вопрос