![5478531151fec7db89e6bebb5b218698.png](https://i-blog.csdnimg.cn/blog_migrate/76c6e65eae0888c2c4d19932e7b7c558.jpeg)
介绍
本文通过顶点程序对任意拓扑形状的网格进行动态的自适应优化。网格优化技术将表面的描述分割成粗糙的多边形网格,而使用连续的置换函数将它们连接起来。然后在渲染的时候,对粗糙网格进行细分和位移操作。细分把粗糙网格的每个多边形分割成一系列小三角形。位移通过位移函数给出的向量方向移动这些顶点。本文方法依赖于质心坐标来执行一个一致的、具有任意适应性的细分过程。
本优化方法的一个主要优点在于仅CPU层处理低分辨率的网格,而使GPU适应性地产生高质量替代网格。目标网格不会在CPU中产生也不会在图形总线上传输,甚至不会存储在GPU上,唯一瓶颈在于GPU的顶点运算能力。
本文提出的通用网格自适应的优化技术(generic adaptive mesh refinement, GAMeR)提供了以下特性:
- 不需要预处理过程就可以表示标准几何体,也不需要额外的数据结构
- 只有粗糙的网格从CPU传到GPU,需要的额外数据是逐顶点的缩放属性,称为深度标签(depth tag),标记了每个顶点附近需要的LOD,既可以自动生成也可以用户指定
- 网格优化动态执行,因此可以获得任意LOD
- 细分和位移都在顶点程序中单过程执行
GAMeR的工作架构如下,核心思想在于对单个三角形各顶点的深度标签中所有可能的优化结构进行预处理,并使用质心坐标进行编号。每个可能的配置称作自适应优化模式(adaptive refinement pattern, ARP),并存储在顶点缓冲中。渲染时将各个属性传输到GPU并根据深度标签选择对应ARP,根据ARP中的质心坐标进行细分和位移。
![aa12fa941710e6c32ec3e199a0118f74.png](https://i-blog.csdnimg.cn/blog_migrate/baddeb4175ccfecc6e559b9779495d00.jpeg)
此外,之前在GPU Gems2中也提到过另一种自适应细分和位移的做法:
Tolo:[GPU Gems2] 7. 带有位移映射的细分表面的自适应镶嵌zhuanlan.zhihu.com实现
ARP
在GAMeR的初始阶段,所有可能的ARP被计算一次,并且存储在一个被称为ARP池的三维矩阵中,根据三条边对应的深度{i,j,k}来索引对应的ARP值。
![5d5093f7365e9184803a97e7b44b494d.png](https://i-blog.csdnimg.cn/blog_migrate/eaf0a7f84fbc206973abd9b90f5c5c71.png)
每个ARP的结点通过质心坐标来编号。在顶点程序中,被优化的顶点的质心坐标会替代其原有的位置gl_Vertex。要在光栅化阶段达到最大的效率,ARP被编码为一个关于细分三角形的、有序化的顶点缓存,直接存储在GPU内存中。在渲染阶段,要执行的唯一操作就是绑定所选的ARP的序号缓冲区,而顶点缓冲区则始终保持一致。
深度标签计算
计算深度标签通常的选择包括了视点到顶点的距离、局部曲率、对象的重要程度、突出点或是对这些值的综合。一旦深度标签被计算,每个粗糙多边形的属性便以一致变量的形式传输到GPU中,并且使用深度标签配置来选择合适的ARP序号缓冲区。每条边的深度由相邻顶点的深度标签取平均得到。
![6b21544b024751170e217bba1287159a.png](https://i-blog.csdnimg.cn/blog_migrate/3c49c5a0e61f634a2539d287cdc7a8e6.jpeg)
CPU阶段的渲染循环
CPU阶段,应用程序只需要计算逐像素的深度标签,从ARP池中绑定合适的序号缓冲区并且绘制它即可。
GLuint ARPPool[MaxDepth][MaxDepth][MaxDepth];
. . .
void render(Mesh M) {
if (dynamic)
for each Vertex V of M do
V.tag = computeRefinementDepth (V);
for each CoarseTriangle T of M do {
sendToGPU(T.attributes);
bind(ARPPool[T.v0.tag][T.v1.tag][T.v2.tag]);
drawElement();
}
}
GPU阶段的优化处理
顶点程序包含三个阶段:
- 细分阶段:简单的对粗糙三角形顶点进行插值
- 置换阶段:对连续的置换函数进行采样和插值
- 传统的着色阶段
const uniform vec3 p0, p1, p2, n0, n1, n2;
// User-defined Displacement Function
float dispFunc(vec3 v) {. . .}
void main(void) {
// Tessellation by barycentric interpolation
float u = gl_Vertex.y, v = gl_Vertex.z, w = gl_Vertex.x; // w=1-u-v
gl_Vertex = vec4 (p0*w + p1*u + p2*v, gl_Vertex.w);
gl_Normal = n0*w + n1*u + n2*v;
// User-defined Displacement Function
float d = dispFunc(gl_Vertex.xyz);
gl_Vertex += d * gl_Normal;
// Shading and Output
. . .
}
结果
![05f8f2e6b4da78d42f01435d43d3dcaa.png](https://i-blog.csdnimg.cn/blog_migrate/5a4ae67eca7bf87c94b9c9240fab9f6d.jpeg)
![a153b0deef3dc46587a082d75838d7ca.png](https://i-blog.csdnimg.cn/blog_migrate/089f99a209172d5dda293871fbf3a7b1.jpeg)
![a8ff7874d3ea4f1c3a664d3b0dd2c81a.png](https://i-blog.csdnimg.cn/blog_migrate/ef332fe9a5367cc3bc15a0e0ae3eddbe.jpeg)
![3927277e83cc5ba47bb80df5373b7ee3.png](https://i-blog.csdnimg.cn/blog_migrate/f689cdd657b94d67b700076f5786fadb.jpeg)