游戏优化常见技巧(OpenGL)

OpenGL渲染优化:优化性能的主导思想是:流水线的速度由最慢的一个阶段决定

OpenGL基本优化:

  1. 减少OpenGL的状态变化
    1. 每当我们改变一个OpenGL状态,可能会涉及到硬件的多个寄存器的数据,那么驱动程序就必须将修改的硬件寄存器通过AGP总线发送到硬件,占用大量的CPU资源和AGP带宽和硬件命令解释器时间;
    2. Advice1:尽可能将状态相近的图形绘制命令放在一起,减少OpenGL状态变化
    3. Advice2:使用状态集合,降低驱动程序的CPU处理时间;
  2. 避免光照计算特别是高光计算: Specular的计算是光照计算中最为耗时的运算之一。Diffuse计算相对比较普通,一般图形硬件都会对Diffuse运算进行优化。
  3. 图元类型优化:
    1. 我们使用的大多数图元类型都是Triangle。如果我们每次都是用GL_TRIANGLES,我们将浪费大量的CPU时间和AGP带宽和图形硬件资源。
    2. 使用GL_TRIANGLES,我们每绘制一个三角形,我们就会发送三个定点的数据,如果我们使用G:_TRIANGLE_FAN或者GL_TRIANGLE_STRIP,那么我们可以平均每个三角形一个顶点。
    3. 一般的硬件设计中都开辟一定的Cache区域,如果使用GL_TRIANGLE,我们将无法使用图形硬件的Cache,浪费大量的图形硬件TnL时间
    4. 使用GL_TRIANLGES将比GL_TRIANGLE_STRIP多耗费200%的硬件TnL时间。
    5. 建议:尽可能地使用GL_TRIANGLE_STRIP替代GL_TRIANGLES
  4. 光照条件下使用glMaterial替代glColor:
    1. 光照条件下,如果程序使用glMaterial,那么驱动程序只加载Material属性一遍到硬件,使用glColor将使得驱动程序对每个定点加载颜色信息。将会占用更多的CPU时间和AGP带宽。

CPU到GPU顶点传输数据速度太慢,特别是大量的数据:

  1. 最大化一次性传入数据;从OpenGL简单的来讲就是避免使用glVertex之类,转而使用glDrawArray,对数据集进行批次传送;
  2. 减少顶点传递开销
    1. 在顶点格式中使用适当的位,足够就可以了.不需要对一切都使用浮点格式(16位足够).
    2. 由于顶点数据可以在现代GPU中实现高速缓存,在任意的内存层次当中以相对连续的方式访问顶点数据,引用的空间局部性有助于最大化高速缓冲储存器的命中率,减少对带宽的要求.
    3. 相对于glBegin, glEnd以及Display List,Vertex Array对于驱动程序而言具有最高的内存复制效率,因为驱动程序仅仅需要一次内存数据移动;glBend, glEnd和Display List,则需要三次数据移动。因此尽可能多地使用glDrawArrays和glArrayElement的方式
    4. 实例化思想;绘制草,小行星带等

查找操作耗时:

  1. 把绑定点,Uniform等缓存起来:是因为查找的方法是一个很“昂贵”的功能调用,所有调用后就把结果存起来,避免重复调用造成性能问题;

PBO的使用:

  1. 使用PBO的纹理加载,原图像可以直接加载到PBO中,而PBO是由OpenGL控制的。虽然CPU有参与加载纹理到PBO,但不涉及将像素数据从PBO传输到纹理对象的工作,而是由GPU(OpenGL驱动)来负责PBO到纹理对象的数据传输的,这也就意味着OpenGL执行DMA传输操作不会占用CPU的时钟周期。
  2. 此外,OpenGL还可以安排稍后执行的异步DMA传输。所以glTexImage2D()可以立即返回,CPU也无需等待像素数据的传输了,可以继续其他工作

