glsl fragmentshader 渲染对象ID

Posted

技术标签:

【中文标题】glsl fragmentshader 渲染对象ID【英文标题】:glsl fragmentshader render objectID 【发布时间】:2012-05-12 10:56:55 【问题描述】:

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

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

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

但是,fragmentshader 输出的类型是 vec4。 如何正确地将我的 ID 转换为四分量浮点数并避免转换不准确,从而最终我的整数纹理目标中的整数值对应于我要渲染的整数 ID?

【问题讨论】:

【参考方案1】:

我仍然认为这里没有明确的答案。下面是我如何通过 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);

【讨论】:

我认为接受的答案有点模糊。经过无数小时,这实际上挽救了我的一天!谢谢你好先生【参考方案2】:

你的问题有几个问题。

首先,GL_LUMINANCE16 不是“整数纹理”。它是一个包含标准化无符号整数值的纹理。它使用整数来表示 [0, 1] 范围内的浮点数。如果要存储实际整数,则必须使用实际的integer image format。

第二,不能渲染到亮度纹理;它们不是可呈现颜色的格式。如果你真的想渲染到单通道纹理,你必须创建单通道图像格式。因此,您使用 GL_R16UI 代替 GL_LUMINANCE16,这是一种 16 位单通道无符号整数图像格式。

现在您已正确设置,这非常简单。定义一个uint 片段着色器输出并让您的片段着色器将您的uint 值写入它。这个uint 可能来自顶点着色器或制服;不管你想做什么。

显然,您还需要将纹理或渲染缓冲区附加到 FBO,但我很确定您知道这一点。

最后一件事:除非您的意思是one of these,否则不要使用短语“纹理缓冲区”。否则,它会变得混乱。

【讨论】:

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

我想你的整数 ID 是一组原语的标识符;确实可以将其定义为着色器制服:

uniform int vertedObjectId;

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

此纹理可以附加到帧缓冲区对象(注意framebuffer completeness)。帧缓冲附件可以是bound 到一个整数输出变量:

out int fragmentObjectId;

void main() 
    fragmentObjectId = vertedObjectId;

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

【讨论】:

以上是关于glsl fragmentshader 渲染对象ID的主要内容,如果未能解决你的问题,请参考以下文章

GLSL中几个修饰符的简单讲解

理解GLSL内部变量gl_FragCoord

用于片段着色器的 OpenGL GLSL 绑定采样器

qt glsl渲染rgb

更快的全屏渲染(OpenGL/glsl)?

GLSL 粒子的渲染(premult)