毫无疑问的,现代图形设备的管线渲染非常复杂,即使在屏幕上仅仅渲染一个最简单的三角形,也要执行大量的API,其中包含了很多任务,如:为挂接到操作系统的相机视图创建缓冲区;为顶点数据分配缓冲区;建立数据通道以将顶点和纹理数据从RAM传送到VRAM,配置这些内存空间来使用一组特定的数据格式;确定对相机可见的对象;为三角形设置并初始化DrawCall;等待渲染管线完成其任务;最后将渲染的图像显示到屏幕上;
虽然绘制这样一个简单的对象方法开起来过于复杂,理由是,渲染常常需要重复这样的任务,不管是渲染复杂的任务还是简单的任务;

接下来以如下的顺序来讲述关于动态图形的渲染

管线渲染

首先来看一下现代GPU处理一个典型管线渲染的简化图,它概括的展示了步骤执行顺序

GPU前端

前端是指渲染过程中GPU处理顶点数据的部分,它从CPU中接收到网格数据,并发出DrawCall,然后GPu从网格数据中收集顶点信息,通过顶点着色器进行传输,对数据按照1:1的比例进行修改和输出。然后GPU得到一个需要处理的图元列表(三角形------3D图形的最基本形状),然后光栅化器获取这些图元,用来确定图形的那些像素需要绘制,并且根据顶点的位置和当前的相机视图创建图元,这个过程中生成的像素列表称之为片元,这些片元将在后端进行处理;

注意:顶点着色器是类似于C的小程序,用来确定想要的输入数据和数据处理的方式,并且向光栅化器输出一组信息用来生成片元,同时这里也是曲面细分着色器处理的地方,和顶点着色器类似,他们也是上传到GPU的小脚本程序,不同的是他们可以1对多的输出顶点,因此可以通过编程的方式生成其他几何图形;

着色器,实际上是通过脚本来处理光照和着色的任务的叫法

GPU后端

后端描述的是渲染管线中处理片元的部分,每个由光栅化器生成的图元列表(片元)都将通过片元着色器进行处理,与顶点着色器对比,片元着色器往往设计到更复杂的活动,例如深度测试、Alpha测试、着色、纹理采样、光照、阴影等一些可行的后期效果处理,然后这些数据会绘制到帧缓冲区,帧缓冲区保存了当前的图像,一旦当前帧的渲染任务完成后,这些图像就发送到显示设备(显示器);
正常情况下,图形API默认使用两个帧缓冲区,(当然也可以给自定义的渲染方案中生成更多的帧缓冲区),在任何时候,一个镇缓冲区包含了渲染到帧中、并显示到屏幕上的数据;另一个帧缓冲区则在GPU完成命令缓冲区的命令后被激活,进行图形渲染,一旦GPU完成了swap buffers 命令,(CPU请求完成指定帧的最后一条指令,)就翻转帧缓冲区,以呈现新的帧,GPU则使用旧的帧缓冲区绘制下一帧,每次渲染新的帧的时候,都会重复这样的工作;因此GPU只需要两个帧缓冲区就可以处理这个任务;
只要程序还在渲染,就会为每个网格、顶点、片元、帧重复这个从调用图形API到切换帧缓冲区的处理流程;

在后端,有两个指标往往是瓶颈的根源——填充率和内存带宽

填充率

填充率是一个非常广泛的属于,它指的是GPU绘制片元的速度,但是,这仅仅是包含在给定的片元着色器中通过各种田间测试的片元,正常来讲,片元并不等于像素,因为片元会经历不同的测试,只要它没有通过任何一个测试,都会被立即抛弃,这样的处理会大大提升性能,因为不用花费昂贵的操作去处理将要被抛弃的片元;

比如说一个可能导致片元丢失的测试是Z-测试(Z代表的是从相机的视角观察的深度维度),它会检查较近的片元是否已经被绘制,如果已经绘制,则抛弃当前的片元,反之将通过片元着色器推送,在目标像素上绘制,并且在填充率上小号一个填充量;

