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

问题描述:

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

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 
} 

将会产生这种(注意如何可以看到每个像素):

enter image description here

这里是一个线框:

enter image description here

我想使地形采样平滑。所以我想我可以使用一些双线性取样而不是标准的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 /高度图大小:

vec2 texel = vec2(1.0/tHeightSize); 

和textureSize是高度图的宽度和高度。然而,当我使用这个功能我得到这样的结果:??

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

enter image description here

现在似乎更糟:(任何想法我可能是做错了或者怎么样,我可以得到更流畅的地形采样

编辑

这里是一个垂直的屏幕截图地势往下看,可以看到该层很好地工作。但是请注意,对具有较少三角外层看起来更平滑,而具有更高曲面细分的则显示每个像素。我试图找到一种方法来平滑纹理采样。

enter image description hereenter image description here

+0

为什么您首先使用自定义双线性插值?如果每个顶点在高度图中都有一个像素,则应该在纹理上使用高斯模糊以使其“平滑”。如果你有更多的顶点比像素内置的纹理插值将做的工作。 – dari 2014-09-30 22:10:21

+0

嗨达里,我将不得不编辑我的问题来澄清。原因是因为我正在使用geoclipmapping技术。相机附近的地形是非常高的曲面细分。因为曲面细分非常高,所以有更多的三角形比像素更多。所以它不是1比1。即采样需要更精细,或者确实需要在像素值之间插值。 – Mat 2014-09-30 22:19:15

+0

为什么你不使用内插插件? HTTPS://www.opengl。org/wiki/Sampler_Object#Sampling_parameters – dari 2014-09-30 22:26:37

我能够找到并实现使用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 
} 
+0

这很有用!谢谢。你能分享结果(图片)吗? – Nolesh 2017-07-06 04:15:37