培养游戏设计与开发精英 网站地图 | 联系我们
首页 | 学校简介 | 课程设置 | 师资力量 | 权威认证 | 开班信息 | 在线服务 | 学员天地 | 就业广场 | 学院新闻 | 媒体关注 | 交通指南
招生办公告栏

报名/咨询热线:

027-87685520、67863577

点此浏览最新一期招生简章 点此直接在线报名
游戏课程体系
汇众益智游戏学院
v4.0专业课程设置
游戏策划课程
游戏美术设计
游戏程序设计
权威专业资格认证
最新开班信息
汇众益智所获荣誉
游戏课程相关
点此浏览汇众益智游戏学院简介
《学校简介》
点此浏览就业广场栏目
《课程设置》
点此浏览最新一期招生简章
《招生简章》
专题讲座
《专题讲座》
点此进入学员天地栏目
《学员天地》
点此浏览就业广场栏目
《就业广场》
你现在的位置:首页 >> 网游 >> 正文 网游
加速&优化技术
发布:武汉游戏学院 来源:www.gamedu.org

作者:Tom Hammersley 译者:pAnic

译者序:这是一篇讲解3D引擎中,某些细节优化的文章,文中使用的方法同样适用于其他场合。不过因为本人对这方面的技术和词汇不是很熟悉,所以这篇翻译可能存在很多谬误,希望大家多加批评指正。
原文:Speed-up & Optimization Techniques

绪论
  在这个页面,我收集了一些不同的加速3D引擎的小窍门。我会先介绍一些显而易见的,因为许多人会忽视它们,接着是一些更精彩的。如果你有其他的窍门和算法,尽可以告诉我。

Float --> int 转化
值得尝试的做法,因为这种转化一般会更慢。我知道的这两种方法是用#pragma和内联函数。首先,#pragma:

#pragma aux RoundToInt= "fistp DWORD [eax]" parm nomemory [eax] [8087] modify exact [8087];

这种方法表现的非常好,WDISASM 表明编译器抛弃了对__CHP的调用而内联了这个转化。另一种方法不使用#pragma,可以被移植到其他编译器。这由"InnerSect"提出:

#define FIST_MAGIC ((((65536.0 * 65536.0 * 16)+(65536.0 * 0.5))* 65536.0))
int32 QuickFist(float inval)
{
double dtemp = FIST_MAGIC + inval;
return ((*(int32 *)&dtemp) - 0x80000000);
}

他做了一些测试,说来有趣,如果我没记错的话,他发现上面的方法更快。

提前计算
  提前计算所有数据!你没法提前计算太多东西。不要以为什么都能查表,那样你会失去灵活性。关注不同的方法。首先要提前计算的是你的法向量。表面法向量和顶点法向量。对调试来说非常重要-这些向量的计算耗时惊人。一旦你提前计算了这些数值,你只要像对待对象的其他部分一样旋转他们就可以了。可是不要去平移或者缩放他们。它们必须保持为单位向量。如果你偶然缩放了他们,可以乘以一个相反的比例把他们还原;例如:

| Sx 0 0 0 |
Transform Matrix = | 0 Sy 0 0 |
| 0 0 Sz 0 |
| 0 0 0 0 |
然后你需要这样:
NormalX *= 1 / Sx
NormalY *= 1 / Sy
NormalZ *= 1 / Sz

当你进行裁减和计算光照的时候,你也不需要平移他们。Also you don't need to transform your normals when you perform culling or lighting. 简单的反向平移你考虑到的其他向量,避免矩阵/向量的乘法。你反向平移可以简单的通过重置上方3×3的矩阵来实现:

For every row
For every column
output[row][column] = input[column][row]
End
End

转换灯光,视角向量,以及任何依赖这个矩阵的向量,都因为提前计算而节省了大量工作。你只需要像往常一样计算,用新的向量和旧的法向量。

倒数
  如果你需要做很多次除数相同的除法,取除数的倒数,变成乘法。倒数就是1/n,n是你的除数。如果你确实很聪明,你会发现在运行 fdiv 的时候还有其他事可做。一个不错的窍门是预热高速缓存,高速缓存命中失败会导致从内存中载入数据等占用很多时间。所以,当你坐等fdiv完成的时候,为什么不读一些内存地址呢?那样它们就会被载入高速缓存了。 在我的屏幕渲染的代码里面,这样看起来运行良好。你可以把这个技术用在透视纹理映射上。也有助于你做透视转换。如果想还原那个倒数,再做一次1/n就可以了。非常精彩。如果你喜欢冒险,你可以把数据存储为倒数,例如Z。我还没试过-这会很难调试。

