OpenGL创建深度模板纹理以供读取。

6

我在我的应用程序中使用延迟渲染,我正在尝试创建一个包含深度和模板的纹理。

glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0,
???, GL_FLOAT, 0);

现在OpenGL需要什么格式的枚举来处理这个特定的纹理呢?我尝试了几种,但都出现了错误。
另外,访问纹理深度和模板部分的正确GLSL语法是什么?我知道深度纹理通常是uniform sampler2Dshadow类型的,但我该怎么做呢?
float depth = texture(depthstenciltex,uv).r;// <- first bit ? all 32 bit ? 24 bit ?
float stencil = texture(depthstenciltex,uv).a;
2个回答

15

现在,OpenGL需要什么格式的枚举来处理这个特定的纹理。

你遇到的问题是深度+模板是一种完全奇怪的数据组合。前24位(深度)是定点数,剩余的8位(模板)是无符号整数。这需要一种特殊的打包数据类型:GL_UNSIGNED_INT_24_8

此外,访问纹理的深度和模板部分的正确glsl语法是什么。我知道深度纹理通常是uniform sampler2Dshadow。

实际上,你永远不能使用同一个采样器uniform来采样这两个东西,原因如下:

OpenGL Shading Language 4.50 Specification  -  8.9 Texture Functions  -  p. 158

对于深度/模板纹理,采样器类型应与通过OpenGL API设置的要访问的组件相匹配。当深度/模板纹理模式设置为GL_DEPTH_COMPONENT时,应使用浮点采样器类型。当深度/模板纹理模式设置为GL_STENCIL_INDEX时,应使用无符号整数采样器类型。使用不支持的组合进行纹理查找将返回未定义的值。

这意味着如果你想在着色器中同时使用深度和模板,你将需要使用纹理视图(OpenGL 4.2+)并将这些纹理绑定到两个不同的纹理图像单元(每个视图都有一个不同的GL_DEPTH_STENCIL_TEXTURE_MODE状态)。这两件事加在一起意味着你至少需要一个OpenGL 4.4实现。

采样深度和模板的片段着色器:

#version 440
// Sampling the stencil index of a depth+stencil texture became core in OpenGL 4.4

layout (binding=0) uniform sampler2D  depth_tex;
layout (binding=1) uniform usampler2D stencil_tex;

in vec2 uv;

void main (void) {
  float depth   = texture (depth_tex,   uv);
  uint  stencil = texture (stencil_tex, uv);
}

创建模板视图纹理:

// Alternate view of the image data in `depth_stencil_texture`
GLuint stencil_view;
glGenTextures (&stencil_view, 1);
glTextureView (stencil_view, GL_TEXTURE_2D, depth_stencil_tex,
               GL_DEPTH24_STENCIL8, 0, 1, 0, 1);

// ^^^ This requires `depth_stencil_tex` be allocated using `glTexStorage2D (...)`
//       to satisfy `GL_TEXTURE_IMMUTABLE_FORMAT` == `GL_TRUE`

此着色器的OpenGL状态设置:

// Texture Image Unit 0 will treat it as a depth texture
glActiveTexture (GL_TEXTURE0);
glBindTexture   (GL_TEXTURE_2D, depth_stencil_tex);
glTexParameteri (GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);

// Texture Image Unit 1 will treat the stencil view of depth_stencil_tex accordingly
glActiveTexture (GL_TEXTURE1);
glBindTexture   (GL_TEXTURE_2D, stencil_view);
glTexParameteri (GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);

1
可能的解决方法是创建原始纹理的纹理视图,并为该视图设置不同的“GL_DEPTH_TEXTURE_STENCIL_MODE”。 - peppe
@peppe:感谢您的建议。我也有这个想法,但是我愚蠢地认为这不起作用,因为GL_DEPTH24_STENCIL8不是GL_VIEW_CLASS_32_BITS的成员。然而,由于两个纹理的内部格式完全匹配,所以这并不重要。我已经更新了我的答案。 - Andon M. Coleman
@AndonM.Coleman 这是一个旧话题,但也许你可以帮助我吗?如果我不想在着色器中访问模板值,而是常规地使用它作为遮罩,那么我应该如何处理模板附件?这是我的尝试 - http://stackoverflow.com/questions/43504028/how-to-use-depth-texture-with-stencil-opengl-es-3-0。非常感谢任何意见。 - Zheden
我猜你只是为了清晰起见才包含了这行代码:glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_STENCIL_MODE, GL_DEPTH_COMPONENT);,但是它可以省略,因为GL_DEPTH_TEXTURE_STENCIL_MODE的初始值就是GL_DEPTH_COMPONENT - plasmacel
1
这个着色器代码在后续版本中无法编译。在#version 460中,对sampler2D调用texture()总是返回一个vec4,对于usampler2D它将返回uvec4。我现在不知道该怎么办了... - neo-mashiro
1
@neo-mashiro 我在我的代码中完全猜测了它,因为似乎没有任何文档记录,但是 uint stencilBits = texture(texture0, coord).r; 确实似乎提取了实际的模板字节(假设 texture0 是包含深度/模板纹理的 usampler2D,并且启用了 GL_STENCIL_INDEX)。就其价值而言,.g.b 属性似乎都始终为 0,而 .a 似乎始终为 255。我不知道我是否会相信这些最后的值在硬件上保持一致,但是 .r 似乎是答案。 - Sean Werkema

2

nvm 找到了它

glTexImage2D(gl.TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w,h,0,GL_DEPTH_STENCIL,
GL_UNSIGNED_INT_24_8, 0);

我的问题是uint24_8。

在glsl(330)中的用法:

sampler2D depthstenciltex;
...
float depth = texture(depthstenciltex,uv).r;//access the 24 first bit, 
//transformed between [0-1]

实际上,只有前24位可用,其他(模板)位是未定义的。纹理的GL_DEPTH_TEXTURE_STENCIL_MODE属性的初始值为GL_DEPTH_COMPONENT,因此您可以在不更改纹理对象的任何参数的情况下对组合深度/模板纹理的深度分量进行采样。如果您想要对组合深度/模板纹理的模板索引进行采样,则可以将纹理的GL_DEPTH_TEXTURE_STENCIL_MODE设置为GL_STENCIL_INDEX,就像@Andon在他的答案中所示。 - plasmacel

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