在地形渲染中为了减轻渲染负担其中一个技术是就LOD,但其带了的一个问题就是会产生地形“跳动”,因为地形的LOD精度发生变化时镶嵌的密度也发生了 变化。要解决这个问题有一个方法就是当高精的模型向底精模型变化时不是突然变化过去的,而是慢慢渐变而来,这样可以极大的减少“跳动”。通过设置适当的渐 变距离可以做到让观察者感觉不到地形的变化。
关于这个问题在一篇文章里面也有介绍:Terrain Geomorphing in the Vertex Shader[1],但是原文介绍的方法过于复杂而且不适合用硬件加速,但是这仍然是一篇学习地形渲染的好文章。下面提出一种更加简单,快速的方法来解决 这个问题。使用这个方法有一个前提条件就是地形块之间的LOD级别相差不能大于1.
解决这个问题最基本的思想就是:对高精度的地形块进行形变来模拟低精度的地形块, 当高精地形和低精地形的形态完全一样时,地形块的LOD才变为低精度的。设置高精度地形块的高度为H1, 低精度的地形块的高度为H2,最后根据渐变距离求出一个插值t[0, 1],最后地形的高度就为H = lerp(H1, H2, t)。
为了加速渲染顶点形变的过程由vs去计算。设置地形的最大LOD级别为5,也就说有4种形变可能:0->1, 1->2, 2->3, 3->4.这样我们可以把四种形变的高度值存到一个float4(yMove)中。
对于如何求取低精度地形的高度值可以这样计算:先求出顶点是属于那一个地形四方面,然后求出其在四方面上的uv坐标,最后用二次线性插值来得到 四方面内任意一点的高度值。比如:一个地形四方面这四个高度值分别为h00, h10, h01, h11.设u为四方面x方向的坐标[0,1], v为四方面y方向的坐标[0,1].那么最后的高度值:
h(u, v) = (1.0f-v) * ((1.0f-u)*h00 + u*h10) + v * ( (1.0f-u)*h01 + u*h11 )
在计算好的形变的高度值后就可以在vs内计算形变高度值了,在计算之前还需要传入两个参数:
float4 MorphStart, MorphInvRange.其中MorphMin表示形变的起始距离,比如(50, 100, 150, 200)
就表示地形在50处开始0->1形变,100处1->2形变, 150处2->3, 依此类推。MorphInvRange是形变距离范围的倒数,比如上例:MoprhInvRange = (1/50, 1/50, 1/50, 1/50)。
在vs中计算高度值的代码如下:
void vsMain( in float3 Pos : POSITION0,
in float4 yMove : POSITION1 )
{
float viewDist = length(Eye - Pos );
float4 moprh = saturate( (viewDist-MorphStart)*MorphInvRange );
Pos.y += dot( yMove, morph );
}
根据每个顶点对眼睛的距离来决定最后的形变值,可以保证只要顶点位置相同最后形变后的位置也一定相同,避开了文献[1]中地形边界接缝等等麻烦 的问题。注意在vs中计算morph为什么要用saturate,是因为顶点缓冲中的Pos.y是最高精度地形的高度值,比如要从1->2级,除了 要加上1->2的形变高度值外,还要加上0->1的高度差值, 而且2->3, 3->4的形变高度值应该为0.上面这个式子正好能满足上面要求的情况。
处理完顶点形变后,还有一个工作要做就是要根据眼睛与地形块的最小距离来求地形块的LOD,设置眼睛与地形块的最小距离为d,那么求地形lod的代码如下:
if ( d < MorphStart.y ) // 0->1
lod = 0;
else if ( d < MorphStart.z ) // 1->2
lod = 1;
else if ( d < MorphStart.w ) // 2->3
lod = 2;
else if ( d < MorphStart.w + 50 ) // 3->4 (50为最后一级形变的距离)
lod = 3;
else
lod = 4;
现在似乎可以做到消除地形“跳动”的现像了,但是有一点还需要注意那就是光照。上面做的工作只是消除了几何形变产生的“跳动”,还有一个产生 “跳动”感觉的是光照颜色,如果你是在vs内计算的顶点光照那么还是会有“跳动”,因为顶点的密度变了光照的效果肯定不一样。要解决这个问题就需要用光照 图来代替顶点光照,但是这样就限制了地形的光照是静态的。如果你还是想要动态的地形光照,那就需要用地形的法向图在ps内动态计算,注意:法向图的边界的 地方要特殊处理。