Chinaunix首页 | 论坛 | 博客
  • 博客访问: 174375
  • 博文数量: 43
  • 博客积分: 827
  • 博客等级: 准尉
  • 技术积分: 487
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-26 19:19
文章分类

全部博文(43)

文章存档

2015年(1)

2014年(1)

2013年(5)

2012年(36)

我的朋友

分类: WINDOWS

2012-05-04 17:57:15

本文描述了使用Screen Error Metric的Error Detection机制和Vertex Dependency Graph来渲染Terrain的一种方法,该方法使用了屏幕空间的Error检测,利用Vertex Dependency Graph很自然的解决了在Terrain上Block之间的Crack问题,并且对于算法的复杂度为O(n),n为Height Map中顶点的数量。采用该算法,对于复杂的地方会采用更多的小三角形,而对于平坦的地方,将采用少得大三角形,因此在三角形的数量上能够一定程度的降低,而这取决于给定的Screen Error Threshold。对于本文中的算法主体来源于引用的两篇Paper,而LOD虽然只是简单的设计,但是确实能够起到一定的效果(可参见文章最后的附图,当Screen Error Threshold增高的时候,LOD能够明显的下降)。

Vertex Dependency Graph

在一个Quad中,仍然采用前面的Non-Persistent和Persistent的vertex记法,则一个Quad为
则对于不同level上的顶点上的依赖关系为:
这里的Level是通过对原来的Quad划分的深度确定:
Level                 Partion Depth
1                        0
2                        1
...                      ...
n                        n-1

这种依赖关系是固定的,只要Level确定,那么这个Level上的各个顶点之间的依赖关系确定,而所有的Level上的顶点依赖关系构成了整个网格上的依赖关系。

在这种依赖关系中,对于任意一个顶点,最多有四个孩子,如上图Level 1中右边所示,中点的四个孩子分别为周围的四个边上的中点(而对于在整个网格上的边界上的点,就会出现孩子不足四个的情况);一个点,最多有两个父亲,如Level 1中的左边所示,中点的两个父亲分别为对角线上的两个点(同样不足的情况在网格的边界上会出现)。

建立依赖关系

给定MaxLevel,Level和在这个Level层上某一个Quad的坐标(x,y):
则:
Offset = pow(2, MaxLevel-Level);
Offset为这个Quad上相邻顶点的间距。
如图所示:

Center XY = (x*2*Offset+Offset, y*2*Offset+Offset)
其他8个顶点根据Center XY的相对位置可以得到。
上面的过程求出来的是在Height Map上的顶点坐标,有了这个坐标找到实际在Vertex Buffer上的下标是没有问题的。

对于中间点,他的父亲则需要根据x+y的奇偶性来判定(判定是坐对角线还是右对角线)。
对于四个边上的中间点,他的父亲为中间点。

注意:建立依赖关系仅仅在初始化的时候运行

Screen Error Metric Detection 

首先对于一个顶点,他有一个自身的Error,这个Error的产生来源于该顶点保留和被Removed掉之间的垂直距离。
如图:

红色部分即为这个Error。

检测一个顶点是保留还是移除

记这个Err_w代表这个图中的Error,这个时候Err_w是在World Space中的。
这个方法的大致过程为:计算Err_w在屏幕空间的Err_s,在检测这个Err_s是否超出给定的一个Threshold,如果超出,表示误差过大,需要保留该点,否则表示该点可以移除。

具体过程:
记:
View Space的基向量为 : EVx, EVy, EVz
Eye Position         : EPos = (ex, ey, ez)
Vertex Position      : VPos = (vx, vy, vz)

总是假设Vertex Position就是当前的Camera看出去的中心,并且记
V+ = VPos+(0, Err_w/2, 0)
V- = VPos-(0, Err_w/2, 0)
(这里Height Map以Y轴向上,因此Error产生的方向在Y方向)
Ve+和Ve-为V+和V-对应在View Space中的点

则建立模型

首先计算出在View Space中需要计算的距离,记为De

Ve+-Ve- = (V+-V-)*ViewMatrix

根据假设
EVz_new = Normalize(EPos-VPos)
ViewMatrix = | EVx.x       EVy.x        EVz_new.x      0 |
             | EVx.y       EVy.y        EVz_new.y      0 |
             | EVx.z       EVy.z        EVz_new.z      0 |
             | -Epos*EVx   -EPos*EVy    -EPos*EVz_new  1 |

Ve+-Ve- = (0, Err_w, 0)*ViewMatrix = Err_w*(EVx.y, EVy.y, EVz_new.y)

计算出Ve+-Ve-在View Space中的xy平面上的投影,然后根据比例即可得到De
De = Near*Project_XY(Ve+-Ve-)/|EPos-VPos|
这里
Project_XY(Ve+-Ve-)为在View Space中XY平面上的投影距离即
Project_XY(Ve+-Ve-) = Sqrt((Err_w*EVx.y)^2+(Err_w*EVy.y)^2) = Err_w*Sqrt(EVx.y^2+EVy.y^2)
Near为近平面距离

而ViewMatrix的左上角的3x3子矩阵为标准正交阵则:
(EVx.y, EVy.y, EVz_new.y)为单位向量

De = Near*Err_w*Sqrt(1-EVz_new.z^2)/|EPos-VPos|

有了De,要计算出在屏幕空间的距离,记为Ds,只要知道在View Space中在近平面上一个单位距离有多少像素点即可,记这个值为L

L = Wscreen/(2*Near*tan(Fov/2)), 这实际上是一个常量
其中:
Wscreen : 屏幕宽度
Fov : Camera张角

Ds = De*L = L*Err_w*Near*Sqrt(1-EVz_new.z^2)/|EPos-VPos|
其中 EVz_new = Normalize(EPos-VPos)
这就是最后的表达式

