![38e78f94b8f7539191ce327f0282c152.png](https://i-blog.csdnimg.cn/blog_migrate/ec2135bb2343a907cff8ca0fecb069ae.jpeg)
写在前边
周末学习了下UE4实时渲染的视频,看完后觉得收获颇多,为了以后自己复习方便,记录于此,如果能帮助到大家,则再好不过了。为了以后复习的准确性和深刻,在这里我力求写的尽可能的详细,同时尽可能的保持客观,不作太多的主观讨论和个人意见掺杂在里边。内容偏多,总结性偏弱,原因是想让大家了解的包括我自己 再次翻看的时候能够了解的更透彻,如果单单写总结结论,可能无法了解背后执行的原理。本篇文章更像是半个翻译,所以各位可以当作读书一样来学习即可。由于写的比较仓促,如果哪里有什么不对的地方还请多多指正,篇幅较长分上下两篇来写,这是上篇。
https://learn.unrealengine.com/course/2504896?r=False&ts=637296221411220370learn.unrealengine.com第一篇:深入探索实时渲染
1,讲解目录也算是延迟渲染渲染器工作顺序
Before Rendering
Geometry Rendering
Rasterizing and GBuffer
Textures
Pixel Shaders and Materials
Reflections
Static Lighting/Shadows
Dynamic Lighting/Shadows
Fog/Transparency
Post Processing
![1185b2ba5df48dbd14d5a860e98ad2cd.png](https://i-blog.csdnimg.cn/blog_migrate/af88441f525297ff5c8be15164e081fa.jpeg)
2,性能优化和画质成反比关系,而我们要做的是通过优化,调整画质使其性能在目标帧率以上运行
![ef0fdf6827e11c4a89430206f541358c.png](https://i-blog.csdnimg.cn/blog_migrate/3e74e204ee62fc5b29dd7735e0b020ed.png)
3,在RTR中我们无法让大量功能既运行流畅,又效果出众,三者中总会缺失一个或两个,我们要做的就是在性能,画质和功能之间找到完美的平衡。
![84c6e24d6ae58d8ced838ecb064cedcf.png](https://i-blog.csdnimg.cn/blog_migrate/9f095a12f0fd0890a7e164f8f7908546.png)
4,要实现良好的性能,我们需要做到四点
所有环节都必须尽可能高效
需要严格的流程标准和限制(比如导入贴图严格限制2的N次幂)
将一部分工作量分配到预计算环节
还需要混合各种方案。
![4cb1c55c15c9c4204ee972d0497484cc.png](https://i-blog.csdnimg.cn/blog_migrate/9120bdf895fc6822164de3e61b8fcab8.jpeg)
5,UE4的直接光照和间接光照总览
我们的方案基本就是混合这些方案,将更具体的方案混合在一起。例如我们可以采用CSM渲染一部分阴影,然后再用SDF渲染一部分阴影,再结合静态阴影等等。
考虑到内存和带宽,有些部分采用预渲染,有些则必须采用混合方案
![e031db942cbf042065ab4ab0c50c7004.png](https://i-blog.csdnimg.cn/blog_migrate/8f6178a1e60f4083ec00d568cc59dd5b.jpeg)
6,所有我们看到的内容在计算机内都是由三角形渲染出来的
![f60899488e7b87af0e72e9ab8ea394d5.png](https://i-blog.csdnimg.cn/blog_migrate/fa35b24dab0639effc02267d02785db4.png)
7,渲染的基本工作原理
从硬盘或其他存储设备加载数据,然后通过数据传输线(带宽)传递到内存中,接下来数据 要在内存和CPU,GPU中相互传输,绝大部分工作中CPU和GPU是保持同步工作的。
CPU和GPU负责处理渲染的不同部分,多数时候是同步的,其中最慢的那个会成为瓶颈。我们需要知道如何分配他们的工作,才能更好的应对。
![53737e4a914df028e23ac4c6c199fd79.png](https://i-blog.csdnimg.cn/blog_migrate/e9959a91f2e34a5c6ff0ccf9a3dc07c4.jpeg)
8,渲染方式有两大类,延迟渲染和前向渲染。UE4默认采用延迟渲染,以下是延迟渲染6个特性
着色发生在延迟环节,所以称为延迟渲染。
使用GBuffer进行合成,GBuffer是一组图像,用于存储信息,然后将部分内容,延迟到稍后渲染:例如着色和光照不会和几何体渲染同时完成,几何体首先会被渲染,之后UE4才会渲染光照并且将结果与之前的几何体混合,延迟渲染延迟了部分渲染。
由于是延迟渲染,所以当它渲染光照时就不再拥有之前渲染集合体时所获得的所有信息,所以光照阶段可供光照渲染的信息变少了。
延迟渲染的好处是可以获得更加稳定且可预测的高质量效果,所以是很多大型游戏的首选标准。
多重采样抗锯齿(MSAA)不能用。
![46d9d0c5d1aa3ac8d27e23049c602b0f.png](https://i-blog.csdnimg.cn/blog_migrate/253772e12b95535068d1b699f7ffe7f1.jpeg)
9,前向渲染的几个特性
着色和几何体和材质在同一个环节计算,即可称之为前向渲染。
无论出现什么功能,都在同时渲染,光照和着色不会延迟,会在几何体渲染的时候被同时完成。这能让你在如何计算光照和材质拥有更多自由度。
当在计算光照和阴影,可以获得更多的信息,因为在同一个环节完成。
不擅长混合各种不同功能,很快会在屏幕后变得非常复杂,前向渲染更适合比较简单的应用程序
擅长渲染半透明表面,延迟渲染则不适合。
动态光照对性能往往有着更大的影响。(注意:虽然传统的前向渲染在光照和阴影上表现糟糕,但现在的UE4渲染中已经完全不是这样了,有许多新技术使得前向渲染,包括前向渲染中的动态光照几乎和延迟渲染达到相同水平)。
可以使用多重采样抗锯齿(MSAA)
![938552bf1817fafb812299ed370d0451.png](https://i-blog.csdnimg.cn/blog_migrate/b35bca68b996b038ff876e1ec19cbaa5.jpeg)
10,前向渲染适合简单应用程序,一旦你开始添加各种功能,应用变得复杂时,性能表现就会一次次降低,做的每件事都可能让性能大幅下降。
而延迟渲染性能在刚开始就会低一截,即便是非常简单的场景,但后期不管你添加什么功能,性能都会相对稳定,不会下降太多。
手机和移动包括VR应用适合采用前向渲染,而高端主机和PC游戏则适合采用延迟渲染。
第二篇:渲染之前和遮挡
1,各个线程之间是同步执行的,CPU和绘制线程之间相差一帧的时间,而CPU和GPU之间则会相差两帧的时间。如果帧率是30fps,那么意味着实际渲染从66毫秒开始,即等待CPU完成以后,中间存在66毫秒的延迟。
![c08dc959d946430e059226b12910599e.png](https://i-blog.csdnimg.cn/blog_migrate/5a6685756c2ff0f1ce3ef71dcbdec537.jpeg)
2,渲染之前需要知道对象的位置,由于是实时渲染对象每帧的位置都可能发生改变。为了做到每帧及时获取对象位置,我们需要计算动画,物理,AI,以及对象消失和隐藏。这些逻辑其实是一个相当复杂的过程。
![d9bc3777bc3bf2df5ba1fae49b4ffda9.png](https://i-blog.csdnimg.cn/blog_migrate/4ba11d813e210fa9dec3f344a8e672a0.jpeg)
3,上述的都是CPU计算完成的,计算完物体的位置以后,传递到绘制线程,绘制线程会告诉我们哪些需要包含到渲染中,哪些不需要,也就是会做剔除操作
4,具体的剔除操作按先后顺序会分四步执行,UE采取4种不同系统来处理剔除操作,这样更高效。
距离剔除
视锥体剔除
预计算可见性
遮挡剔除
![5ea4fc81c50d814f3f675f8cd93177ae.png](https://i-blog.csdnimg.cn/blog_migrate/f8dbdd89f243bca70d4057adce529f33.jpeg)
5 距离剔除默认不会开启,需要手动开启。有两种方式,(1),单独给某个物体指定当前绘制距离。 (2),添加 剔除距离体积,为场景所有物体统一设置
![ec945912728571de3b48c123eed64936.png](https://i-blog.csdnimg.cn/blog_migrate/af53be48e34d54670698d06d1fa112f7.png)
![7d4723b269cadd0199840c75aced2b7e.png](https://i-blog.csdnimg.cn/blog_migrate/d6934271f54e0da5a258a068b9521b9a.jpeg)
6,视锥剔除默认开启,我们能操作的并不多。检查摄像机能看到什么内容,视野之外的一切对象都会自动被剔除。
![eb9d0204ce9c3feed0b0ba0e04081d37.png](https://i-blog.csdnimg.cn/blog_migrate/7bff68b0c7bfc9af449fca59b9b86bce.jpeg)
7,预计算可见性默认情况下也不会开启,需要在世界设置中开启。需要烘培构建才可以生效。单元格的高度可以在lightmass的ini文件中配置。在每个单元格都会记忆哪些对象是可见的,哪些是不可见的。每当相机移动进入不同的单元格都会检测询问判断哪些对象被遮挡,哪些对象该渲染,哪些不该渲染。引擎会告诉它我记得这些可见的,这些是不可见的,每个单元格都会储存一张列表,包含这个位置可以看见的对象,
![7cfa7ae5a3dd677ceba4db0d0bdb1b51.png](https://i-blog.csdnimg.cn/blog_migrate/b6c0edea33af8e12515e1b2d58478d12.png)
![8c66c9875df8a8692c7e892056e4630c.png](https://i-blog.csdnimg.cn/blog_migrate/06666c74c4b22caa01905df276dea1e8.jpeg)
8,遮挡剔除 能精确的计算物体的可见性。遮挡剔除会消耗更多的性能,因为它需要精确计算每个模型的可见性。这里可以用 freezerendering(名字不恰当,虽然是冻结渲染,其实冻结的是遮挡,渲染没有冻结) 命令来查看当前的遮挡剔除情况 unfreezerendering来恢复
![5ebb7907baa6cb9e7894946954ad16d1.png](https://i-blog.csdnimg.cn/blog_migrate/919c0d5e3484a37befb2866cfdbdbd63.jpeg)
9,执行顺序 假设1万个对象,距离剔除性能最好,先进行一遍筛选,假设剔除3000个,紧接着采用视锥体剔除再次筛选,剔除掉3000个假定,然后再采用预计算筛选出1000个,假设遮挡剔除再剔除1000个,最后只剩下1000个在视野中,才执行真正的渲染。 而如果对这1万个对象都直接进行遮挡剔除消耗性能太大,因为它需要每个物体都判断
10, Stat Initviews 统计初始画面, 会显示出遮挡消耗的时间
![ea4603e1a02e482c3bdba2f2124064b1.png](https://i-blog.csdnimg.cn/blog_migrate/e6c8f644c8defd7eb19d9d1bfc9d1644.jpeg)
11,总结归纳
首先设置距离剔除,因为默认是不开启的,应当一直开启,尤其适合室外环境
场景中超过1万或1万5个对象,可能会产生影响。这种影响主要是CPU,也可能是GPU
大型开放场景可能没法很好的应用遮挡,因为我们很容易看到所有对象。如果所有对象都在视野中,我们就没办法使用遮挡。
几乎任何可见对象都能被遮挡,包括粒子这类对象也能被遮挡。事实上粒子通过BoundingBox进行遮挡的,边界盒每秒钟更新若干次,试着查看粒子的边界盒有助于进一步优化。
大型模型很少会被遮挡,并且会增加GPU的开销
将模型拼合在一起会降低CPU的运行效率
如果场景中存在大量小型对象,引擎会执行更多的遮挡检测;但是如果模型很庞大(大量小模型拼合成一个),即便我们眼睛只看到了模型的一小小部分,该模型也不会被剔除,整个模型仍然会被渲染,这样虽然计算遮挡的工作量变少了,但从另一方面渲染的东西变多了。这就是我们需要权衡的地方。你不可能完美实现,两种方案都不是绝对正确,都会有有一些瑕疵,我们要做的就是找到两种方案之间的平衡点,寻找折中通常是我们最后的做法
![5298a9236f3b039f8d81c2e4a5e3024c.png](https://i-blog.csdnimg.cn/blog_migrate/aaa31054eedcb74dd691f12b279bd364.jpeg)
12,当UE有了一张可见性列表后,会传递给GPU进行渲染。
![3c88930fa55651ca135839706617896e.png](https://i-blog.csdnimg.cn/blog_migrate/a40fd2baee3047540167e64bfe59b607.png)
第三篇:几何结构渲染第1部分
1, 重申渲染流程:当CPU已经确定了所有对象的位置(CPU各种逻辑运算),也知道了具体要渲染哪些对象了(绘制线程剔除),可以开始真正的渲染了(GPU渲染开始)
2,Prepass/Earyly Zpass
渲染按模型逐个进行,不会逐像素渲染。
由于存在穿插模型的情况,所以需要依靠前期渲染的深度通道,做一个深度测试。以极其简单的方式渲染几何体。通过这个我们可以知道对象在哪以及如何遮罩对象。
对此能做的不多,只有可以项目设置中有一些修改。
![b0caa9189fc7a8921f48e991ddfc734e.png](https://i-blog.csdnimg.cn/blog_migrate/17006579ed669907285d90ca11c69d9f.jpeg)
![0406d5a61e410b76d9a20816448223bb.png](https://i-blog.csdnimg.cn/blog_migrate/3cb7ede7b2988772813ec959e5cd8bf8.jpeg)
![ed3395ff0865770bd9debeee2cf633c6.png](https://i-blog.csdnimg.cn/blog_migrate/b4f1dba4f01012d66ab6679cb0e43d09.jpeg)
![b1961d372eefcb1a3d4af261699631c7.png](https://i-blog.csdnimg.cn/blog_migrate/e85b8a23e1d3be5c57babe411bd43a34.jpeg)
3,drawcall
一个drawcall,即一次绘制调用,可以理解为拥有相同属性的一组多边形。
几何体会根据绘制调用逐个渲染。
每一个材质,都意味着一次绘制调用。
4,举例
左边需要5次调用(天空,地板和3个圆柱体)。右侧由于最后边一个圆柱体上下两部分材质不同,所以需要6次drawcall;绘制调用就是一组拥有相同属性的多边形,而右侧的柱子拥有不同的属性,因为他们使用了不同的材质。
![8147d8e4239b67ff4070352324a07abc.png](https://i-blog.csdnimg.cn/blog_migrate/852c1560d44679b5e23acfc9d08efacc.jpeg)
6, 渲染顺序
按材质类型对场景对象进行分类,因为这样会更快,否则他需要改变硬件的渲染状态。 这也是先渲染最右侧柱子的原因,因为剩下的柱子材质相同,所以不需要修改渲染状态,节省开销。我们能做的也不多。
![2112581c672e969819a13d4f9859f30e.png](https://i-blog.csdnimg.cn/blog_migrate/295af8ffcc2f5fff85a1cab8010d852a.jpeg)
7, stat RHI 可以看到绘制的三角形数量以及DrawPrimitive的调用次数(drawcall)
stat RHI显示的数量可能和实际不符,因为引擎本身都有一定的基本开销,背后会有很多看不见的步骤。
![60231515b957c6b6954d0e9a1b088ae5.png](https://i-blog.csdnimg.cn/blog_migrate/9a3cd73e1c3887a993a32b73b2a15aaa.jpeg)
8, 总结
stat RHI 能够让我们查看drawcall的数量
2000-3000的drawcall是一个标准
超过5000就有点高了
超过10000就可能会产生问题
对于手机平台,绘制调用要少得多,通常几百次,一般不超1000次
![0c7d198b5c22f74364be88701fd23e58.png](https://i-blog.csdnimg.cn/blog_migrate/cbf05c6c2d2209cccf676849828daf5f.jpeg)
9 ,drawcall对于性能有很大影响。 每次渲染器完成绘制调用时,它都需要接受来自渲染线程的命令,导致损耗的增加。
drawcall的影响在大多数情况下要比多边形面数大得多。
以前人们往往关注的多边形面数,因为当时渲染器还很简单功能不多,主要功能就是计算多边形数量。 但现在的UE引擎包括很多的渲染引擎,以及当今的硬件绘制调用产生的影响要比多边形数量大得多。
![c4ef81790be409493d67ec87c7b8ac67.png](https://i-blog.csdnimg.cn/blog_migrate/c5075f37a6bceff9a0d1815af41031aa.jpeg)
10,类比计算机中复制1个1GB的文件和复制1百万个1kb文件(仍然是1GB)的开销对比,很容易知道1GB文件大小要快很多。复制1百万 个小文件慢的原因是它 需要开始传输文件,然后停下,然后需要询问下一个文件在哪,然后再从硬盘读取出来,然后传输到新的位置。简单理解:每次渲染完都需要询问总部 ,我当前渲染完了,能告诉我下一条信息吗,然后它需要等待,因为需要一段间隔才能找到并传输下一条信息,当它获得下一条信息后,它会重新开始渲染。但我们会遇到 延迟,引擎会渲染一部分内容,然后停下,继续渲染,然后又停下,渲染之间的停顿正是影响性能的原因。
![205756cd37f1b5b6865cc1ea68b5f65d.png](https://i-blog.csdnimg.cn/blog_migrate/68d5f0cd30abe245dbe7fb6bba07fb82.png)
11,这里有个对比关系,一个是一种材质的平面,但面数很多,另一个是面数虽然不多,但材质很多。可以看出drawcall对于性能的影响远远大于三角形面数。
![8482cd77243aff0214fa2d2404c6cc62.png](https://i-blog.csdnimg.cn/blog_migrate/e9d58d019c64b3bee38685fbb2f1233a.jpeg)
![177cf9092993e1076ecf7a7ec93db2d6.png](https://i-blog.csdnimg.cn/blog_migrate/9318b1fd2242347f07d3b901188d6646.jpeg)
![6ba29a538be06daf8d9403c5693d3950.png](https://i-blog.csdnimg.cn/blog_migrate/a6def93c2dadd99a2f68ec80c1a41a0c.jpeg)
![ffdf6c79a46240589f84992d7dd06e3c.png](https://i-blog.csdnimg.cn/blog_migrate/0ddcb9e3b7e1efa4b95c2cc4986d1c4a.jpeg)
第四篇:几何结构渲染第2部分
1,可以通过UE内置插件RenderDoc查看渲染过程以及耗时。
PrePass 预渲染通道,最先完成的环节。
HZB 层级深度遮挡(Hierarchical Z-based occlusion)另一种遮挡方式,负责为阴影深度内容做准备。
BasePass 主要负责渲染几何体,显示层次关系按照材质排序。
左侧事件图表的每一次绘制都是一个drawcall
左侧事件图标的右侧的钟表代表该drawcall消耗的时间,以微秒为单位us.
假如有1万个对象,帧率为60FPS,那么每秒需要渲染 60*1万个对象,对于性能要求还是很高的。
选择其中某个立方体12个三角形,渲染花了5微秒。 而雕像模型2万多个三角形,用了28us,时间并非和多边形数量相关。
事实上找不到低于5毫秒的,因为每个drawcall都有一个基本消耗。当面数低于某个临界点后,数值不会导致时间差异,不管1000的还是2000的,临界点具体多少取绝于硬件但一般1000-4000时间上不会有差异。即使你把面数减少的很低,也不会对drawcall产生多大影响,可能对其他方面比如动态阴影产生影响。
![2cff4343cb8107e275f97c12a9b6b549.png](https://i-blog.csdnimg.cn/blog_migrate/4a5aff634cd4ea63c095fb17ad3b6f88.jpeg)
![ef1cad49e1901c0e79be5c2ffec70bf2.png](https://i-blog.csdnimg.cn/blog_migrate/45980ff799989e9d1756491a5572c1a2.jpeg)
2,组件也会导致绘制调用,如果在蓝图中添加了很多静态网格体那么每个静态网格体仍然需要一次绘制调用,没有什么区别.
组件也是逐个进行遮挡和渲染的,没什么区别。 无论是蓝图中添加组件还是游戏世界中放置对象,没任何区别。组件合并成Actor也一样。
![1a7e7a1e0fe7272276cd636a46e78c0b.png](https://i-blog.csdnimg.cn/blog_migrate/66391768cd63e87cb3e03f710707a64e.jpeg)
3,减少绘制调用常用方法
模块化工作流,当然也可以完整的创建好后再整体导入,当然这样对于遮挡剔除来说很糟糕。虽然要检查的遮挡对象减少了,但是它实际上能遮挡的对象减少了,因此面数会上升,虽说影响不大,但依然会有影响。 光照贴图的情况也会糟糕,光照贴图内存空间有限,光照贴图纹理所占空间有一个最大值,光照贴图分辨率都会有上限。以4K为例,4096贴图已经很大了,但是如果你用了很大的模型,最终他们会用光所有uv空间,这就是问题所在。
碰撞也是另外一个问题,尽管人们不太会考虑到这个问题。其实游戏世界中所有对象都有碰撞体积。例如一个弹跳的小球,碰到了墙边的柱子上,需要检测碰撞的位置,以及接下来的触发,如果柱子是一个独立,则碰撞盒会很小,得到的碰撞信息也就少。但是如果整个房间都是一个整体,那么会获取所有网格的信息,这样会很复杂,但也不会影响太厉害,但是依然会有影响。它也会影响内存。模型越大也就需要更多的内存,因为引擎需要记住整个大模型。 但如果小模型,你加载过一次,不管你复制粘贴了多少次,也是会引起内存增大的,影响流传送和游戏体验。
![631b6e20841c3f0a86df6a05f55fb824.png](https://i-blog.csdnimg.cn/blog_migrate/f61d3e3e3fb932614fe1cdae894eb234.jpeg)
![3516e7c9e00cb0fa2ef80466274c0b38.png](https://i-blog.csdnimg.cn/blog_migrate/9b6cb383000b51decb14e5f059b55a9a.jpeg)
4,大的模型会减少drawcall,小而多的模型则会节约工作流,减少lightmap大小,提高剔除,减少碰撞消耗,但是会增加drawcall。
![20525c548ab7b97d0ee04e7b737068b5.png](https://i-blog.csdnimg.cn/blog_migrate/07b9624d8959b4f81b025877b73cae74.jpeg)
5,一般工作流程
开始搭建的时候先保留分散的小物件,加快开发效率,快要成型的时候再考虑合并。
即便合并,也只合并影响大的物件,而且使用频繁的(可以通过Statics查看。
一旦网格合并了,无法撤销,所以需要谨慎操作。
![39b2814f304c2352d8bf941619f3683e.png](https://i-blog.csdnimg.cn/blog_migrate/438a13934cadd291b24255767675f055.jpeg)
![a431d3ea961c459511c117c7debcb62b.png](https://i-blog.csdnimg.cn/blog_migrate/8f17644d04525154a7d339f6270855ad.jpeg)
![982d7df6484d5b968ba63570b09ff496.png](https://i-blog.csdnimg.cn/blog_migrate/40f245ecfa34238ce8d34c9837caf6a4.jpeg)
![accbdb695db6b3874adc035168763efe.png](https://i-blog.csdnimg.cn/blog_migrate/5779d4161c497a509ab3aa9ae1d19733.jpeg)
6,合并网格的考虑点:
对使用频繁的,面数很低的进行合并
只合并同一区域内的网格体,假设有6个房间不要将第1个房间的和第6个房间的合并,划不来,可以将同一房间的或者区域内的合并
每一种材质都意味着绘制调用,所以仍然会有相同的绘制调用次数,合并这些网格。
缺少碰撞,或者碰撞体积很简单的适合合并。
非常小或者只接受动态光照的网格体,如果网格体很小,我们不要担心光照贴图大小,如果只接受动态光照更是如此。
远距离几何体合并。因为遮挡在远距离时也不能起到很好的作用,所以远距离物体应尽可能合并。
![49c05d07242234ec5a651937f04baed2.png](https://i-blog.csdnimg.cn/blog_migrate/307a42f05bb90fd5394e4c8b30ebeb16.jpeg)
7,合并后的网格可以导出引擎,导入其他三维软件再操作。
![a14a7ce385010fa5ae498c38b3d23df7.png](https://i-blog.csdnimg.cn/blog_migrate/0443df2ad9687dd83562807b04a259e4.jpeg)
8,合并多少以及如何合并很大程度上取决于很多因素,在合并时要衡量取得的效益和合并花费的成本。例如你的目标帧率是30fps,现在游戏35fps在目标帧率之上,你可以花费一周检查优化让所有环节更有效率,使其帧率提升到45fps,也可以花费这一周的时间用在项目的其他方面,总之自己衡量。
和其他情况一样,在极端情况下寻找折中才是最好的方法。
![6b3bf38137f3f071bd151a61580eb31f.png](https://i-blog.csdnimg.cn/blog_migrate/e7ccffb29ba0d4d7cafc650912f4c43c.jpeg)
9,实例化静态网格,Instance Static Mesh
默认情况下UE4中的网格体会实例化并保存在内存中,但他们不会在渲染时实例化,这是有区别的内存中实例化意味着如果导入模型它只在内存中存在一次,拖入场景中两次,但内存依然是一份,但是它会渲染两次。模型会在内存中实例化,并不会在渲染时实例化但如果你要进行实例化渲染,它会在两者中都实例化。
进行实例化渲染,只需要将物体添加到实例化静态网格组件中,并按组进行渲染,在实例化以后仿佛就是一个单一模型,有多个对象那么大,事实上仍是一个模型,这样就只需要一次绘制调用,1个drawcall,在api底层其实调用的是drawelementinstance的方法(opengl)。
由于用户界面稍微有点复杂,不太适合在日常工作中对所有对象使用。
另一个原因为什么不对所有对象这么做,默认没有开启的原因,同样也是引擎默认不这么做的原因:因为实例渲染要求每次询问渲染器,当前的和上一个是否一样,意味着每渲染一个对象,都需要检查一下,这个一样吗哪个一样吗等等,如果有大量物体的化,损耗更大相比渲染它而言这也是需要手动指定实例化静态网格的原因,这样就会告诉UE4一个简短列表,执行这个列表里的比较,而不要比较整个场景,让过程变得更快。
UE4 对于植被默认采用的是这种实例化静态网格渲染方式。
实例化网格渲染方式可以渲染海量对象,几乎没有性能损耗。但是不适合于孤立少数的对象,场景中的少数几个没有必要采用,反而会得不偿失。
![ef28bd2ac305f56cc316eb74e9e9d9a8.png](https://i-blog.csdnimg.cn/blog_migrate/2b4108bc02b216152d3af9d62f902ca5.jpeg)
10,LOD细节层次
确保每一个LOD面数是前一个LOD面数的50%或者更多,两者之家差别必须大到足以抵消为了使用LOD所占用的额外内存
还有一种LOD,分级LOD(HLOD:HierarchicalLOD),因为常规LOD有一个问题,虽然很好用,但之减少多边形数量,drawcall不会减少,而HLOD则可以解决此问题。 HLOD是对对象编组,在远距离上采用一个单一模型,替换这一组对象,而近距离观察的时候你仍然看到4个单独模型。 说白了就是当距离较远时会被一个合并后的低面数模型取代,绘制调用和面数会同时减少。
可以通过分层LOD大纲视图或其他工具设置LOD,UE会生成集群,会在这些集群中自动创建网格体来替换集群内的模型
请务必在室外环境中使用LOD,大型的开放式环境需要使用LOD,你需要距离剔除,可能还需要HLOD
![b6ef2d434f6650b7d325b285c5970ebd.png](https://i-blog.csdnimg.cn/blog_migrate/ad5cc59377c649c245ec96703322b234.jpeg)
![e7caceea6bfa19df5d99326b43b085cd.png](https://i-blog.csdnimg.cn/blog_migrate/d0aba1eb785a8c06d884400ad5b014d6.png)
![9cf49431042b3417c4b8d0a34e184ab5.png](https://i-blog.csdnimg.cn/blog_migrate/6a7cd2c59e15447513c5177a08503d72.png)
第五篇:几何结构渲染第3部分
1,有许多不同的着色器,包括顶点着色器,像素着色器,整体着色器(whole shaders),域着色器(Domain shaders) .着色器可以理解为一小段程序,输入变量,然后输出不同的变量。
2,着色器非常高效,所以UE要引入。CPU这样的硬件不足以处理大量的运算,需要依靠GPU强大的并行运算能力
3,顶点着色器负责三件事
将本地坐标的顶点位置转换成世界坐标,你对此不能优化它,它就是这样。
处理着色,处理硬边缘和柔化边缘以及顶点的颜色。在DX9或DX11出来前还会处理光照阴影之类的。
应用额外偏移,材质里的WorldPositionOffset属性。 但是这里需要注意的是 CPU压根不知道这件事,所以在计算碰撞的时候还是会在原位置计算。
4,顶点着色器作用
顶点着色器可以偏移布料的世界坐标,布料是由顶点着色器驱动的,包括水面的位移,水面的波浪,也是顶点着色器驱动的,还有常见的植被。
这里考虑下对于草地无法应用顶点动画原因:我们不可能对其运用骨骼和骨架,这意味着CPU需要计算所有位置,想象一下一片森林,几百万个顶点,不可能让几百万个顶点每帧都使用动画。但是顶点着色器可以做到这一点,因为着色器做了优化。
我们只是将模型渲染到了其他位置,CPU根本不知道模型发生了位移 ,诸如物理和碰撞之类的不会受任何影响。可以看到虽然雕像通过着色器偏移到了上方,但是碰撞盒子没动(碰撞是由CPU执行的),CPU并不知道模型被移动过,所以玩家是过不去的。其实CPU甚至也不可能知道,即便GPU回头告诉CPU,但那也是过去式了,因为CPU在流水线中先行一步。所以请记住,顶点着色器的偏移只能将它用于视觉效果。你不能将顶点着色器精确定位或者触发其他内容(例如物理效果)。旗帜也非常适合用顶点着色器。
![5fea7e8d4c38524f9395d22705fb769c.png](https://i-blog.csdnimg.cn/blog_migrate/0b02f5d046c0cc750ad255dcb576a672.jpeg)
![ca4dde47ca199ab695e3940f0348ffc2.png](https://i-blog.csdnimg.cn/blog_migrate/2fce2fc55eab1c9005eab0d254875914.jpeg)
![a532e5d9adccb7ee04f8fbc56254148d.png](https://i-blog.csdnimg.cn/blog_migrate/e8bccb90c8d3cd664c86f314cbc8027b.jpeg)
5,记忆点
动画越复杂,效率就越低下。
模型的顶点数量越多,当然效率也会越低下
高面数对象最好使用简单的顶点着色器。
禁用远距离世界坐标偏移。 因为距离很远的时候,即便物体在动,也不一定能看得到。尝试在远距离尽可能的禁用动画,特效和偏移等等,节省性能。
![b388f5ac209e2850bf79f04cbdb80a9d.png](https://i-blog.csdnimg.cn/blog_migrate/af5fcd8c6476ed01c43d236971f50239.jpeg)
第六章:光栅化,遮蔽和GBuffer
1,光珊化指的是已知模型渲染成图像,GBuffer是我们渲染出来的不同图像,不单单是一张图像而是一组图像,供我们之后合成使用。
2,为了渲染信息,会使用很多像素格,像素是正方形,意味着整张图像其实是一张大网格。假定蓝色代表三角形,必须找出三角形背后的哪些像素格会因为这个三角形着色,这个计算结果数学上称之为 光栅化过程,光栅化按照绘制调用逐次执行,我们无需操作,过程发生在后台。
![c4620d429b059bb54432c2a5b6abfd71.png](https://i-blog.csdnimg.cn/blog_migrate/39c08991102efd15a09b9a458b6cdf40.jpeg)
3,我们可以控制的是过度着色。过度着色和光栅化密切相关。 每个像素点只能同时显示一次,理解过度着色前一定要理解这一点,一个像素点永远只会同时用于显示一个多边形,可能是多边形的一部分,也可能是整个多边形,但是一个像素显示的多边形永远不会超过一个,这是过度着色的核心基础。
4,我们可以确信一个像素所表示的多边形永远不会超过1个。如下图所示,我们可能觉得颜色会混合在一起,以为像素点显示的是不同多边形混合的颜色,其实不然,他们颜色会泾渭分明,不会混合到一起。不同三角形的像素点会清晰的划分开来。 如果你有10万个多边形的对象,从很远的距离观察只会看到它呈一个像素点的大小。你实际上只会看到一个多边形,但是你要处理10万个多边形,所以从性能上看这样毫无意义。仍然要指出,你其实只会看到一个多边形。 当UE4渲染这样的像素时,它们使用像素着色器来精确计算着色。
![7b7b16a359186bafaaf9d6e1a9891eaa.png](https://i-blog.csdnimg.cn/blog_migrate/610e966ad9e362d4429dd5b3328f1298.png)
5,着色意味着必须给它上色或者赋予颜色,纹理,材质等等都会影响颜色,因此那个多边形必须告诉我们上面是什么样的纹理,任何关于顶点着色的信息,是否有光滑边缘或者类似信息,顶点颜色等等,任何我们需要的信息都应该告诉我们
![b2de1b9738979324cf36af315bfcb6b9.png](https://i-blog.csdnimg.cn/blog_migrate/c36d80e89026bf102d0c7ca19e3ab969.jpeg)
6,通过三角形讲过度着色(核心)。由于硬件设计的原因,它总是使用一个2*2像素方格单位来进行处理,我们不是逐像素处理。 这样的化如果一个多边形非常细小或者很薄,它可能会对4个像素着色,而实际上只有一个像素会显示颜色,这是第一次过度着色
![b132f45166e9f57d65c079403963a4b3.png](https://i-blog.csdnimg.cn/blog_migrate/eec3f7fe1e53ec7ba8de39bd3c15060f.jpeg)
7,如下图所示,绿色和蓝色部分的5个像素会被填充颜色,也是我们最终看到的颜色区域,但它会导致像素重新计算。 当我们绘制右侧三角形的时候,上边的4个和下边的4个都会被再次计算一遍,这就是第二次过度着色
![5d9e27149a489dd710c0328dcbc15b7a.png](https://i-blog.csdnimg.cn/blog_migrate/8ae5727f9927118ca16d16f043e679d8.jpeg)
![405e44b7f38666889faad843b4b5418e.png](https://i-blog.csdnimg.cn/blog_migrate/8da12e761c5305b3c4ddb6a6fdf016fb.jpeg)
8,一次只是对可见像素以外的其他像素着色,第二次发生在其他多边形和模型,覆盖相同的4像素区域的时候,它们会再次处理这些像素,这里我们有5个像素被填充了颜色,但是实际上计算了5组4像素方格也就是20个像素。5个像素可见计算了20个像素,计算了4遍。这就是过度着色,也是它背后的原理。
9,打开UE的查看Overdraw的方法,越红色的区域代表overdraw越严重。远距离观察对象实际上渲染速度更慢,因为会有更多的多边形覆盖更小范围的像素区域。
![c0715d08645ec67eb2c5bb935e08667b.png](https://i-blog.csdnimg.cn/blog_migrate/5f47b3ed6fdb1df22796836a52f7a631.jpeg)
10,总结
多边形如果越密集渲染起来损耗就越大
从远处看密度会自动增加
因为这个球体的多边形数量始终一致,但是显示相同数量多边形的像素变得更少了,从远处观察的时候每个像素的多边形密度会增加
你应该使用LOD或者剔除在远距离上减少多边形数量来客服这点问题
初始通道着色器越复杂,过度着色损耗就越大,前向渲染中像素着色通道损耗更大,因此过度着色对前向渲染效率产生的影响比延迟渲染更大。延迟渲染中过度着色会增加损耗,但并不会太大。这也意味着非常薄的三角形会对过度着色产生影响,因为它们会穿过许多个2*2的像素方格。
如下第二张图最坏的情况,原因是过度着色,你要沿着所有多边形的整条边长过度着色,中间会发生巨量的过度着色,因为中间有太多的重叠。所以尽量不要使用,而要尽可能将多边形均匀的分布在几何体上。之所以现在即便那样做了感受不到性能影响,那是因为现在的硬件太强大了,但是移动平台还是要考虑的。
![2dd3e508ce3f0fa725aedd864ba9bf6d.png](https://i-blog.csdnimg.cn/blog_migrate/838e7be4d94cc8b62e1ca510aee1fb2e.jpeg)
![9819f5034efc9accb049148457569515.png](https://i-blog.csdnimg.cn/blog_migrate/b507ad0c1bede1c8c32f03509f083921.jpeg)
11,光栅化以后会渲染图像,会进行GBuffer的操作。GBuffer用于合成各种内容,包括光照材质雾等等。接下来的计算完全是GBuffer图片的操作而不再是具体的几何体,说白了可以说是Photoshop的处理。
![77e88a8b41a0a8ea8d1c3355a8f67388.png](https://i-blog.csdnimg.cn/blog_migrate/d2de9a1f5697cca48116bc07ff438b26.jpeg)
12,通过查看RenderDoc里的GBuffer信息,包含SceneColor光照图(静态Lightmap信息),WorldNormal图,混合图(R:金属度,G:高光,B:粗糙度),AlbedoColor图,标记不同材质特性(皮肤,头发等)的遮罩图,DepthSceneZ深度图
![17589375eb967491a4e3a189a103106c.png](https://i-blog.csdnimg.cn/blog_migrate/917a18fa6952642759a4eb267b4d383e.jpeg)
13,自定义深度概念。自定义深度会用单独的渲染目标或者说G缓存来包含模型,然后可以用于各种特效,例如轮廓特效等等。通过勾选截屏工具面板中自定义深度,可以将对象从环境中剥离出来。甚至可以勾选"包含缓冲区可视化目标"将GBuffer内容全部保存下来,然后再输出到Nuke等其他合成软件中,进行图像编辑。
![af9ad426e433e56e736fd5786a127a54.png](https://i-blog.csdnimg.cn/blog_migrate/6530ba4d22899fff3dd58c595e899f81.jpeg)
14,可以通过窗口查看GBuffer
![e3a9d7d463fe959177b51210ebfbae6f.png](https://i-blog.csdnimg.cn/blog_migrate/0490081107851dca43bf3daa3e96e330.jpeg)
15,总结: 对于GBuffer就是这样,我们做不了太多,GBuffer确实会影响性能,但是是以一种非常稳定的方式总是消耗同等的性能。GBuffer会占用很多内存,如果想把GBuffer扩展更多的内存用于其他特效,一定要考虑内存上限,引擎会存在限制。 基本上每帧要渲染10张不同的画面,如果帧率是60fps,基本每秒要渲染600帧,而不是60帧。因为你要为每帧额外渲染X张G缓存图像,这些图像都未经压缩,必须传输给硬件中的GPU. 考虑巨量的带宽量,每秒传输600张未经压缩的图像,所以持续以60fps的帧率运行,消耗还是很大的。
本篇完