假设现在又成千上万的重叠的对象,每个对象都可能生成成百上千个片元,那么这将会导致在每一帧中都需要处理上百万个片元,因为从相机的角度来看,片元可能会产生重叠,更重要的是,我们每秒都尝试重复该过程数十次,这是渲染管线中执行许多初始化设置工作非常重要的原因,尽可能的跳过这些绘制过程将节省大量的渲染成本;
正常来讲显卡制造商回奖特定的填充率作为显卡的特性进行宣传,通常来说填充率越高,说明设备通过管线渲染可处理的片元数量越多,因此,如果以每秒30千兆像素,帧速率为60HZ计算,在到达填充率瓶颈之前,每帧可以处理约5亿个片元,在3560*1440的分辨率下,最好的清醒时每个像素只绘制一次;
但是没有完美的事情,填充率也会被高级渲染技术所占用,例如阴影、后期效果处理等也需要提取片元数据 ,在帧缓冲区里面执行自己的处理,即便如此,由于渲染都西昂的顺序,我们总是会重绘一些相同的像素,这样的现象称之为过度渲染;

过度绘制

实际上有几种不同的队列用于渲染,他们可以分为两种类型;一种时不透明队列,一种是透明队列,不透明队列中的渲染对象可以通过Z-测试进行剔除,透明队列中的对象不能这样做,这意味着不管前面有多少对象,都不能假设他们不需要绘制,这样就会导致大量的过度绘制;

内存宽带

内存带宽的瓶颈实际上是通常发生在纹理采样时期,片元着色器会尝试选择匹配的纹理像素,以便在给定的位置绘制给定的片元,我们知道GPU有多个内核、每个内核都可以访问VRAM,还都有一个小得多的本地纹理缓存,来存储GPU最近使用过的纹理;那么当着色器尝试匹配纹理像素的时候,如果需要的纹理已经存在于内核的本地纹理缓存中,那么这样的速度是最快的,如果没有,就需要去VRAM中寻找和匹配需要的纹理,这种传输就会消耗一定数量的内存带宽;
由于纹理缓存必须要等待获取数据之后,才能处理给定的一批片元,如果这个过程没有即时的获取到数据,那么GPU也就无法即时的将数据推送到帧缓冲区,这个过程就会被阻塞, 造成帧率降低

光照和阴影

在现代游戏中,单个对象很少能在一个步骤中完成渲染,主要原因是光照和阴影,这些任务通常在片元着色器的多个过程中处理,对于多个光源的每一个都处理一次,最后才将结果合并,以适应多个灯光效果;
正常来讲,阴影信息的收集需要多个过程;
首先需要为场景设置阴影投射器(针对于光源)和阴影接受器(针对于接受光源效果的物体),分别用来创建和接受阴影,然后每次渲染阴影接受器时,GPU都会从光源的角度将任何阴影投射器对象渲染成纹理,目标是收集每个片元的距离信息,对阴影接收器进行同样的动作,除了阴影投射器和光源重叠的片元以外(也可以认为是没有阴影效果)GPU可将片元渲染的更暗,因为这类片元位于阴影投射器产生的阴影下;
然后,这些信息将变成附加的纹理,称之为阴影纹理,当从著摄像机视角渲染的时候,他们将被混合在阴影接收器的表面,这使得位于光源和给定对象之间的某些位置变得更暗;
为什么光照和阴影会消耗大量的资源?
因为我们需要为每个顶点提供法矢方向,来确定光线如何从表面反射出去,同时要附加顶点颜色属性,来提供额外的着色, 这就为CPU和GPU前端提供了更多要传递的信息,由于片元着色器需要多此传递信息来完成最终的渲染,因此后端在填充率和内存宽带上将处于繁忙状态,这也就是为什么和大多数渲染特性相比,实时阴影异常昂贵(每时每刻都在传输这大量的数据),在启用后会显著的增加DrawCall数的原因;
这样的渲染有两种不同的方式:前向渲染和延迟渲染,他们对光照的性能都有很大的影响;
Edit->projectsetting->player->other settings ->Rendering

前向渲染

前向渲染时场景中传统的灯光渲染的方式,在其过程中,每个对象都通过同一个着色器进行多此渲染,渲染的次数取决于光源的数量、距离、亮度;
首先Unity会考虑对对象影响最大的定向光源组件,然后在基准通道中渲染对象,作为起点,然后再通过片元着色器令附近的几个影响大的光源对同一个对象进行多此重复的渲染,(在起点的基础上进行渲染),然后其他的光源通过一个叫做“球谐函数”的技术取一个平均颜色;
这个渲染采集的灯光数量可以通过Edit->Project settings quality ->pixelLight Count 进行限制,

