感谢Infinity汉化组Sorma的译文!
性能优化
我花了一些时间,给代码加入了些许类(class),来优化多线程任务。这样,性能不足的代码就可以顺利地适应不同的核数/CPU数。自然,这样的优化只能对那些易于平行进行的代码使用,比如对连续或独立代码的大量处理。
地面块的生成,便享受了多线程的好处。一个地面块,是由NxN个顶点组成的,N = 2^i + 1 ( i为整数 )。默认的情况是33x33个顶点。
当一个四叉树再细分(也即玩家放大地面时),四个子地形块又会形成。于是总共会生成33x33x4=4356个顶点。每一个顶点都要求不同的处理,谨依复杂度由大到小的顺序,列举如下:
- 求出多种程序噪声函数的值,以生成高度值。(眼下最慢的方法)
- 生成两个位移值,造成悬崖峭壁的效果。
- 在该顶点上生成一法线。
- 生成顶点的各种参数,诸如位置、切空间和贴图坐标。
为了性能的提高,前两个任务比较重要。给每一个顶点生成高度,都要求三到四个程序函数的值。(一般是ridged fbm, a few fractals, terrace effect, maths operations)。因为大家愈接近地面,就愈要看到更多细节的关系,我只得在这些程序中加入很多的八叉。(最主要的ridged fbm,有16个八叉)总计起来,我可能需要为每个顶点调用3D perlin 噪声30到40次,这还没有算进所有那些成为瓶颈的中间数学运算。
悬崖峭壁的移位,需要求两个不规则形,每个有七八个八叉,所以这也确实是性能消耗大户。
到复杂星球上的测试结果显示,你四处移动,当细分一个节点成四份时,可以看见小小的拖慢。虽然非常短(约20毫秒),但也足够让人觉得镜头移动不平滑了。因为程序的天然原因,这个问题没办法修复(非要的话,我就只能又回到菱形-方形的路子上,星球就变得没什么生趣,但是性能会好起来),不过可以通过各种花样来减小它,前述的多线程就是一个办法。
我家里有一个四核的Q6600,如今真能用到它,可是太好了。
另一个改进性能的办法,就是去写SSE优化的噪音生成代码,不过这样的优化实在效果有限,所以我决定将其作为“最后手段”,暂且搁置。
有关演进的细节
使用“过程生成”的好处,就是《无限星辰》会比其他的引擎/游戏更有前途。
我的意思是:只要在设置中改变一点细节,就能把图像质量和细节增加到极限。当今世上,还没有一台电脑,能有够多的内存,够好的显卡、够强的CPU来让《无限星辰》在最高细节级别下运行——因为这些细节可以近乎无限地细化下去。
眼下的每一个地形块有33x33个顶点,这是主流到高端机器的基本设定。所谓“主流到高端”例如:AMD FX/Intel Core2Duos + NV7800、NV8800 或 ATIX1800.
如果你的机器还要好,则可以增进细节如下:65x65的地形块。我在我自己的四核机器上(Q6600+8800GTX)测试了,正如我所料的,速度拖慢了一大截,但是还是很流畅的。(40-50 FPS)比较以下的截图,注意下面那张图中山的轮廓和光影的细节。
65x65基本上就让我的Q6600+8800到极限了。但是这还远远不止其极限,据我估计,眼下最高配置的电脑:4G内存、顶级4核CPU、8800ultra显卡,应该能在129x129的地形块上跑出一个比较低的帧数,虽然我自己没测试过。这要玩起来,自然是不太舒服,但是想想:
无论过多少年,图像的细节都会依据你的机器配置自行调整!
行星表面的种类
我重新整理了一下代码,使之能支持多种行星类型了。(各种乱七八糟的代码先前一切都在同一个class底下。)眼下我已有了几个factory,用不同的程序噪声函数,生成不同的行星地貌。
第一种就是我先前的手记里大量展示过的,我管它叫“Gaian”行星。
另外还加了“Desert”和“Selenian”两种行星。“Desert”行星主要用ridged perlin 噪声,生成沙丘。上面景色枯燥得很,所以还需要加把劲,给它增添点味道。(增加贴图是很重要的)
“Selenian”型的星球表面,基本上就是弹坑。很多很多弹坑。为了做出效果,我创造了一种新的噪声,名之曰“球面”,主要是建立在Voronoi图表的思想上的。首先预生成一组球面,随即放置在一立方体内,当我运行时解出噪声,就算出一位置与每个球面的距离,在此基础上生成一个值,令其看来像一个陨石坑。
测试的效果非常好,虽然比较慢,但是也不碍事,毕竟这个“球面”噪声还有很大的优化空间。
不过,为什么是球面呢?陨石坑是2D的,但是噪声必须建立在3D基础上,这样才不会在行星上造出一大堆乱七八糟的“伤疤”来(因为所有的噪声函数都建立在行星球面的顶点位置之基础上)。
在满布陨石坑的星球上周游的时候,我找到一处意料之外的趣景。大抵上就是几个小陨石坑,被陡壁悬崖逼得挪移了位置。静态的图像很难表现出其真趣,但在3D环境里看到的时候,感觉实在太奇幻了,颇像外星人的石阵。
自己的创造给了自己一个惊喜,这感觉真是太美妙了。
动态模糊
这一点最后谈及,但并非不重要。我花了一点时间,加了一个动态模糊的渲染管道。最基本的动态模糊技术,就是把整个场景渲染成一张图,同上一帧图进行Alpha混合。不过,这样做有点取巧,而且效果不怎么好看(但是很快,所以在性能不佳的电脑上我可能会倒回来用这一招)。
我所采用的,是“真·动态模糊”。视角以内的每一个物体,都根据当前帧和上一帧的变形矩阵而变化,并且算出一个速度值。存放在速度图形里。整个画面同样也渲染成一张图。在后期处理中,每一个像素都在速度图形中取样,在速度矢量方向上,模糊场景图形N遍(在我的shader里,N=32)。
结果虽然移动时很好看,但截屏却会出现奇怪的假象。问题之一就在于,物体轮廓之外就没有模糊效果了,此即所谓“闭塞问题”。
在OpenGL shader tricks GDC 2003,Nvidia给出了一个“解决方案”,即在shader中计算常态和移动矢量之间的点,并且或在本帧的点(夹角大于0),或在上一帧的点(夹角小于0)中,当作顶点使用。主要思想就是,让物体朝移动方向的反方向突出去。
我测试了这个想法,但是很不幸,这样造成的问题比解决的多。
Project offset显然应用了相似的技术,但是它找到了一种减少乃至去除这些假象的方法。要是有人知道他们怎么解决这问题的,请告诉我。
因为动态模糊效果要求将整个画面渲染成单独的速度图形(每秒一次),而每一帧又有20万~30万个三角形,所以开启真动态模糊,会导致速度大大拖慢(约缩减一半)。
原文:http://www.infinity-universe.com/InfinityForums/viewtopic.php?t=5546