在OpenGL中创建和混合动态纹理

时间:2012-09-28 16:04:32

标签: c++ opengl shader alphablending render-to-texture

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

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

Sphere that is cut off by the edge of the planet

我使用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)摆脱了问题,但现在除了地球和球体看起来是白色的:

Sphere with white incorrect white background

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

2 个答案:

答案 0 :(得分:1)

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

1。渲染到FBO

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

  1. 剪刀测试:不太可能成为原因,因为剪刀测试只会影响屏幕的矩形部分。
  2. Alpha测试:同样不太可能,因为您的片段应该都具有相同的alpha值。
  3. 模板测试:也不太可能,除非您在绘制背景行星时使用模板操作并将模板缓冲区从后缓冲区复制到FBO。
  4. 深度测试:与模板测试相同。
  5. 所以很有可能在这里渲染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());  
    }
    

    生成的文件是portable pixmap(。ppm)。在回读纹理之前一定要取消绑定FBO。

    2。纹理映射

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

    a)碎片被丢弃

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

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

      b)混合错误的结果

      假设所有碎片都到达后缓冲区,混合是唯一仍然可能导致错误结果的东西。使用混合函数(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)后缓冲区中的值与混合无关,我们假设纹理中的alpha值是正确的。所以我认为没有理由说为什么混合应该是这里的根本原因。

      结论

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

答案 1 :(得分:0)

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

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

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