因此可以看出,使用前向渲染处理大量的具有点光源的场景,会导致DrawCall呈现爆炸式的增长,因为需要配置的渲染状态太多,并且还需要着色器通道;

延迟渲染

延迟渲染又称之为延迟着色,因为实际的着色其实发生在处理的后期,他的工作原理时创建一个几何缓冲区,在这个缓冲区中,场景在没有任何光照的情况下进行渲染,有了这些信息,延迟着色系统就可以在一个过程中生成照明配置文件;
从性能角度来说,延迟着色的结果比较好,因为它可以长生非常好的逐像素照明,而且几乎不需要DrawCall,但是其缺点也很明显,无法独立的管理抗锯齿、透明度、和动画任务的阴影应用,并且使用延迟着色往往需要高性能、昂贵的硬件支持,并且不能用于所有平台;

顶点照明着色(传统)

这种着色是基于摄入灯光的颜色进行统一着色,而不是通过单个像素对表面进行混合照明着色;这种技术一般应用于那种不需要使用阴影、法线映射和其他照明功能的2D游戏;

全局照明

全局照明(Global ILLumination GI)是烘焙LightMpping的一种实现,LightMpping类似于阴影映射技术创建的Shadowmap ,其为每个表示额外照明信息的对象生成一个或多个纹理,然后再片元着色器的光照过程中应用于对象,以模拟静态光照效果;
LightMpping和其他形式的光照最大的区别就是,LightMpping是在编辑器中预先生成的,并且打包到游戏的构建版本中,这能确保游戏在运行的时候不要要不断的重复的去生成光照信息,从而节省大量的DrawCall和重要的GPU活动,由于可以烘焙这些数据,因此有足够的时间来生成高质量的LightMap(代价是需要处理所生成的大量的纹理文件);
但是由于这些文件是预先生成的,因此无法影响游戏中的实时活动,所以默认情况下,LightMpping信息只应用于静态对象,但是还可以将LightProbe添加到场景中,为了生成一组额外的LightMap纹理,这种纹理可以应用到附近移动的动态对象,使这些对象可以从预生成的光照中收益;

多线程渲染

大多数系统默认都开启多线程渲染功能,对于场景中的每个对象,渲染有三个步骤:

  1. 确定对象是否需要渲染(通过视锥体来剔除)
  2. 如果需要就生成渲染对象的指令
  3. 调用对应的图形API
    在没有开启多线程渲染之前,这些操作都是在主线陈上执行的,多线程渲染开启之后,渲染线程会将指令推送到GPU,这种模式可以为主现程节省大量的CPU周期;

性能检测问题

前面说了一下当GPU出现瓶颈可能发生的问题,下面说一下如何检测到这些问题

分析渲染问题

使用Profiler

打开Profiler中的CPUUsage 和GPUUsage视图,(在将垂直同步关掉以后(Editor->project setting ->quality ->other -VSync Count 中禁用Vertical Sync)),Gfx.WaitForPresent 一般是只CPU等待GPU完成当前帧所浪费的时间;这样可以确定是CPU的瓶颈还是GPU的瓶颈;

暴力测试

对于CPU受限,最明显的暴力测试就是降低DrawCall来检查性能是否有突然的提升,但是这种方式不太容易实现,因为我们已经通过静态、动态批处理等技术将DrawCall尽可能的降低;
但是可以引入更多的对象或者禁用节省的DrawCall功能,看看是不是会比以前更糟糕;
对于GPU测试:一般GPU的瓶颈发生于填充率和内存宽带上,因此,可以通过降低屏幕分辨率和纹理分辨率来进行测试;
通过降低屏幕分辨率,可以使光栅器生成的片元减少,浙江减少填充率的消耗;
通过降低纹理质量,纹理的大小就会降低,这样就会降低片元着色器的内存宽带成本,允许GPU更快的获取到必要的成本(Edit->projectsettings->quality->Texture Quality 可以设置纹理的质量)