减少渲染面片:

  1. 面剔除
    1. OpenGL能够检查所有面向观察者的面,并渲染它们,而丢弃那些背向的面,节省我们很多的片段着色器调用(它们的开销很大!)。理论上可以提供50%性能优化
    2. 但我们仍要告诉OpenGL哪些面是正向面,哪些面是背向面。OpenGL使用了一个很聪明的技巧,分析顶点数据的环绕顺序
  2. 遮挡查询
    1. 当我们能够断定一个模型被其他模型挡住时,我们就可以跳过对此模型的渲染。这就是条件渲染的根本。
    2. 那么如何去判断?方法就是用一个简单的包围盒(比如一个立方体)去渲染一下,看看fragment是不是有变化(即包围盒上的某些部分通过了depth test,最终渲染到Framebuffer上了)如果没有任何一个fragment发生改变,就说明这个包围盒是被挡住了,那么被包围起来的模型也必然是被挡住了
    3. 需要用到知识点:遮面查询(Occlusion Query):OpenGL提供了一个Query Object,记录一个包围盒是否改变了某些fragment。在通常的渲染前后用glBeginQuery和glEndQuery包围起来,Query就会记录是否有fragment被改变了。只要SampleRendered()返回值为false,那么这个模型就不用渲染了
    4. 上述Query对象使用时的一个缺点是,CPU必须用循环等待GPU的Query结果。这就拖延了后续渲染步骤,降低了FPS
  3. 条件渲染:为避免CPU循环等待,OpenGL提供了下面2个指令,他们的作用就是用GPU代替了CPU循环的功能
  4. 延迟着色加速渲染效率:
    1. 光照所耗费的资源独立于场景复杂度;
    2. 延期着色需要显卡提供MRT的支持,且利用了不断增加的存储器的带宽;
    3. 能保证在G缓冲中的片段和在屏幕上呈现的像素所包含的片段信息是一样的,因为深度测试已经最终将这里的片段信息作为最顶层的片段。这样保证了对于在光照处理阶段中处理的每一个像素都只处理一次,所以我们能够省下很多无用的渲染调用

纹理优化:

  1. 加载模型的时候,纹理的冗余
  2. 尽量使用MipMap纹理:
    1. 如果应用程序使用Mipmap,那么图形硬件会根据当前的片断对应的纹理LOD计算Texel,这样能够节省大量的纹理元video memory寻址时间;
    2. 而且图形硬件对纹理元做Cache,mipmap中尺寸较小的纹理(Level比较大的)能够节约大量的计算时间。如果应用程序仅仅提供Level 0的最大的纹理,那么图形硬件每次都将使用这个纹理作纹理元计算,不但会浪费大量的计算资源,而且消耗很多的图形芯片带宽。
  3. 纹理组合:
    1. 在游戏或者可视化应用中,我们总是会遇到许多非常小的纹理,一种比较好的办法是我们把这些纹理组合成一个比较大的纹理,例如256X256,然后修改对应三角形定点的纹理坐标,这样驱动程序在加载纹理的video memory的地址时候,驱动程序仅仅需要加载一次可以了
    2. 这种方法在多个造型软件中也经常见到,例如人体造型软件Pose,它将一个人的头发,眼睛,等组合为一个纹理。
  4. 使用MultiTexture替代Multi-Pass做多纹理贴图替代Multi-Pass。,,,绘制多层纹理
    1. 例如我们希望会绘制一个可乐瓶子,而且这个可乐瓶子需要两层标签,利用Multi-Pass我们可以分三次绘制;
  5. 检查OpenGL extension支持,尽可能使用MultiTexture。Mutlitexture的方法将比第一种方法节约流水线的4个运算步骤,Depth Test,Alpha Test,Alpha Blend,和 write to frame Buffers。
  6. 使用压缩纹理:
    1. 压缩纹理比非压缩纹理具有更快的运算速度和更小的存储空间要求,而且很容易使用图形硬件纹理Cache。因此能够显著地提高应用程序性能,特别应用程序的纹理数据量巨大。
    2. 缺点:要求纹理的色彩空间规律性极强,否则会造成严重的颜色失真。
    3. 建议:检查下面的三个OpenGL Extension,尽可能地使用压缩纹理。
    4. GL_ARB_texture_compression  GL_EXT_texture_compression_s3tc GL_S3_s3tc
  7. 合理的纹理尺寸:
    1. 图形硬件系统一般使用4X4,8X8,最高到64X64的纹理Cache策略,如果你的纹理比较简单,在满足可识感官的要求下,尽可能地使用较小的纹理尺寸。

