GLSL顶点着色器双线性采样高度图

我正在创建一个几何图形地形。 到目前为止,我的工作还算不错。 相机附近的地形曲面细分非常高,因此几何图形越远越差。 地形的几何形状基本上沿着相机并基于顶点的位置对高度贴图纹理进行采样。 由于几何曲面细分非常高,因此有时可以在纹理采样时看到每个像素。 它会产生明显的像素颠簸。 我想我可以通过平滑高度图的采样来解决这个问题。 不过,我似乎有一个与某些双线性采样代码有关的奇怪问题。 我通过根据高度贴图纹理移动每个顶点来渲染地形。 要在给定的UV坐标下获得顶点的高度,我可以使用:

vec2 worldToMapSpace( vec2 worldPosition ) {
   return ( worldPosition / worldScale + 0.5 );
}

float getHeight( vec3 worldPosition )
{
        #ifdef USE_HEIGHTFIELD
        vec2 heightUv = worldToMapSpace(worldPosition.xz);
        vec2 tHeightSize = vec2( HEIGHTFIELD_SIZE_WIDTH, HEIGHTFIELD_SIZE_HEIGHT ); //both 512
        vec2 texel = vec2( 1.0 / tHeightSize );
        //float coarseHeight = texture2DBilinear( heightfield, heightUv,  texel, tHeightSize ).r;
        float coarseHeight = texture2D( heightfield, vUv ).r;
        return altitude * coarseHeight + heightOffset;
    #else
        return 0.0;
    #endif
}

这产生了这个(注意你可以看到每个像素):

在这里输入图像描述

这是一个线框:

在这里输入图像描述

我想让地形取样更顺畅。 所以我想我可以使用一些双线性取样而不是标准的texture2D函数。 所以这里是我的双线性采样函数:

vec4 texture2DBilinear( sampler2D textureSampler, vec2 uv, vec2 texelSize, vec2 textureSize )
{
    vec4 tl = texture2D(textureSampler, uv);
    vec4 tr = texture2D(textureSampler, uv + vec2( texelSize.x, 0.0 ));
    vec4 bl = texture2D(textureSampler, uv + vec2( 0.0, texelSize.y ));
    vec4 br = texture2D(textureSampler, uv + vec2( texelSize.x, texelSize.y ));
    vec2 f = fract( uv.xy * textureSize ); // get the decimal part
    vec4 tA = mix( tl, tr, f.x );
    vec4 tB = mix( bl, br, f.x );
    return mix( tA, tB, f.y ); 
}

texelSize计算为1 / heightmap大小:

vec2 texel = vec2( 1.0 / tHeightSize );

而textureSize是高度图的宽度和高度。 但是,当我使用这个函数时,我得到了这个结果:

float coarseHeight = texture2DBilinear( heightfield, heightUv,  texel, tHeightSize ).r;

在这里输入图像描述

现在看起来更糟:(任何想法,我可能做错了什么?或者我怎么能得到一个更平滑的地形采样?

编辑

这是一个俯视地形的垂直截图。 你可以看到这些图层正常工作。 但请注意,具有较少三角剖分且看起来较平滑的外层和具有较高镶嵌细分的外层显示每个像素。 我试图找到一种方法来平滑纹理采样。

在这里输入图像描述在这里输入图像描述


我能够找到并实施使用catmulrom插值的技术。 代码如下。

// catmull works by specifying 4 control points p0, p1, p2, p3 and a weight. The function is used to calculate a point n between p1 and p2 based
// on the weight. The weight is normalized, so if it's a value of 0 then the return value will be p1 and if its 1 it will return p2. 
float catmullRom( float p0, float p1, float p2, float p3, float weight ) {
    float weight2 = weight * weight;
    return 0.5 * (
        p0 * weight * ( ( 2.0 - weight ) * weight - 1.0 ) +
        p1 * ( weight2 * ( 3.0 * weight - 5.0 ) + 2.0 ) +
        p2 * weight * ( ( 4.0 - 3.0 * weight ) * weight + 1.0 ) +
        p3 * ( weight - 1.0 ) * weight2 );
}

// Performs a horizontal catmulrom operation at a given V value.
float textureCubicU( sampler2D samp, vec2 uv00, float texel, float offsetV, float frac ) {
    return catmullRom(
        texture2DLod( samp, uv00 + vec2( -texel, offsetV ), 0.0 ).r,
        texture2DLod( samp, uv00 + vec2( 0.0, offsetV ), 0.0 ).r,
        texture2DLod( samp, uv00 + vec2( texel, offsetV ), 0.0 ).r,
        texture2DLod( samp, uv00 + vec2( texel * 2.0, offsetV ), 0.0 ).r,
    frac );
}

// Samples a texture using a bicubic sampling algorithm. This essentially queries neighbouring
// pixels to get an average value.
float textureBicubic( sampler2D samp, vec2 uv00, vec2 texel, vec2 frac ) {
    return catmullRom(
        textureCubicU( samp, uv00, texel.x, -texel.y, frac.x ),
        textureCubicU( samp, uv00, texel.x, 0.0, frac.x ),
        textureCubicU( samp, uv00, texel.x, texel.y, frac.x ),
        textureCubicU( samp, uv00, texel.x, texel.y * 2.0, frac.x ),
    frac.y );
}

    // Gets the  UV coordinates based on the world X Z position
    vec2 worldToMapSpace( vec2 worldPosition ) {
        return ( worldPosition / worldScale + 0.5 );
    }


// Gets the height at a location p (world space)
float getHeight( vec3 worldPosition )
{
    #ifdef USE_HEIGHTFIELD

        vec2 heightUv = worldToMapSpace(worldPosition.xz);
        vec2 tHeightSize = vec2( HEIGHTFIELD_WIDTH, HEIGHTFIELD_HEIGHT );

        // If we increase the smoothness factor, the terrain becomes a lot smoother.
        // This is because it has the effect of shrinking the texture size and increaing
        // the texel size. Which means when we do sampling the samples are from farther away - making
        // it smoother. However this means the terrain looks less like the original heightmap and so
        // terrain picking goes a bit off. 
        float smoothness = 1.1;
        tHeightSize /= smoothness;

        // The size of each texel
        vec2 texel = vec2( 1.0 / tHeightSize );

        // Find the top-left texel we need to sample.
        vec2 heightUv00 = ( floor( heightUv * tHeightSize ) ) / tHeightSize;

        // Determine the fraction across the 4-texel quad we need to compute.
        vec2 frac = vec2( heightUv - heightUv00 ) * tHeightSize;

        float coarseHeight = textureBicubic( heightfield, heightUv00, texel, frac );
        return altitude * coarseHeight + heightOffset;
    #else
        return 0.0;
    #endif
}
链接地址: http://www.djcxy.com/p/22937.html

上一篇: GLSL Vertex shader bilinear sampling heightmap

下一篇: Odd memory consumption between x32 and x64