Unity性能优化之动态图形渲染相关推荐

  1. Unity 性能优化(力荐)

    开始之前先分享几款性能优化的插件: 1.SimpleLOD : 除了同样拥有Mesh Baker所具有的Mesh合并.Atlas烘焙等功能,它还能提供Mesh的简化,并对动态蒙皮网格进行了很好的支持. ...

  2. Unity性能优化 :合批篇

    前言 本系列为一些性能优化的小知识,是日常游戏开发中与性能表现的一些点,本篇为该系列文章的第二篇,前篇链接: 第一篇: Unity性能优化:资源篇 在早期Unity中,对于合批的处理手段主要是下面三种 ...

  3. Unity 性能优化:资源篇

    Unity性能优化 大的方面来说,通过Unity对于项目的性能优化大概可以分为下面几个部分: 资源 渲染 程序 项目配置 而在这个部分中,资源的性能优化属于最基础.最有效的优化手段,也是游戏开发者日常 ...

  4. Unity - 性能优化 - 包体,内存 - 偏静态资源的优化

    文章目录 静态资源优化 - AssetPostprocessor Texture 压缩 Model 网格.动画 压缩 音频压缩 纹理的优化经验 尺寸 通道 发布出来的包资源再次分析 如何工具快速定位静 ...

  5. Unity性能优化全攻略

    #Unity性能优化全攻略 总结自Siki的性能优化. ##优化相关前提 ###Unity游戏安装包大/运行卡的原因 Mono虚拟机 解决这个问题 ###DrawCall https://zhuanl ...

  6. Unity 性能优化 学习

    1:unity 游戏安装包大,运行游戏卡 Unity为什么可以跨平台 因为它内置了mono虚拟机(跨平台) 程序要先在虚拟机运行虚拟机再和操作系统交互   所以包体大运行卡 但是不是一定的原因 (如果 ...

  7. 第一章:unity性能优化之内存优化

    目录 前言 unity性能优化之内存的优化 一.unity Analysis工具的使用. 二.内存优化方法 1.设置和压缩图片 2.图片格式 3.动画文件 4.模型 5.RenderTexture(R ...

  8. Unity性能优化分析思路

    1)Unity性能优化分析思路 ​2)Unity2020后Paticle子节点旋转并把ScalingMode设置为Hierarchy后,对根节点进行缩放时表现不正常 3)FBX默认会冗余lit.mat ...

  9. unity性能优化 模型、贴图、shader优化方法大全

    优化,老生常谈.游戏的优化和网站.软件优化没有任何不同,除了编码质量和使用技巧以外,都是那些空间<>时间.效果<>性能的老套路. 带*号的,都是极其重要的优化手段,就算没用上你 ...

最新文章

  1. 操作临时表+事务级别临时表操作+会话级别临时表操作
  2. VMware 下扩展linux硬盘空间
  3. Python概念:生成唯一性序号uuid
  4. Hyperledger Fabric 私有数据(2)操作流程
  5. 操作系统:经典进程同步问题 之 生产者-消费者问题、读者-写者问题、哲学家进餐问题
  6. unity中单位是米还是厘米_401场地清理是什么,由施工单位做还是甲方做?造价中如何体现?...
  7. 先容Oracle中null的运用要领。
  8. 关于 matlab 的 s 函数的 DirFeedthrough
  9. opencv-python 鼠标事件和坐标点截图
  10. hdu1542 矩形面积并(线段树+离散化+扫描线)
  11. C语言数据结构、十字链表的分析及实现
  12. 使用python+selenium批量提取群成员QQ
  13. 各地的磁倾角_中国各地磁偏角
  14. 诺基亚 XGS-PON FTTP 系统在科威特完成测试
  15. 引起1月12日WIN10 Flash停用原因
  16. 前端个人长期的学习目标
  17. 医疗行业虚拟化终端管理平台解决方案
  18. python基础个人总结
  19. 【小程序】小程序安卓,ios,ipad兼容问题
  20. 给嵌入式ARM+Linux的初学者

热门文章

  1. 关于爱情,每个人都有话说
  2. Python:通过turtle 画樱花树
  3. 卡片机和单反机详细对比
  4. 解决在iOS9上安装的软件显示未受信任的企业级开发者
  5. 亲脂性细胞膜染料: DiO, Dil, DiR, Did - MedChemExpress
  6. PyQT5 (四十三) 在 QTableWidget 表格中设置单元格的字体和颜色 的案例
  7. 用起这 16 个顶尖 Vue 开源项目,节约更多的时间摸鱼
  8. 2022数学建模国赛B题:无人机定位(国二分享)
  9. 串口屏之------Usart GPU 使用手册
  10. webpack打包图片资源问题