在OpenGL中采样16位高度图的正确方法是啥
Posted
技术标签:
【中文标题】在OpenGL中采样16位高度图的正确方法是啥【英文标题】:What is the correct way to sample a 16-bit height map in OpenGL在OpenGL中采样16位高度图的正确方法是什么 【发布时间】:2017-01-18 07:31:09 【问题描述】:我有一个由 16 位有符号值组成的高度图,我只是想知道从着色器中对它们进行采样的正确方法是什么。到目前为止,我使用的是 GL_R16 格式,我认为它将图像存储为无符号整数,但是当采样时返回一个浮点数,我假设它被归一化为 0 - 1。是否也可以选择将纹理作为整数采样为存储在纹理中而不是作为浮点数?
当我上传纹理时:
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, 10800, 10800, 0, GL_R16, GL_SHORT, buffer.data());
在我的着色器中,我对其进行采样:
float colour = texture2D(heightmap, texCoords).r;
outFragColour = vec4(colour, colour, colour, 1);
似乎只对黑色进行采样(即 0);
另外,这会保留高度图的负值吗?这一切都非常令人困惑。
编辑:我刚刚意识到我在调用时收到了来自 OpenGL 的 INVALID_ENUM 错误:
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, 10800, 10800, 0, GL_R16, GL_SHORT, buffer.data());
谢谢。
【问题讨论】:
【参考方案1】:纹理格式:
GL_R16
将 [0, 65535] 间隔映射到 [0, 1]。它本身不能代表负值,当然您可以在自己采样后调整值。
GL_R16_SNORM
在采样时会将 [-32767, 32767] 范围映射到 [-1, 1],因此您可以原生存储负值。
GL_R16I
没有映射。它存储值 [-32768, 32767] 并在采样时准确返回它们。但是,这种纹理格式不支持插值,因此您可以指定的唯一缩小/放大过滤器是 GL_NEAREST
和 GL_NEAREST_MIPMAP_NEAREST
。您必须在着色器中使用整数采样器类型(如isampler2D
),采样函数将返回ivec4
中的数据。
GL_R16UI
与 GL_R16I
相同,但用于 [0, 65535] 范围内的无符号数据。对应的采样器类型为usampler2D
,采样函数返回uvec4
中的数据。
GL_R16F
是半浮点格式。它是上述所有方法的一个很好的替代方案,因为如果您可以容忍其较低的精度,它具有普通浮点数据的优势。
至于你的代码:
目前还不清楚为什么你只会得到黑色。如果data
中的所有值都很小,那么当转换为屏幕上的 8 位格式时,它们可能会简单地四舍五入为零,因此您会看到全黑。
确保您没有收到任何错误(例如,您的纹理非常大,OpenGL 是否有足够的内存来创建此纹理?)。
【讨论】:
很好的答案,谢谢。我意识到调用 glTexImage() 时出现 INVALID_ENUM 错误。我试图弄清楚它是 GL_R16 还是 GL_SHORT。 @TitoneMaurice:啊,你是对的。我错过了。它应该是 internalFormat=GL_R16, format=GL_RED, type=GL_SHORT。 我明白了。因此,作为 GL_R16 的内部格式指定它是 16 位(即每个纹素/像素是 16 位大,格式 GL_RED 指定......嗯,存储在那里的值是通过在着色器中采样 .r 来检索的?如果格式是 GL_RG 而不是 GL_RED 并且我在着色器中对 .r 进行了采样,它还会对 16 位值进行采样吗?抱歉,这令人困惑。 @TitoneMaurice:glTexImage
将数据从你交给它的内存中解包并存储在纹理中。format
和type
描述了如何从你的内存中读取数据。 internalFormat
描述了数据是如何存储在 GPU 端的。我不确定如果两者不匹配会发生什么,而且我目前无法找到规范的相关部分。我认为你根本不应该那样做。以上是关于在OpenGL中采样16位高度图的正确方法是啥的主要内容,如果未能解决你的问题,请参考以下文章