2

我需要将球体渲染到纹理(使用帧缓冲区对象(FBO)完成),然后将该纹理与后台缓冲区进行 alpha 混合。到目前为止,除了在每一帧开始时清除它之外,我还没有对纹理进行任何处理。

我应该说我的场景只包含一个空旷空间中的行星,球体应该出现在行星旁边或周围(现在有点像月亮)。当我将球体直接渲染到后台缓冲区时,它会正确显示;但是当我执行将其渲染为纹理然后将该纹理与后台缓冲区混合的中间步骤时,球体仅在它位于行星前面时才会显示,不在前面的部分只是“切断” “:

被行星边缘切断的球体

我使用glutSolidSphere绑定到 FBO 的 RGBA8 全屏纹理渲染球体,确保每个球体像素接收 1.0 的 alpha 值。然后,我将纹理传递给片段着色器程序,并使用此代码在 alpha 混合时将全屏四边形(纹理映射到其上)渲染到后缓冲区:

glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);

glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glBegin(GL_QUADS);
glTexCoord2i(0, 1);
glVertex3i(-1,  1, -1);   // TOP LEFT
glTexCoord2i(0, 0);
glVertex3i(-1, -1, -1);   // BOTTOM LEFT
glTexCoord2i(1, 0);   
glVertex3i( 1, -1, -1);   // BOTTOM RIGHT
glTexCoord2i(1, 1);   
glVertex3i( 1,  1, -1);   // TOP RIGHT
glEnd();
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glEnable(GL_DEPTH_TEST);

glDisable(GL_BLEND);

这是着色器代码(取自用 Cg 编写的 FX 文件):

sampler2D BlitSamp = sampler_state
{
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
    AddressU = Clamp;
    AddressV = Clamp;
};

float4 blendPS(float2 texcoords : TEXCOORD0) : COLOR
{
    float4 outColor = tex2D(BlitSamp, texcoords);
    return outColor;
}

我什至不知道这是深度缓冲区的问题还是 alpha 混合的问题,我尝试了很多启用和禁用深度测试(将深度缓冲区附加到 FBO)和 alpha 混合的组合。

编辑:我尝试将一个空白的全屏四边形直接渲染到后台缓冲区,甚至在地球边缘裁剪。出于某种原因,启用渲染四边形的深度测试(即删除线glDisable(GL_DEPTH_TEST)glEnable(GL_DEPTH_TEST) in the code above)解决了这个问题,但是现在除了行星和球体之外的所有东西都显示为白色:

白色不正确的白色背景的球体

我确保(并且可以确认)纹理的 alpha 通道在每个像素上都是 0,但球体除外,​​所以我不明白可以在哪里引入白度。(也仍然有兴趣解释为什么启用深度测试会产生这种效果。)

4

2 回答 2

1

我在这里看到两个可能的错误来源:

1. 渲染到 FBO

如果丢失的像素在渲染后甚至不存在于 FBO 中,则必须有某种机制丢弃相应的片段。OpenGL 管道包括四种不同类型的片段测试,这些测试可能导致片段被丢弃:

  1. 剪刀测试:不太可能是原因,因为剪刀测试只影响屏幕的矩形部分。
  2. Alpha 测试:同样不可能,因为您的片段都应该具有相同的 alpha 值。
  3. 模板测试:也不太可能,除非您在绘制背景行星时使用模板操作并将模板缓冲区从后台缓冲区复制到 FBO。
  4. 深度测试:与模板测试相同。

所以很有可能渲染到 FBO 不是这里的问题。但为了绝对确定,您应该读回您的颜色附件纹理并将其转储到文件中以供检查。您可以为此使用以下功能:

void TextureToFile(GLuint texture, const char* filename) {
  glBindTexture(GL_TEXTURE_2D, texture);
  GLint width, height;
  glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
  glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);

  std::vector<GLubyte> pixels(3 * width * height);
  glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixels[0]);

  std::ofstream out(filename, std::ios::out | std::ios::binary);
  out << "P6\n"
      << width << '\n'
      << height << '\n'
      << 255 << '\n';
  out.write(reinterpret_cast<const char*>(&pixels[0]), pixels.size());  
}

生成的文件是一个可移植的像素图(.ppm)。在读回纹理之前,请务必取消绑定 FBO。

2.纹理映射

假设渲染到 FBO 中按预期工作,唯一的其他错误来源是在先前渲染的场景上混合纹理。有两种情况:

a) 碎片被丢弃

碎片被丢弃的可能原因与1中相同:

  1. 剪刀测试:不,仅影响矩形区域。
  2. Alpha 测试:可能不是,被纹素覆盖的球体应该都具有相同的 alpha 值。
  3. 模板测试:如果您在绘制背景行星时使用模板操作/模板测试并且旧的模板状态仍处于活动状态,则可能是原因。
  4. 深度测试:可能是原因,但由于您已经禁用它,它确实不应该有任何影响。

所以你应该确保所有这些测试都被禁用,尤其是模板测试。

b) 混合的错误结果

假设所有片段都到达后台缓冲区,混合是唯一仍然可能导致错误结果的事情。使用您的混合功能(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA),后台缓冲区中的值与混合无关,我们假设纹理中的 alpha 值是正确的。所以我看不出为什么混合应该是这里的根本原因。

结论

总之,观察结果的唯一合理原因似乎是模板测试。如果不是,我没有选择:)

于 2012-09-29T16:14:58.807 回答
0

我解决了它,或者至少想出了一个解决方法。

首先,白色源于glClearColor已设置为的事实glClearColor(1.0f, 1.0f, 1.0f, 1000.0f),因此除了行星之外的所有内容最终都没有被写入。我现在在渲染球体之前将后台缓冲区的内容(即行星、大气及其周围的空间)复制到纹理中,并在复制/blit 操作之前渲染大气和空间,因此它们被包括在内在里面。以前,除了行星本身之外的所有东西都是在我的四边形之后渲染的,当使用深度测试时,它显然将所有东西放在了四边形后面,使其不可见。

我试图实现的效果的参考实现在其代码中一直使用这种 blit 操作,但我认为效果没有必要。现在我觉得可能没有别的办法了...

于 2012-10-01T14:31:24.580 回答