【发布时间】:2014-08-12 08:26:31
【问题描述】:
我找到了几个地方问过这个问题,但我还没有找到一个好的答案。
问题:我想渲染到纹理,然后我想将渲染的纹理绘制到屏幕上完全相同如果我跳过渲染到纹理步骤并且直接渲染到屏幕上。我目前正在使用混合模式 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)。我也有 glBlendFuncSeparate 可以玩。
我希望能够将部分透明的重叠项目渲染到此纹理。我知道混合功能目前正在弄乱基于 Alpha 的 RGB 值。我已经看到了一些使用“预乘 alpha”的模糊建议,但关于这意味着什么的描述很差。我在 Photoshop 中制作 png 文件,我知道它们具有半透明位,并且您无法像使用 TGA 那样轻松地独立编辑 Alpha 通道。如果需要,我可以切换到 TGA,虽然 PNG 更方便。
现在,为了这个问题,假设我们不使用图像,而我只是使用带 alpha 的全彩色四边形。
一旦我将我的场景渲染到纹理,我需要将该纹理渲染到另一个场景,并且我需要再次混合纹理假设部分透明。这就是事情分崩离析的地方。在前面的混合步骤中,我清楚地根据 Alpha 更改了 RGB 值,如果 Alpha 为 0 或 1,则再次执行此操作可以正常工作,但如果介于两者之间,则结果是那些部分半透明的像素进一步变暗。
玩混合模式我运气不佳。我能做的最好的就是渲染到纹理:
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
我确实发现使用 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 多次渲染将接近正确的颜色(除非重叠)。但这并不完全完美(如下图所示,绿色/红色/蓝色框重叠的部分变得更暗,或积累了 alpha。(编辑:如果我在渲染到屏幕部分进行多次绘制,并且仅渲染一次纹理,alpha 累积问题就消失了,它确实有效,但是为什么?!我不想在屏幕上渲染数百次相同的纹理才能使其正确累积)
这里是一些详细说明该问题的图像(多个渲染通道使用基本混合(GL_SRC_ALPHA、GL_ONE_MINUS_SRC_ALPHA),它们在纹理渲染步骤中被多次渲染。右侧的 3 个框被渲染为 100% 红色、绿色,或蓝色 (0-255),但蓝色的 alpha 值为 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();
*编辑:帧缓冲设置/拆卸代码:
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);
}
【问题讨论】:
-
我认为您的问题是,您很难理解您在说什么,因为您不完全了解混合的工作原理。如果您在其他部分透明的东西之上渲染部分透明的东西,那么您的显卡绘制的最终颜色将不会与您渲染的任何东西具有相同的 alpha,除非您使用 1 或 0 的 alpha 值。解决这个问题的一种方法(我认为,还没有测试过)是使对象的 alpha 颜色成为渲染次数的函数
-
对,我不知道怎么实现我想要的,因为我对混合函数的理解不完全。它们对我来说不是很直观。这方面的援助正是这个问题的意义所在。 :)
-
我也可以影响我用来渲染这个问题的任何一部分的着色器。 IE:我可以更改在将纹理渲染到屏幕时使用的着色器。
-
一件事可能是您没有在每次渲染纹理时都清除它。每次渲染到纹理时使用 glClear(GL_COLOR_BUFFER_BIT) 或用于清除屏幕的任何函数清除它。我相信这将在您的 refreshTexture 方法中
-
@M2tM 不,不用担心。下次(:
标签: c++ opengl blending render-to-texture glblendfunc