我需要帮助在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轴上的0
和1
之间交替,因此应该将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上实现这一点。
1条答案
按热度按时间c3frrgcw1#
在D3D11规范中,GPU的纹理单元只需要以8位精度进行小数采样。这解释了在(归一化)整数或中间整数坐标上不会发生的小误差。
分数精度也可以通过subTexelPrecisionBits在Vulkan中查询,在线Vulkan数据库显示,截至目前,还没有GPU在采样期间提供超过8位的分数精度。
在着色器本身中执行线性插值可提供完整的float32精度。