2014-09-30 102 views
7

我正在創建幾何映射地形。到目前爲止,我的工作還算不錯。相機附近的地形曲面細分非常高,因此幾何圖形越遠越差。地形的幾何形狀基本上沿着相機並基於頂點的位置對高度貼圖紋理進行採樣。由於幾何曲面細分非常高,因此有時可以在紋理採樣時看到每個像素。它會產生明顯的像素顛簸。我想我可以通過平滑高度圖的採樣來解決這個問題。不過,我似乎有一個與某些雙線性採樣代碼有關的奇怪問題。我通過根據高度貼圖紋理移動每個頂點來渲染地形。爲了得到一個頂點的高度,在給定的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 here enter 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

回答

3

我能夠找到並實現使用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