分享
 
 
 

ROAM实时动态LOD地形渲染

王朝other·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

1 引言

如果没有接触过涉及到细节等级(LOD)的地形生成法则,恐怕你就不能在地形可视化的世界里任意挥舞你的指挥棒了。细节等级是一种使用了一系列启发式的方法来决定地形的哪一部分需要看起来有更多的细节的技术。在这里,对于地形渲染的许多技术挑战之一是如何存储一个地形的特征。高度图是事实上的标准解决方案,简单的说他们就是保存地形每点高度的二维数组。

2 LOD地形法则概论

一个LOD地形法则的优秀概述可以被三篇论文来描述,作者分别为[1微软的Hoppe][2 Lindstrom][3 Duchaineau]。在第一位作者的论文中描绘了一个基于Progressive Meshes的法则,这是一个与增加三角形到任意网格来达到你需要的细节相关的新的和绝妙的技术。这篇论文是一篇精彩的读物但有点复杂,同时这项技术需要大量的内存。第二篇论文的作者是Lindstrom,他描述了一个叫四叉树(Quad Tree)的结构用于描绘地形碎片(PATCH),一个四叉树递归的把一个地形分割成一个一个小块(tessellates)并建立一个近似的高度图。四叉树非常简单但很有效。第三篇论文的作者是Duchaineau,他描述了一个基于二元三角树结构的法则ROAM(实时优化自适应网格)。这里每一个小片(PATCH)都是一个单独的正二等边三角形,从它的顶点到对面斜边的中点分割三角形为两个新的正等边三角形,分割是递归进行的可以被子三角形重复直到达到希望的细节等级。由于ROAM法则的简单和可扩展性吸引了我的目光。不幸的是这片论文非常短,仅仅只有少量的伪代码。但无论如何,他可以在连续的范围实现从最基本的平面到最高级的优化。而且ROAM分割成小方块非常快速,而且可以动态更新高度图。

3 ROAM执行初步

代码用Visual C++ 6.0来写的,使用OPENGL来渲染。

ROAM资源说明

让我使用一个概述来介绍这个法则,然后讨论单独的小块是如何相互影响的:

1高度图文件被载入内存并和一个Landscape类的实例相联系,多个Landscape物体连接起来产生无限的地形。

2一个新的Landscape物体把载入的高度图的一部分包裹到新的Patch类物体中,这一步的目的是:

(1)使用基于树的结构来控制随着深度而呈指数增长的内存,这样可以保持他们的深度在一个很小的有限的范围。

(2)动态更新高度图需要在变更场景时有一个完整的变更树从算操作。过大的Patch类物体在实时重新计算时非常慢。

3每一个Patch类物体被调用来建立一个MESH的近似值(分割成小块)。Patch类物体使用了一个叫二元三角树的结构来存储即将显示在屏幕上的三角的坐标。这些三角形顶点坐标被非常合理的存储,ROAM使用36字节以上的内存来存储每一个三角形。高效的坐标计算也是渲染的一部分(见下)。

4在分割完高度图后,引擎已经建立了二元三角树。树的叶节点保存了需要进入图形渲染流水线的三角形。

高度图文件格式

高度图使用一个RAW的数据格式来保存,这个格式包含了8位的高度信息。通常高度图必须从头至尾保存在内存中,在高级标题中我将讨论如何扩展法则来呈现大的数据集。

二元三角树Binary Triangle Trees

ROAM使用了二元三角树来保持三角坐标而不是存储一个巨大的三角形坐标数组来描绘地形。这个结构可以看作是一个测量员把地形切断为一个一个小三角块的结果。这些三角块逻辑上看就象一组相连的邻居一样(左右邻居)。同样的当一个三角块把土地当作遗产时,他需要平等的分给两个儿子。

用这样进行扩展,这个三角块就是二元三角树的根节点,其他三角块也是他们各自树的根节点。Landscape类如同一个局域的土地注册表,保存所有三角块的索引,同时也保存他们之间的层次关系。由于大量子三角块的产生,分割土地也成为一个沉重的负担,但是大量的细节可以被需要更好模拟的区域的种群'population'来简单的处理。看图一:

图一 二元三角树结构等级0-3

二元三角树被TriTreeNode结构保存,同时他还保存ROAM需要的五个最基本的数据,参考图二。

