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

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

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

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

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

示例

为了详细说明,我将浮点数据以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))。
实际计算值的直观示例:

uv(x, y, z) = ((x, y, z) + 0.5) / (20, 20, 20)

19|                    1 |           
  |                      |           
..|               uv   ..|           
  |  (2.2, 3.0)   ===>   |  (0.135, 0.175)
1 |      x               |      x    
  |___________           |___________
0    1  ..  19         0            1

代码

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

// Texture buffer creation
t = globjects::Texture::createDefault(gl::GLenum::GL_TEXTURE_3D);

t->setParameter(gl::GL_TEXTURE_WRAP_S, gl::GL_CLAMP_TO_EDGE);
t->setParameter(gl::GL_TEXTURE_WRAP_T, gl::GL_CLAMP_TO_EDGE);
t->setParameter(gl::GL_TEXTURE_WRAP_R, gl::GL_CLAMP_TO_EDGE);

t->setParameter(gl::GL_TEXTURE_MIN_FILTER, gl::GL_LINEAR);
t->setParameter(gl::GL_TEXTURE_MAG_FILTER, gl::GL_LINEAR);

将调用计算着色器。

// datatex holds image information
t->image3D(0, gl::GL_RGB32F, datatex->dimensions, 0, gl::GL_RGB, gl::GL_FLOAT, (const uint8_t*) datatex->data());

// ... (Make texture resident)

gl::glDispatchCompute(1, 1, 1);

// ... (Make texture not resident)

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


# version 450

# extension GL_ARB_bindless_texture : enable

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

layout(binding=0) uniform samplers
{
  sampler3D tex[1];
};

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

void main() {
  // Automatic interpolation
  vec4 correct1 = texture(tex[0], uv(vec3(2.0,0,0), 0);
  vec4 correct2 = texture(tex[0], uv(vec3(2.5,0,0), 0);
  vec4 correct3 = texture(tex[0], uv(vec3(3.0,0,0), 0);
  vec4 wrong    = texture(tex[0], uv(vec3(2.1,0,0), 0);

  // Manual interpolation on x-axis
  vec3 pos = vec3(2.1,0,0);
  vec4 v0  = texelFetch(tex[0], ivec3(floor(pos.x), pos.yz), 0);
  vec4 v1  = texelFetch(tex[0], ivec3(ceil(pos.x), pos.yz), 0);
  vec4 correct4 = mix(v0, v1, fract(pos.x));
}

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

c3frrgcw

c3frrgcw1#

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

相关问题