顶点标识
  任意给定一个需要处理的顶点列表,你会发现有很多-也就是说至少一半-是不可见的,在裁减区域之外。你需要快速排除它们。一个简单的方法就是给这些顶点加一个'可见'标志。

  进入处理循环之前,在准备期间,把这个标志设为false。然后,进入循环之后,如果发现顶点是必要的,就设为true。例如,你发现一个三角形可见,就把它所有的顶点可见标志都设为true。在那之后,你可以简单的跳过或者排除那些没有设这个标志的顶点。这对复杂模型非常有用,因为他们包含大量顶点。同样可以这样处理有很多不可见部分的很大的场景,跳过不必要的渲染。如果用于光照,那你必须得小心。如果你进行可见性裁减的时候,没有计算光照,你会发现在边缘的地方出现错误。你在寻找已定义数据和未定义数据的交界,这很费神。。。

  一种方法识用一个计数器来标识。方法很简单。初始化的时候,你把计数器设置为某个非法值,例如-1 (0xFFFFFFFF).。同时,你把帧计数器设置为0。然后在你处理对象的过程中,如果你发现一个面/顶点需要处理,你就把它的计数器设置为和帧计数器相等。反之,不作任何设置和清除。接着,当你实际处理的是时候,你把它和帧计数器对比,那些计数器值和当前帧计数器相等的顶点/面片会被使用,其他的则被跳过。这在大的数据集很方便,因为那种情况下,每帧都正确清除那个标志代价高昂。

指针
  指针非常方便。利用它们你能实现精巧的数据结构,就像链表,二叉树等等。你也可以利用它们快速寻址。比如说你有一个数组,每个元素都是27字节长。你可以把它们填充到32字节,利用移位来计算地址。但是,数组的每个元素都浪费了5个字节。这很浪费。因此,用指针来寻址。就是说你的顶点结构有27字节。在三角形结构中,不要用int vertindex[3],而是用vertex *vertptr[3]。然后简单的载入指针,寻址,就可以了。(译者注:说实话,这段话我没有完全理解,有不对的地方恳请读者指正)

三角形vs 多边形
  三角形易于处理,渲染速度快。但是如果你有一系列6边形构成的表面,你很容易把它们替换为多边形,不过对三角形渲染器来说,你得做6倍的工作。但是三角形渲染更快。个人推荐三角形,在基于三角形的环境操作起来很简单。多边形有它的优势,可能值得一试。如果有人拥有凸多边形偏移纹理的算法,我会非常感兴趣。

过度渲染/渲染不完全
  过度渲染是速度杀手。尤其是复合渲染代码。你绘制,再次绘制,二次重绘,三次重绘,等等。损失时间和速度。一个简单的排除过度渲染的方法是从前到后排列你的三角形,给它们提供Z-Buffer/S-Buffer。这仍然导致问题。完全依赖 Z-Buffer的三角形渲染会最终变成浪费时间。扫描所有的点,而不渲染任何东西!同样的复杂的分段插入S-Buffers代码,也要付出一定代价。 对大的三角形 S-Buffer看起来运作良好。但是,对大量小三角形,它就没那么吃香了;例如,我的S-buffer算法渲染一个不到3k三角形的头部模型,只计算lambert阴影,耗费了接近30秒钟。很明显,三角形数量使得分段插入的代码负载过大。我想,这里的解决方法是开发更高效的闭合和VSD算法。将来处理这个问题有很多可能性。

  另一个问题是“渲染不完全”(我的术语)。这是说,你花费了太多时间在处理离屏的多边形上。一些有帮助的因素是,场景中的可见多边形不会明显变化。到现在为止,你大概可以计算一个FPS中的可见多边形集了,用玩家的方向寻找一些需要注意的多边形,如果玩家靠的太近,就把它们变为临界点。有效范围例如包围球/包围盒在这里也能派上用场。Hierarchial模型也可能有帮助,用来决定哪部分模型是不必要的。

总结
这里有几条规则,可以让你的引擎跑得更快:

尽可能提前计算
不做不必要的计算
不要重复计算同一内容
如果可能,要利用以前计算的结果
寻找那些可以事半功倍的场合
在用汇编改写你的函数前,自问“我已经找到最好的解决方法了吗?”
试验!
冒险。
不要 重写任何代码仅仅因为“看起来慢”
探索你的目标构架,了解它的特性

  • 上一篇文章:
  • 下一篇文章:
  • 常用功能:[收藏本页] [在线咨询] [在线报名] [联系我们] [关于我们]

    联系电话:027-87685520 027-67863577 全省报名咨询电话(免长话费):400-811-8830

    地 址:武汉市洪山区广埠屯资讯广场B座6楼(华中师范大学北门) >> 详细交通指南

    版权声明:Copyright 2008© 汇众益智游戏学院武汉校区.All Rights Reserved