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