引擎设计跟踪 地形LOD的改进
虽然地形已经有LOD和形变(geomorphing, 这里简称morphing)来进行LOD的渐变,从而避免bump, 但是如果LOD级别过多,远处的高山就会严重丢失细节,比如变成尖尖的凸起,非常丑陋,这是morphing无法解决的。
解决一种方式是使用强制的固定LOD (fixed LOD),如果一个block的高度(maxH)或者高度比率(dH/dX)超过一定阈值,就使用固定的高细节的LOD。
例如LOD 0为最高细节,aab为block的包围盒,那么:
if(aab.getSize().y / aab.getSize().x >= THRESOLD)
block.fixedLOD = ; //use the 2nd largest detail level. the most largest 0 is better.
之前我也考虑过类似的问题, 比如一个block如果是平的,那么就使用固定的最低细节的LOD。 这个想法我也是开的脑洞,看起来非常简单,但是实现起来有很多坑:
1.LOD链接问题
通常的LOD索引方法,只处理了相邻两级LOD的缝合链接: LODn--- LODn+1 (1:1),中间不会有2级以上的跳变。但是如果一个block是固定的级别f,那么对于所有的LOD级别,要处理LODf--LODn的缝合问题。
如果固定LOD级别f有n个,数量就是n:n(n对n),index buffer会呈指数级增长,非常浪费。如果固定LOD级别f比较少,比如只有两种: f=0 和 f=max(LOD),实际上index buffer的数量不会太多,
只需要生成2:n (2对n)的缝合。实际上1种或者两种就足够了,f=max(LOD)是针对平的地形block的优化,可有可无,一般地形里面纯平的地面比较少,除了像城市这种场景,里面可能会多点。
另外一种避免浪费的思路是,把中间和四个边的index buffer 拆开,因为中间是大部分index buffer的内容,边上的skirt缝合数据非常少,不管是1:1还是n:n,基本都可以忽略不计。这样每个block要分2个draw call,数量会double,但在开启批次合并时并没有损失。
2.LOD morhping的问题
如果一个block使用了固定LOD,顶点不会有任何改变,那么这个block的morphing已经没有意义了。原来的morhping数据反而会对固定LOD产生影响,导致一些不必要的高低起伏,表现为一些奇怪的凸起。
比如vertex buffer存储了morphing 高度, 就需要清除掉,使用原始高度,关闭morhping。同时,相邻的那些block, 如果不是固定LOD级别,也要清除掉一排边缘衔接顶点的morphing数据,否则一边没有morhping,另一边有,这样也有问题,会有裂缝。
3.LOD 索引生成的方式
假如有一个固定LOD级别f,那么对于f,要生成1对n的缝合。缝合的方式和正常的LODn--- LODn+1缝合方式类似,生成各个边的skirt。
主要问题是生成skirt的方式,可能会影响顶点插值效果。要尽量避免狭长的三角形,因为使用了fixed LOD的block,通常高度或者高度差很大,如果有狭长的三角形,那么他的两个顶点的高度可能差距很大,导致这个三角形的边非常突兀,具体表现为一个非常硬的边和旁边一个凹陷的坑(在加了shadow和AO之后更加明显)。
比如下面的这种就是不好的 (bad tesselation pattern of LOD skirt):

下面这种更好

最后发效果截图.。512x512地形, 6级LOD 从远处看:

下图是加载范围为1536x1536 (512x3)的地形,6级LOD。可以看到远处山的细节还在,近处纯平面的地形的三角形数量很低 (图中有bug已经修复)

