相机位置不同,OpenGL纹理采样有所不同

8
我正在使用加载的高度图数据渲染基于点的地形,但是这些点根据相机位置的不同而变化纹理。为了展示这个 bug(以及这不是 z-buffering 问题造成的),我拍了一些截图,从略微不同的相机位置(相同的角度)下固定了点的大小为 5 像素,如下所示:
PS:如果您将它们拖到一个新标签中,则这些图像足够大,没有意识到 stack 会将它们缩小。
状态 1: State 1 image 状态 2: State 2 image 生成点的代码相对简单,所以我只是发布此帖子来排除选项- mapArray 是一个单维浮点数组,并且被复制到 VBO 中:
for(j = 0; j < mHeight; j++)
{
    for(i = 0; i < mWidth; i++)
    {
        height = bitmapImage[k];

        mapArray[k++] = 5 * i;
        mapArray[k++] = height;
        mapArray[k++] = 5 * j;
    }
}

我认为更有可能是需要调整我的片段着色器,因为我对着色器不太熟悉,尽管我不确定在这样简单的代码中哪里出了问题,但我猜测它可能只是不适用于点渲染。下面是我的片段着色器:

in varying vec2 TexCoordA;
uniform sampler2D myTextureSampler;

void main(){
    gl_FragColor = texture2D(myTextureSampler, TexCoordA.st) * gl_Color;
}

编辑(请求信息): OpenGL版本为4.4,未使用任何纹理标记。

TexCoordA直接从我的顶点着色器传递到着色器中,没有任何改变。使用以下自行计算的UV:

float* UVs = new float[mNumberPoints * 2];
    k = 0; 
    for(j = 0; j < mHeight; j++)
    {
        for(i = 0; i < mWidth; i++)
        {
            UVs[k++] = (1.0f/(float)mWidth) * i;
            UVs[k++] = (1.0f/(float)mHeight) * j;
        }
    }

1
除了扭曲的宽高比之外,这两张图片究竟应该有什么不同? - Andon M. Coleman
1
这是因为相机在移动。请参考(下方)此处的图表,考虑当您以亚像素增量移动位置时会发生什么。如果您的覆盖区域包括整个像素,则此移位不会那么明显。 - Andon M. Coleman
2
透视纹理校正(通过Q的除法)会自动发生。说实话,这甚至不是问题所在。问题在于,如果您将生成点的顶点向屏幕上方稍微移动一点((2.5,0.5)处的底部X将消失),那么图中的覆盖区域将整体向上移动1个像素。中心可能不会跨越像素边界,但覆盖区域会。 - Andon M. Coleman
1
@Tom Burbeck:你能否更新一下问题,包括顶点着色器(我对TexCoordA的计算很感兴趣),加载分配给myTextureSampler uniform的纹理以及用于该纹理的采样标志(如果没有使用,请告诉我OpenGL版本)? - Raxvan
你需要关闭平滑处理,关闭抗锯齿,并且要明白OpenGL只支持点大小为1。任何其他的“支持”都来自硬件供应商提供的驱动程序。当“点”被光栅化时,它会在给定扫描线上从点的起始位置插值到点的结束位置。这些值来自某个地方。屏幕像素与纹理元素之间的比率随着相机位置的变化而改变。你可能认为这些值是直接从顶点着色器传递到像素着色器,但事实并非如此。在另一个GPU上运行并查看结果。 - Dan
显示剩余21条评论
2个回答

0
猜测一下:你的点渲染参数设置如何?也许距离衰减(GL_POINT_DISTANCE_ATTENUATION)以及GL_POINT_SIZE_MINGL_POINT_SIZE_MAX会根据相机位置导致不同的片段大小。另一方面,我记得使用顶点着色器时这些功能被禁用,顶点着色器必须决定大小。我曾经通过使用...
//point size calculation based on z-value as done by distance attenuation
float psFactor = sqrt( 1.0 / (pointParam[0] + pointParam[1] * camDist + pointParam[2] * camDist * camDist) );
gl_PointSize   = pointParam[3] * psFactor; 

其中pointParam保存了三个系数和最小点大小:

uniform vec4 pointParam;   // parameters for point size calculation [a b c min]

你可以通过在顶点着色器中直接设定点的大小 gl_PointSize = [value] 来进行调整。

0

这看起来像是亚像素精准纹理映射的副作用。纹理映射实现的问题在于它需要在实际光栅化像素(片段)上对纹理坐标进行插值。当你的相机移动时,从真实位置到整数像素位置的舍入误差会影响纹理映射,并且通常需要实现抖动自由的动画(否则所有纹理都会在相机移动时以看似随机的亚像素数量跳动。这个主题有一个Paul Nettle的tutorial

您可以尝试通过不采样纹素角而尝试采样纹素中心(将纹素的一半大小添加到点纹理坐标中)来修复此问题。

另一件您可以尝试的事情是通过计算光栅化整数坐标(您需要在着色器中自己计算)和真实位置之间的差异来补偿亚像素精确渲染。这可能足以使采样的纹素更加稳定。

最后,尺寸很重要。如果您的纹理很大,则有限精度纹理坐标的插值误差可能会引入这些伪影。为什么不使用GL_TEXTURE_2D_ARRAY,为每个颜色瓷砖单独设置一个图层?您还可以将ST纹理坐标夹紧到纹理边缘,以更优雅地避免这种情况。


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