GLSL片段着色器渲染对象ID

6

如何将对象的整数ID正确地渲染到整数纹理缓冲区中?

假设我有一个内部格式为GL_LUMINANCE16的texture2D,并将其作为颜色附件附加到我的FBO上。

在渲染对象时,我将一个整数ID传递给着色器,并希望将此ID渲染到我的整数纹理中。

然而,fragmentshader输出的类型是vec4。 我该如何正确地将我的ID转换为四个组件的浮点数,并避免转换不准确,以便最终整数纹理目标中的整数值对应于我想要渲染的整数ID?

3个回答

5
我认为这里并没有明确的答案。以下是我如何通过2D纹理使其工作的方法:
// First, create a frame buffer:
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);

// Then generate your texture and define an unsigned int array:
glGenTextures(1, &textureid);
glBindTexture(GL_TEXTURE_2D, textureid);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, w, h, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

// Attach it to the frame buffer object:
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, textureid, 0);

// Before rendering    
glBindFramebuffer(GL_FRAMEBUFFER, fbo);    
GLuint buffers[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; // this assumes that there is another     texture that is for the render buffer. Color attachment1 is preserved for the element ids.
glDrawBuffers(2, buffers);

// Clear and render here
glFlush(); // Flush after just in case
glBindFramebuffer(GL_FRAMEBUFFER, 0);

对于GLSL方面,片段着色器应该具有以下(4.3核心配置代码):

layout(location = 0) out vec4 colorOut; // The first element in 'buffers' so location 0    
layout(location = 1) out uvec4 elementID; // The second element in 'buffers' so location 1. unsigned int vector as color

// ...
void main()
{
//...

elementID = uvec4( elementid, 0, 0, 0 ); // Write the element id as integer to the red channel.

}

您可以在主机端读取这些值:

unsigned int* ids = new unsigned int[ w*h ];
glBindTexture(GL_TEXTURE_2D, textureid);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, ids);

1
我认为被接受的答案有点模糊。经过无数个小时,这实际上挽救了我的一天!谢谢您,先生。 - Cengiz Can

4

你的问题存在几个问题。

首先,GL_LUMINANCE16 不是一个“整数纹理”。它是一个包含归一化无符号整数值的纹理。它使用整数来表示范围为 [0, 1] 的浮点数。如果你想存储实际的整数,你必须使用实际的 整数图像格式

其次,你不能将光度纹理渲染出来;它们不是可渲染颜色格式。如果你真的想要渲染到单通道纹理中,你必须创建一个单通道图像格式。所以你需要使用 GL_R16UI,它是一个 16 位单通道无符号整数图像格式,而不是 GL_LUMINANCE16

现在你已经正确设置了这个,它就变得非常简单了。定义一个 uint 片段着色器输出,并让你的片段着色器将你的 uint 值写入其中。这个 uint 可以来自于顶点着色器或者一个 uniform,无论你想如何做都可以。

显然,您还需要将纹理或渲染缓冲区附加到FBO上,但我相信您已经知道了。

最后一件事情:除非您指的是其中之一,否则不要使用“纹理缓冲区”这个短语,否则会让人感到困惑。


听起来不错 - 不过我已经通过将我的...除以GL_LUMINANCE16使其工作了,出于某些原因我可以渲染它 - 也许只是我的显卡支持它?如果我使用整数纹理格式,如何为仅一个渲染目标定义int输出?我在我的着色器中使用了5个颜色附件,前4个将其他东西渲染到浮点纹理中,第5个将渲染objectId。现在它可以工作(使用GL_LUMINANCE16),但也许我只是在使用未定义的行为?我有一个OpenGL 4.2上下文。 - Mat
@Mat:你必须声明输出为“int”才能写入“int”输出。另外,考虑到这个可以工作,我猜测你有一张NVIDIA显卡。 - Nicol Bolas
是的,我有一张NVIDIA显卡。但是如何将单个输出组件声明为int类型?我的片段着色器中的out变量已经声明为out vec4 out_colors[5] - Mat
@Mat:为什么你这样做呢?你可以为每个输出指定一个名称,这样你的着色器就能自我记录了。然后你可以使用“layout(location=#)”语法将它们与绘制缓冲索引关联起来。 - Nicol Bolas

0

我猜你的整数ID是一组基元的标识符;实际上它可以被定义为着色器统一变量:

uniform int vertedObjectId;

渲染完成后,您希望将片段处理存储到一个整数纹理中。请注意,整数纹理应使用整数采样器(isampler2D)进行采样,该采样器返回整数向量(即ivec3)。

此纹理可以附加到帧缓冲对象上(请注意帧缓冲完整性)。帧缓冲附件可以绑定到整数输出变量:

out int fragmentObjectId;

void main() {
    fragmentObjectId = vertedObjectId;
}

你需要一些扩展支持,或者一个高级的OpenGL版本(3.2?)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接