另外,目前世界和地形的最大范围为16km(千米),因为原点在(0,0,0),范围为0~16km,损失了符号位的精度。如果将原点设置在-16km,范围为-16km~16km,世界最大范围就能达到32km。
关于最大范围的预估,可以根据IEEE754的浮点数来简单确认,float32的尾数位为23, 而游戏的最小精度大概要1毫米,即10-3,约等于1/1024 = 2-10, 要占用10位,剩下13位,就是8km,加上符号位是16km, 如果精度降低到2毫米,2-9,最大范围可以支持到32km。
如果要支持更大的范围,那么就需要用local坐标系或者类似的方法,或者再加上渲染前直接用物体和相机的相对位置,等等。工作太忙,暂时放后面有时间再做。
总的来说,ChunkLOD虽然比较老,没有充分利用现代GPU的优势,但是也比较适合大型世界,玩法也不算少,而且目前来说更适合mobile。如果是geometry clipmap的话,还没想过如何实现上面类似的效果。
---更新:clipmap也很简单,远处的高山也用高密度网格,采样mip用更高级的miplevel,比如0或者1,效果应该也是类似,但是也要处理缝合。
---更新2:因为clipmap周边低lod网格是空心的“回”型结构,所以高密度网格不管是直接做到这些低密度网格里,还是用缝合衔接,都比较复杂,并不简单。想到的可能方法,至少要对固定LOD的高密度网格分chunk,因为这些网格的形状(高LOD的范围)不固定。
引擎设计跟踪 地形LOD的改进的更多相关文章
- 引擎设计跟踪(九.14.2a) 导出插件问题修复和 Tangent Space 裂缝修复
由于工作很忙, 近半年的业余时间没空搞了, 不过工作马上忙完了, 趁十一有时间修了一些小问题. 这次更新跟骨骼动画无关, 修复了一个之前的, 关于tangent space裂缝的问题: 引擎设计跟踪( ...
- 引擎设计跟踪(九.10) Max插件更新,地形问题备忘
最近没有大的更新. 最近本来要做max的骨骼/动画导出, 看导出插件代码的时候, 突然想起之前tagent space导出的疑问, 于是确认了一下. http://www.cnblogs.com/cr ...
- 引擎设计跟踪(九.14.3.4) mile stone 2 - model和fbx导入的补漏
之前milestone2已经做完的工作, 现在趁有时间记下笔记. 1.设计 这里是指兼容3ds max导出/fbx格式转换等等一系列工作的设计. 最开始, Blade的3dsmax导出插件, 全部代码 ...
- 引擎设计跟踪(九.14.2i) Android GLES 3.0 完善
最近把渲染设备对应的GLES的API填上了. 主要有IRenderDevice/IShader/ITexture/IGraphicsResourceManager/IIndexBuffer/IVert ...
- 引擎设计跟踪(九.14.2f) 最近更新: OpenGL ES & tools
之前骨骼动画的IK暂时放一放, 最近在搞GLES的实现. 之前除了GLES没有实现, Android的代码移植已经完毕: [原]跨平台编程注意事项(三): window 到 android 的 移植 ...
- 引擎设计跟踪 ShadowMap 细节和分析
之前在工作总汇总了shadowmap的各种问题 [工作积累] shadow map问题汇总 最近有点时间再仔细研究了shadowmap的一些算法.主要修复了LiSPSM(上面链接里后面有更新),实现了 ...
- 引擎设计跟踪(九.14.2 final) Inverse Kinematics: CCD 在Blade中的实现
因为工作忙, 好久没有记笔记了, 但是有时候发现还得翻以前的笔记去看, 所以还是尽量记下来备忘. 关于IK, 读了一些paper, 觉得之前翻译的那篇, welman的paper (http://gr ...
- 引擎设计跟踪(九.14.2d) [翻译] shader的跨平台方案之2014
Origin: http://aras-p.info/blog/2014/03/28/cross-platform-shaders-in-2014/ 简译 translation: 作者在2012年写 ...
- 引擎设计跟踪(九.9) 文件包系统(Game Package System)
很早之前,闪现过写文件包系统的想法, 但是觉得还没有到时候. 由于目前工作上在做android ndk开发, 所以业余时间趁热做了android的移植, 因为android ndk提供的mountab ...
随机推荐
- Python 字典删除元素clear、pop、popitem
同其它python内建数据类型一样,字典dict也是有一些实用的操作方法.这里我们要说的是字典删除方法:clear().pop()和popitem(),这三种方法的作用不同,操作方法及返回值都不相同. ...
- 004dayPython学习输入并输出用户名和密码
在python 2.7中,捕获用户输入用raw_input 一.捕获并打印用户名和密码 要求: 输入用户名和密码都可见 # -*- coding:utf-8 -*-userName = raw_inp ...
- SpringSecurity简单记录
在pom.xml中将springsecurity导入后,对于springsecurity会出现三个依赖包:spring-security-web,spring-security-config,spri ...
- easyui datagrid 后台分页,前端如何处理
module.exports = { queryMethod(){ let params = checkQueryParams.call(this); if (!params) { return; } ...
- Python全栈day9习题
本内容主要为If条件语句和while循环的相关知识. 一.使用while循环输入1 2 3 4 5 6 8 9 10 i = 1 while i < 11: if i == 7: pass el ...
- java中的异常处理问题。
异常处理--基本概念 当出现程序无法控制的外部环境问题(用户提供的文件不存在,文件内容损坏,网络不可用...)时,JAVA就会用异常对象来描述. java中用2种方法处理异常: 1.在发生异常的地方直 ...
- Unity more efficient find
Unity caches GameObjects based on their tags, so in a big scene with lots of objects, GameObject.Fin ...
- MySQL关于root密码修改
(转载) 方法一:在mysql系统外,使用mysqladmin修改mysqladmin -u root -p password "test123"Enter password: [ ...
- Swing学习2——图标添加Icon接口使用
废话没有,看代码. 主要就是通过实现Icon接口在标签添加一个圆形图标,并在框架中显示. package com.sword.swing_test; import javax.swing.*; imp ...
- Mysql慢查询-Mysql慢查询详细教程
一.简介开启慢查询日志,可以让MySQL记录下查询超过指定时间的语句,通过定位分析性能的瓶颈,才能更好的优化数据库系统的性能.二.参数说明slow_query_log 慢查询开启状态slow_quer ...