改进光照算法

  1. 改进渲染算法,例如用matcap替代PBR
  2. 预渲染

避免像素操作

  1. 在OpenGL的实现中,都是使用纯软件的方法实现从系统内存到GPU的复制,那么这些将中断整个图形流水线的执行,等待硬件空闲后使用CPU完成,它们将大大降低程序的执行效率。
  2. 解决办法:使用纹理替代像素操作,例如建设你希望在屏幕输出一行字,例如”Qauke III Arena”;

优化游戏大场景,提升渲染速度:

  1. 场景管理的意义
    1. 便于玩家快速的定位场景里面的对象;
    2. 妥善处理巨大场景带来的内存开销问题;
    3. 处理巨大场景带来的渲染效率问题
    4. 处理大量对象的碰撞检测的效率问题
  2. 关于非物理碰撞检测的问题与优化方案:
    1. 对于一个非基于物理引擎的碰撞检测,我们一般需要遍历场景中所有对象的位置并判断二者间距离(假设对象都是球型的),才能检测二者是否发生碰撞。一旦对象数量多起来,CPU根本无法承受。
    2. 优化:实际上,我们知道距离很远的两个对象一定时间内是不会发生碰撞的。为了减小碰撞检测的开销,我们可以将场景划分成N*N块,然后将所有的对象规分到不同的块里面,不同块(且不相邻)之间的对象不会发生碰撞,这样就减小了碰撞检测的开销。
    3. 四叉树与八叉树:二维空间的管理与对象检测可以使用四叉树进行处理。 三维空间的管理与对象检测可以使用八叉树进行处理。
  3. 关于视野渲染的优化:
    1. 与现实世界相似,我们无法看到无线远的东西,或者是无限远的东西几乎是看不清的。所以,游戏世界里面的玩家也类似,每个玩家的视野限制在一个视锥里面。这对于游戏的优化是非常重要的;
    2. 渲染上,因为不在视野内的东西,我们完全就可以不去渲染,即渲染剔除;
    3. 游戏逻辑上,因为玩家的视野有限,过于远的场景数据可以完全不去加载,即场景动态加载;
    4. 在视野范围但是又很远的东西,我们可以给他换一个精度比较低的模型,即LOD
  4. 入口管理:
    1. 对于一些房间内(场景比较小)的游戏,玩家的视野只局限在一个房间里面。这时候其实房间外的任何东西都不需要加载与处理(假如与当前游戏逻辑无关)
    2. 这样,我们就可以在玩家切换房间(如开门)的时候再去加载其他房间场景,这对于游戏的优化是很有帮助的。
  5. 游戏天空的实现:
  6. LOD:
    1. 视野远处的东西玩家是看不到细节的,所以也就不需要给其渲染那么复杂的模型(贴图等)
    2. LOD就是基于这个原理根据与相机位置的距离动态切换资源(高精度资源与低精度资源),是游戏中常用的渲染优化方式
  7. 地形的处理:
    1. 如果给所有的地形都赋予足够的精度,就需要占用大量的内存,这是不现实的,所以地形也要有LOD处理,远处的地形不需要太精细的效果
    2. 另外,在游戏中的大地形是不存在球形的。其实地形只是一个面片,玩家只在面片的上面进行移动
    3. 高度图:其实就是一个二维表,记录每个坐标的高度值(Z值)
    4. 由于地形面积过大,正常情况下需要提供大量的贴图来表现不同位置的不同效果。为了节省贴图资源所占用的内存空间,同时还想表示出丰富的地貌效果,就需要用到多层纹理混合技术只需要几个贴图,按照不同权重的混合就能达到多种不同的效果
    5. 草等生成:实例化
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值