后面需要比较的就是Ds 与 给定的在屏幕空间的一个Threshold。
可见最后的结果只涉及到了EPos和VPos,其他都是常量。

对上面过程的说明:

上面整个过程都是基于一个假设,这个点是Camera看出去的中心,也就是说当这个点真正的处在了假设的位置,Ds才是正确的。而越是偏离的越远,这个计算的Ds就会与真实值偏差越大。

并且根据最后的结果表达式看,只涉及到了EPos和VPos,而与Camera的观察方向没有关系。
所以两者的比较

则假设当中计算出来的值可能偏大也可能偏小,根据原文当中的话“an artifact that is often acceptable as human visual perception degrades toward the periphery”。

该文中介绍的方法来自于:
P. Lindstrom, D. Koller, W. Ribarsky, L. F. Hodges, N. Faust,and G. A. Turner. “Real-time, continuous level of detail rendering of height fields”

Construct Enabled Dependency Graph By Bottom-Up Algorithm

对于在整个Height Map上的所有点,如果一个顶点
1:经过Error Detection没有被移除,那么这个顶点将被Enable
2:这个顶点有一个孩子是Enabled的,那么这个顶点被Enable

对于整个Height Map, 划分为多个Block, 每一个Block的宽度为2^n+1, 因此一个Block的最大Level为n, 整个Height Map只有一个Dependency Graph, Enable Dependency Graph构建在其上。

构建的算法

对每一个可见的Block执行下面的算法
1:以当前选择的LOD Level作为当前Level
2:对当前的Level上的每一个Quad,从左到右,从上到下的顺序遍历Quad,执行如下算法
        1:计算出Quad的Non-Persistent Vertex
        2:对4个Edge-Middle Vertices,如果该顶点没有Enable,进行Error Detection,如果没有通过Error Detection,则执行Enable操作;否则对该点无操作。
        3:对Center执行2中同样的操作
3:如果已经是Lowest Level,停止;否者Level -= 1, 转2

Enable操作
1:该点Enable标示置为true
2:如果Left Parent没有Enable,对Left Parent执行Enable操作
3:如果Right Parent没有Enable,对Right Parent执行Enable操作
注:
1:这里的Left Parent和Right Parent实际上就是一个顶点的两个Parent,Left和Right只是一个规则
2:Enable操作是一个扩散的过程,本Block会将Enable扩散到其他的Block上去,所以就避免了Block之间的Crack的出现

对于上面的算法,遍历Level的时候是从下往上的遍历,最坏情况是每一个点都Enable,即每一个顶点做了一次Enable操作,所以复杂度是O(n),n为顶点数目。

LOD设计

首先对于一个Block中,一个Level上所有顶点的Error的最大值为该Level的最大Error。

我们定义在每一个Level
1:当前Level可往下看多少层,如果为k,则需要取出下k层中最大的Error,作为当前Level的PeekError
2:当前Level可允许的差距Level值,该值用于在Triangulate该Block时候和实际到达最大深度与当前LODLevel的差值进行比较

LOD过程(在每一帧中检测)
1:LOD_TAG记为NO_OP
2:遍历一个Level的Quad时候,如果当前Level为LOD Level,进行下面的过程
        1:NeedLower = true, NeedHigher = false        
        2:取出LOD Level的PeekError
        3:对每一个顶点,如果该顶点需要Error Detection,则:1,如果该点没有通过Detection,则NeedLower=false;2,以PeekError作为该点的Error进行Detection,如果没有通过Detection时,则NeedHigher = true
        4:如果NeedHigher为true,则LOD_TAG = NEED_HIGHER;否则,如果NeedLower为true,则LOD_TAG = NEED_LOWER
3:Triangulate该Block的时候,如果LOD_TAG!=NEED_HIGHER,则取出当前LOD Level的可容许Level差值,记为k,在Triangulate过程中,如果发现存在一个三角形,他所在的Level-LOD_Level的结果大于k,则LOD_TAG = NEED_HIGHER
4:如果LOD_TAG为NEED_HIGHER则,++LOD;否则如果为NEED_LOWER,--LOD。


Triangulate操作

首先在一个Quad中

将会出现上面的八种三角形
Top --> LB + RB
Right --> LU + RU
Bottom --> ...
Left --> ...
这四种划分是属于同一个Level上的,所以上面图中的三角形是属于同一个Level上的

LB --> Top + Right
RB --> Left + Top
RU --> ...
LU --> ...
这四种划分是一级Level向高一级Level的

定义:在一个三角形中,定义这个三角形的斜边上的中点为这个三角形的Base Vertex。
对于一个三角形,如果他的Base Vertex是Enable的,那么这个三角形需要分裂,否则直接选择这个三角形。

因此对于整个Block的Triangulation,只需要以这个Block的两个对角三角形开始,从上往下进行Base Vertex判定。同样只对于可见的Block执行这个过程。

因为使用Vertex Dependency Graph,这可以很好的避免了临接Block上的Crack的问题。


Reference :
P. Lindstrom, D. Koller, W. Ribarsky, L. F. Hodges, N. Faust,and G. A. Turner. "Real-time, continuous level of detail rendering of height fields"
Renato Pajarola, "Large Scale Terrain Visualization Using The Restricted Quadtree Triangulation" 

Samples
使用513x513的Height Map,65x65的Block。从该Sample可以看出,在Screen Error Threshold变大的过程中,注意远处的三角形,和对于复杂地表部分的三角形的变化。
使用513x513的Height Map,65x65的Block。该Sample,可以看到在Screen Error Threshold变大的时候,各个Block的LOD的变化(左边红色部分)


阅读(1567) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~