struct TriTreeNode {

TriTreeNode *LeftChild;

// Our Left child

TriTreeNode *RightChild;

// Our Right child

TriTreeNode *BaseNeighbor;

// Adjacent node, below us

TriTreeNode *LeftNeighbor;

// Adjacent node, to our left

TriTreeNode *RightNeighbor;

// Adjacent node, to our right

};

图二 基本的二元三角树的子和邻节点

当对高度图建立一个网格模拟值时,我们需要向二元三角树中添加子节点直到达到我们需要的细节。这一步完成后重新遍历整个树,此时把子节点中保存的三角形数据渲染到屏幕上。这就是一个最基本的引擎了但需要重新设置每一帧,这种递归的方法最大的优点是我们不需要保存每一个顶点的数据,可以释放大量的内存给其他物体。实际上,TriTreeNode结构需要多次的建立和销毁,但这种方法是非常高效的,同时我们或许需要建立几万个这样的结构,因此我们需要一个指针指向我们需要的内存,TriTreeNode结构是通过一个静态内存池来分配的,而不是动态分配,他也给了我们一个快速的重新设置状态的方法。

图三 典型的地形PATCH,从左至右依次是网格模式,光照模式,纹理模式

4

Landscape类的详解

Landscape类对地形的细节渲染进行了高级的封装,通过一些简单的函数调用我们可以在屏幕缓冲中进行从简单的点的显示到复杂的地形渲染工作。这里是Landscape类的定义。

class Landscape {

public:

void Init(unsigned char *hMap);

// Initialize the whole process

void Reset();

// Reset for a new frame

void Tessellate();

// Create mesh approximation

void Render();

// Render current mesh static

TriTreeNode *AllocateTri();

// Allocate a new node for the mesh

protected:

static int m_NextTriNode;

// Index to the next free TriTreeNode

static TriTreeNode m_TriPool[];

// Pool of nodes for tessellation

Patch m_aPatches[][];

// Array of patches to be rendered

unsigned char *m_HeightMap;

// Pointer to Height Field data

};

Landscape类管理了一个大的正三角块,同时可以和其他Landscape物体一起工作。在初始化过程中,高度图被分割成大量的可管理的小块,同时把他和一个新的Patch物体联系起来。Patch类及其它的方法我们将在下面花费更多的时间讲解。注意这些函数的简单性,Landscape物体本身是设计用于一个简单的渲染流水线的,尤其是在可以免费使用Z缓冲的今天。

5 Patch类详解

Patch类是这个引擎的灵魂,他可以分为两部分,一半是递归部分,另一半是基本函数部分,下面就是这个类的数据成员和基本函数描述:

class Patch {

public:

void Init( int heightX, int heightY, int worldX, int worldY, unsigned char *hMap);

// Initialize the patch

void Reset();

// Reset for next frame

void Tessellate();

// Create mesh

void Render();

// Render mesh void

ComputeVariance();

// Update for Height Map changes

...

protected:

unsigned char *m_HeightMap;

// Adjusted pointer into Height Field

int m_WorldX, m_WorldY;

// World coordinate offset for patch

unsigned char m_VarianceLeft[];

// Left variance tree

unsigned char m_VarianceRight[];

// Right variance tree

unsigned char *m_CurrentVariance;

// Pointer to current tree in use

unsigned char m_VarianceDirty;

// Does variance tree need updating?

TriTreeNode m_BaseLeft;

// Root node for left triangle tree

TriTreeNode m_BaseRight;

// Root node for right triangle tree

...

在上面的代码中,下面要解释的基本函数被每一个PATCH物体所调用,PATCH类的方法名类似于调用他们的Landscape类的方法,这些方法或许太单纯化这里需要详细的解释一下:

Init()函数需要高度图和世界坐标的偏移值,他们用来对地形进行缩放,指向高度图的指针已经经过调整,指向了这个PATCH物体所需要数据的第一个字节。

Reset()函数释放所有无用的TriTreeNodes结构,接着重新连接两个二元三角树成为一个PATCH,现在这些还没有被提及,但是每一个PATCH物体都有两个单独的二元三角树构成一个正方形(ROAM论文中称为'Diamond')。如果不明白的话再看一下图二,详细的内容下一节再讨论。

Tessellate()函数简单的传递适当的高级三角形参数(每一个PATCH物体的两个根节点)给一个递归版本的函数,函数Render()和ComputeVariance()也是这样。

6 ROAM精华

讲了这么多我们只是讨

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有