精确地将世界位置Map到UV纹理坐标(OpenGL计算着色器)

nkhmeac6  于 2022-11-04  发布在  其他
关注(0)|答案(1)|浏览(385)

我需要帮助在OpenGL(4.5)计算着色器中从我的3D纹理中精确采样给定的世界位置(在纹理维度的域内)。更准确地说,我需要帮助我的uv()函数,它将世界坐标Map到精确的对应纹理坐标。
我希望对数据进行线性插值,因此我当前的方法使用texture()。但与预期值相比,这会导致0.001附近的误差。**但是,**如果我使用texelFetch()mix()手动模拟texture()的线性插值,如specification中所述(第248页),我可以将错误减少到0.0000001(这是所需的)。您可以在下面的代码部分看到我如何实现它的示例。
这是我目前在计算着色器中用来计算我的UV坐标的函数:

  1. vec3 uv(const vec3 position) {
  2. return (position + 0.5) / textureSize(tex[0], 0);
  3. }

虽然这一条经常在互联网上被建议,但我的结果并不完全一致。

示例

为了详细说明,我将浮点数据以GL_RGB32F的形式存储在纹理中。为了简单起见,我在这里的示例使用标量GL_R32F。数据的维度为20 x20 x20(但可以是任意的)。我在数据域[0, 19]^3中操作,并希望将我的当前位置精确地Map到纹理域[0, 1]^3,以索引该位置的数据。
我有一个测试纹理,它在x轴上的01之间交替,因此应该将vec3(2.2, 0, 0)插值到0.2
如上所述,我测试了texture()texelFetch() + mix()。我的手动插值计算结果为0.200000003,这是正常的。但调用texture()计算结果为0.199218750,这是一个相当高的错误。奇怪的是,手动插补和自动插补评价相同整数位置的(正确)值和整数位置之间的中间值(例如,vec3(2.0, 0, 0)vec3(2.5, 0, 0)vec3(3.0, 0, 0))。
实际计算值的直观示例:

  1. uv(x, y, z) = ((x, y, z) + 0.5) / (20, 20, 20)
  2. 19| 1 |
  3. | |
  4. ..| uv ..|
  5. | (2.2, 3.0) ===> | (0.135, 0.175)
  6. 1 | x | x
  7. |___________ |___________
  8. 0 1 .. 19 0 1

代码

我使用C++、OpenGL4.5和globjects作为OpenGL的 Package 器。纹理缓冲区的创建和配置如下所示。

  1. // Texture buffer creation
  2. t = globjects::Texture::createDefault(gl::GLenum::GL_TEXTURE_3D);
  3. t->setParameter(gl::GL_TEXTURE_WRAP_S, gl::GL_CLAMP_TO_EDGE);
  4. t->setParameter(gl::GL_TEXTURE_WRAP_T, gl::GL_CLAMP_TO_EDGE);
  5. t->setParameter(gl::GL_TEXTURE_WRAP_R, gl::GL_CLAMP_TO_EDGE);
  6. t->setParameter(gl::GL_TEXTURE_MIN_FILTER, gl::GL_LINEAR);
  7. t->setParameter(gl::GL_TEXTURE_MAG_FILTER, gl::GL_LINEAR);

将调用计算着色器。

  1. // datatex holds image information
  2. t->image3D(0, gl::GL_RGB32F, datatex->dimensions, 0, gl::GL_RGB, gl::GL_FLOAT, (const uint8_t*) datatex->data());
  3. // ... (Make texture resident)
  4. gl::glDispatchCompute(1, 1, 1);
  5. // ... (Make texture not resident)

计算着色器,总结为重要部分,如下所示:

  1. # version 450
  2. # extension GL_ARB_bindless_texture : enable
  3. layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
  4. layout(binding=0) uniform samplers
  5. {
  6. sampler3D tex[1];
  7. };
  8. vec3 uv(const vec3 position) {
  9. return (position + 0.5) / textureSize(tex[0], 0);
  10. }
  11. void main() {
  12. // Automatic interpolation
  13. vec4 correct1 = texture(tex[0], uv(vec3(2.0,0,0), 0);
  14. vec4 correct2 = texture(tex[0], uv(vec3(2.5,0,0), 0);
  15. vec4 correct3 = texture(tex[0], uv(vec3(3.0,0,0), 0);
  16. vec4 wrong = texture(tex[0], uv(vec3(2.1,0,0), 0);
  17. // Manual interpolation on x-axis
  18. vec3 pos = vec3(2.1,0,0);
  19. vec4 v0 = texelFetch(tex[0], ivec3(floor(pos.x), pos.yz), 0);
  20. vec4 v1 = texelFetch(tex[0], ivec3(ceil(pos.x), pos.yz), 0);
  21. vec4 correct4 = mix(v0, v1, fract(pos.x));
  22. }

我希望你的意见,我在我的结束..谢谢!
系统
此外,我正试图在NVIDIA GPU上实现这一点。

c3frrgcw

c3frrgcw1#

在D3D11规范中,GPU的纹理单元只需要以8位精度进行小数采样。这解释了在(归一化)整数或中间整数坐标上不会发生的小误差。
分数精度也可以通过subTexelPrecisionBits在Vulkan中查询,在线Vulkan数据库显示,截至目前,还没有GPU在采样期间提供超过8位的分数精度。
在着色器本身中执行线性插值可提供完整的float32精度。

相关问题