OpenGL渲染优化:优化性能的主导思想是:流水线的速度由最慢的一个阶段决定;
OpenGL基本优化:
-
减少OpenGL的状态变化;
- 每当我们改变一个OpenGL状态,可能会涉及到硬件的多个寄存器的数据,那么驱动程序就必须将修改的硬件寄存器通过AGP总线发送到硬件,占用大量的CPU资源和AGP带宽和硬件命令解释器时间;
- Advice1:尽可能将状态相近的图形绘制命令放在一起,减少OpenGL状态变化。
- Advice2:使用状态集合,降低驱动程序的CPU处理时间;
- 避免光照计算特别是高光计算: Specular的计算是光照计算中最为耗时的运算之一。Diffuse计算相对比较普通,一般图形硬件都会对Diffuse运算进行优化。
-
图元类型优化:
- 我们使用的大多数图元类型都是Triangle。如果我们每次都是用GL_TRIANGLES,我们将浪费大量的CPU时间和AGP带宽和图形硬件资源。
- 使用GL_TRIANGLES,我们每绘制一个三角形,我们就会发送三个定点的数据,如果我们使用G:_TRIANGLE_FAN或者GL_TRIANGLE_STRIP,那么我们可以平均每个三角形一个顶点。
- 一般的硬件设计中都开辟一定的Cache区域,如果使用GL_TRIANGLE,我们将无法使用图形硬件的Cache,浪费大量的图形硬件TnL时间。
- 使用GL_TRIANLGES将比GL_TRIANGLE_STRIP多耗费200%的硬件TnL时间。
- 建议:尽可能地使用GL_TRIANGLE_STRIP替代GL_TRIANGLES。
-
光照条件下使用glMaterial替代glColor:
- 光照条件下,如果程序使用glMaterial,那么驱动程序只加载Material属性一遍到硬件,使用glColor将使得驱动程序对每个定点加载颜色信息。将会占用更多的CPU时间和AGP带宽。
CPU到GPU顶点传输数据速度太慢,特别是大量的数据:
- 最大化一次性传入数据;从OpenGL简单的来讲就是避免使用glVertex之类,转而使用glDrawArray,对数据集进行批次传送;
-
减少顶点传递开销:
- 在顶点格式中使用适当的位,足够就可以了.不需要对一切都使用浮点格式(16位足够).
- 由于顶点数据可以在现代GPU中实现高速缓存,在任意的内存层次当中以相对连续的方式访问顶点数据,引用的空间局部性有助于最大化高速缓冲储存器的命中率,减少对带宽的要求.
- 相对于glBegin, glEnd以及Display List,Vertex Array对于驱动程序而言具有最高的内存复制效率,因为驱动程序仅仅需要一次内存数据移动;glBend, glEnd和Display List,则需要三次数据移动。因此尽可能多地使用glDrawArrays和glArrayElement的方式。
- 实例化思想;绘制草,小行星带等
查找操作耗时:
- 把绑定点,Uniform等缓存起来:是因为查找的方法是一个很“昂贵”的功能调用,所有调用后就把结果存起来,避免重复调用造成性能问题;
PBO的使用:
- 使用PBO的纹理加载,原图像可以直接加载到PBO中,而PBO是由OpenGL控制的。虽然CPU有参与加载纹理到PBO,但不涉及将像素数据从PBO传输到纹理对象的工作,而是由GPU(OpenGL驱动)来负责PBO到纹理对象的数据传输的,这也就意味着OpenGL执行DMA传输操作不会占用CPU的时钟周期。
- 此外,OpenGL还可以安排稍后执行的异步DMA传输。所以glTexImage2D()可以立即返回,CPU也无需等待像素数据的传输了,可以继续其他工作。
减少渲染面片:
-
面剔除:
- OpenGL能够检查所有面向观察者的面,并渲染它们,而丢弃那些背向的面,节省我们很多的片段着色器调用(它们的开销很大!)。理论上可以提供50%性能优化
- 但我们仍要告诉OpenGL哪些面是正向面,哪些面是背向面。OpenGL使用了一个很聪明的技巧,分析顶点数据的环绕顺序。
-
遮挡查询:
- 当我们能够断定一个模型被其他模型挡住时,我们就可以跳过对此模型的渲染。这就是条件渲染的根本。
- 那么如何去判断法就是用一个简单的包围盒(比如一个立方体)去渲染一下,看看fragment是不是有变化(即包围盒上的某些部分通过了depth test,最终渲染到Framebuffer上了)。如果没有任何一个fragment发生改变,就说明这个包围盒是被挡住了,那么被包围起来的模型也必然是被挡住了
- 需要用到知识点:遮面查询(Occlusion Query):OpenGL提供了一个Query Object,记录一个包围盒是否改变了某些fragment。在通常的渲染前后用glBeginQuery和glEndQuery包围起来,Query就会记录是否有fragment被改变了。只要SampleRendered()返回值为false,那么这个模型就不用渲染了
- 上述Query对象使用时的一个缺点是,CPU必须用循环等待GPU的Query结果。这就拖延了后续渲染步骤,降低了FPS。
- 条件渲染:为避免CPU循环等待,OpenGL提供了下面2个指令,他们的作用就是用GPU代替了CPU循环的功能。
-
延迟着色加速渲染效率:
- 光照所耗费的资源独立于场景复杂度;
- 延期着色需要显卡提供MRT的支持,且利用了不断增加的存储器的带宽;
- 能保证在G缓冲中的片段和在屏幕上呈现的像素所包含的片段信息是一样的,因为深度测试已经最终将这里的片段信息作为最顶层的片段。这样保证了对于在光照处理阶段中处理的每一个像素都只处理一次,所以我们能够省下很多无用的渲染调用。
纹理优化:
- 加载模型的时候,纹理的冗余;
-
尽量使用MipMap纹理:
- 如果应用程序使用Mipmap,那么图形硬件会根据当前的片断对应的纹理LOD计算Texel,这样能够节省大量的纹理元video memory寻址时间;
- 而且图形硬件对纹理元做Cache,mipmap中尺寸较小的纹理(Level比较大的)能够节约大量的计算时间。如果应用程序仅仅提供Level 0的最大的纹理,那么图形硬件每次都将使用这个纹理作纹理元计算,不但会浪费大量的计算资源,而且消耗很多的图形芯片带宽。
-
纹理组合:
- 在游戏或者可视化应用中,我们总是会遇到许多非常小的纹理,一种比较好的办法是我们把这些纹理组合成一个比较大的纹理,例如256X256,然后修改对应三角形定点的纹理坐标,这样驱动程序在加载纹理的video memory的地址时候,驱动程序仅仅需要加载一次可以了。
- 这种方法在多个造型软件中也经常见到,例如人体造型软件Pose,它将一个人的头发,眼睛,等组合为一个纹理。
-
使用MultiTexture替代Multi-Pass做多纹理贴图替代Multi-Pass。,,,绘制多层纹理
- 例如我们希望会绘制一个可乐瓶子,而且这个可乐瓶子需要两层标签,利用Multi-Pass我们可以分三次绘制;
- 检查OpenGL extension支持,尽可能使用MultiTexture。Mutlitexture的方法将比第一种方法节约流水线的4个运算步骤,Depth Test,Alpha Test,Alpha Blend,和 write to frame Buffers。
-
使用压缩纹理:
- 压缩纹理比非压缩纹理具有更快的运算速度和更小的存储空间要求,而且很容易使用图形硬件纹理Cache。因此能够显著地提高应用程序性能,特别应用程序的纹理数据量巨大。
- 缺点:要求纹理的色彩空间规律性极强,否则会造成严重的颜色失真。
- 建议:检查下面的三个OpenGL Extension,尽可能地使用压缩纹理。
- GL_ARB_texture_compression GL_EXT_texture_compression_s3tc GL_S3_s3tc
-
合理的纹理尺寸:
- 图形硬件系统一般使用4X4,8X8,最高到64X64的纹理Cache策略,如果你的纹理比较简单,在满足可识感官的要求下,尽可能地使用较小的纹理尺寸。
改进光照算法:
- 改进渲染算法,例如用matcap替代PBR;
- 预渲染;
避免像素操作:
- 在OpenGL的实现中,都是使用纯软件的方法实现从系统内存到GPU的复制,那么这些将中断整个图形流水线的执行,等待硬件空闲后使用CPU完成,它们将大大降低程序的执行效率。
- 解决办法:使用纹理替代像素操作,例如建设你希望在屏幕输出一行字,例如”Qauke III Arena”;
优化游戏大场景,提升渲染速度:
-
场景管理的意义:
- 便于玩家快速的定位场景里面的对象;
- 妥善处理巨大场景带来的内存开销问题;
- 处理巨大场景带来的渲染效率问题
- 处理大量对象的碰撞检测的效率问题
-
关于非物理碰撞检测的问题与优化方案:
- 对于一个非基于物理引擎的碰撞检测,我们一般需要遍历场景中所有对象的位置并判断二者间距离(假设对象都是球型的),才能检测二者是否发生碰撞。一旦对象数量多起来,CPU根本无法承受。
- 优化:实际上,我们知道距离很远的两个对象一定时间内是不会发生碰撞的。为了减小碰撞检测的开销,我们可以将场景划分成N*N块,然后将所有的对象规分到不同的块里面,不同块(且不相邻)之间的对象不会发生碰撞,这样就减小了碰撞检测的开销。
- 四叉树与八叉树:二维空间的管理与对象检测可以使用四叉树进行处理。 三维空间的管理与对象检测可以使用八叉树进行处理。
-
关于视野渲染的优化:
- 与现实世界相似,我们无法看到无线远的东西,或者是无限远的东西几乎是看不清的。所以,游戏世界里面的玩家也类似,每个玩家的视野限制在一个视锥里面。这对于游戏的优化是非常重要的;
- 渲染上,因为不在视野内的东西,我们完全就可以不去渲染,即渲染剔除;
- 游戏逻辑上,因为玩家的视野有限,过于远的场景数据可以完全不去加载,即场景动态加载;
- 在视野范围但是又很远的东西,我们可以给他换一个精度比较低的模型,即LOD
-
入口管理:
- 对于一些房间内(场景比较小)的游戏,玩家的视野只局限在一个房间里面。这时候其实房间外的任何东西都不需要加载与处理(假如与当前游戏逻辑无关)
- 这样,我们就可以在玩家切换房间(如开门)的时候再去加载其他房间场景,这对于游戏的优化是很有帮助的。
- 游戏天空的实现:
-
LOD:
- 视野远处的东西玩家是看不到细节的,所以也就不需要给其渲染那么复杂的模型(贴图等)。
- LOD就是基于这个原理根据与相机位置的距离动态切换资源(高精度资源与低精度资源),是游戏中常用的渲染优化方式
-
地形的处理:
- 如果给所有的地形都赋予足够的精度,就需要占用大量的内存,这是不现实的,所以地形也要有LOD处理,远处的地形不需要太精细的效果。
- 另外,在游戏中的大地形是不存在球形的。其实地形只是一个面片,玩家只在面片的上面进行移动。
- 高度图:其实就是一个二维表,记录每个坐标的高度值(Z值)
- 由于地形面积过大,正常情况下需要提供大量的贴图来表现不同位置的不同效果。为了节省贴图资源所占用的内存空间,同时还想表示出丰富的地貌效果,就需要用到多层纹理混合技术。只需要几个贴图,按照不同权重的混合就能达到多种不同的效果。
- 草等生成:实例化
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!