0x00 前言

我想很多开发游戏的小伙伴都希望自己的场景内能渲染越多物体越好,甚至是能同时渲染成千上万个有自己动作的游戏角色就更好了。

但不幸的是,渲染和管理大量的游戏对象是以牺牲CPU和GPU性能为代价的,因为有太多Draw Call的问题,如果游戏对象有动画的话还会涉及到cpu的蒙皮开销,最后我们必须找到其他的解决方案。那么本文就来聊聊利用GPU实现角色的动画效果,减少CPU端的蒙皮开销;同时将渲染10,000个带动画的模型的Draw Call从10,000+减少到22个。(模型来自:RTS Mini Legion Footman Handpainted

0x01 Animator和SkinnedMeshRender的问题

正常情况下,大家都会使用Animator来管理角色的动画,而角色也必须使用SkinnedMeshRender来进行渲染。

例如在我的测试场景中,默认情况下渲染10,000个带动作的士兵模型,可以看到此时的各个性能指标十分糟糕:CPU 320+ms,DrawCall:8700+。

因此,可以发现如果要渲染的动画角色数量很大时主要会有以下两个巨大的开销:

  • CPU在处理动画时的开销。

  • 每个角色一个Draw Call造成的开销。

CPU的这两大开销限制了我们使用传统方式渲染大规模角色的可能性。因此一些替代方案——例如广告牌技术——被应用在这种情况下。但是实事求是的说,在这种情境下广告牌技术的实现效果并不好。

那么有没有可能让我们使用很少的开销就渲染出大规模的动画角色呢?

其实我们只需要回过头看看造成开销很大的原因,解决方案已经藏在问题之中了。

首先,主要瓶颈之一是角色动画的处理都集中在CPU端。因此一个简单的想法就是我们能否将这部分的开销转移到GPU上呢?因为GPU的运算能力可是它的强项。

其次,瓶颈之二是CPU和GPU之间的Draw Call问题,如果利用批处理(包括Static Batching和Dynamic Batching)或是从Unity5.4之后引入的GPU Instancing就可以解决这个问题。但是,不幸的是这两种技术都不支持动画角色的SkinnedMeshRender。

那么解决方案就呼之欲出了,那就是将动画相关的内容从CPU转移到GPU,同时由于CPU不需要再处理动画的逻辑了,因此CPU不仅省去了这部分的开销而且SkinnedMeshRender也可以替换成一般的Mesh Render,我们就可以很开心的使用GPU Instancing来减少Draw Call了。

0x02 Vertex Shader和AnimMap

写过shader的小伙伴可能很清楚,我们可以很方便的在vs中改变网格的顶点坐标。因此,一些简单的动画效果往往可以在vs中实现。例如飘扬的旗帜或者是波浪等等。

(来源于bing搜索)

那么我们能否利用vs设置顶点坐标的方式来展现我们的角色动画呢?

答案当然是可行。只不过和飘扬的旗帜那种简单的效果不同,这次我们不仅仅利用几个简单的vs的属性来实现动画效果,而是将角色的动画信息烘焙成一张贴图供vs使用。

简单来说,我们按照固定的频率对角色动画取样并记录取样点时刻角色网格上各个顶点的位置信息,并利用贴图的纹素的颜色属性(Color(float r, float g, float b, float a))保存对应顶点的位置(Vector3(float x, float y, float z))。当然利用颜色属性保存顶点的位置信息时需要考虑到一个小问题,在下文我会再说。

这样该贴图就记录了整个动画时间内角色网格顶点在各个取样点时刻的位置,这个贴图我把它称为AnimMap。

一个AnimMap的结构就是下图这样的:

在实际工程中,AnimMap是这个样子的。水平方向记录网格各个顶点的位置,垂直方向是时间信息。

上图是将角色的Animator或Animation去掉,将SkinnedMeshRender更换为一般的Mesh Render,只使用AnimMap利用vs来随时间修改顶点坐标实现的动画效果。

到这里我们就完成了将动画效果的实现从CPU转移到GPU运算的目的,可以看到在CPU的开销统计中已经没有了动画相关的内容。但是在渲染的统计中,Draw Call并没有减少,此时渲染8个角色的场景内仍然有10个Draw Call的开销。因此下一步我们就来利用GPU Instancing技术减少Draw Call。

0x03 效果不错的GPU Instancing

除了使用批处理,提高图形性能的另一个好办法是使用GPU Instancing(批处理可以合并不同的mesh,而GPU Instancing主要是针对同一个mesh来的)。

GPU Instancing的最大优势是可以减少内存使用和CPU开销。当使用GPU Instancing时,不需要打开批处理,GPU Instancing的目的是一个网格可以与一系列附加参数一起被推送到GPU。要利用GPU Instancing,则必须使用相同的材质,并传递额外的参数到着色器,如颜色,浮点数等。

不过GPU Instancing是不支持SkinnedMeshRender的,也就是正常情况下我们带动画的角色是无法使用GPU Instancing来减少Draw Call的,所以我们必须先完成上一小节的目标,将动画逻辑从CPU转移到GPU后就可以只使用Mesh Render而放弃SkinnedMeshRender了。

很多build-in的shader默认是有开启GPU Instancing的选项的,但是我们利用AnimMap实现角色动画效果的shader显然不是build-in,因此需要我们自己开启GPU Instancing的功能。

#pragma multi_compile_instancing//告诉Unity生成一个开启instancing功能的shader variant
...
struct appdata
{
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID//用来给该顶点定义一个instance ID
} v2f vert(appdata v, uint vid : SV_VertexID)
{
UNITY_SETUP_INSTANCE_ID(v);//让shader的方法可以访问到该instance ID
...
}

使用GPU Instancing之后,我们渲染10,000个士兵的Draw Call就从10,000左右降低到20上下了。

当然,关于GPU Instancing的更多内容各位可以在文末的参考链接中找到。

0x04 颜色精度和顶点坐标

还记得之前我说过在利用贴图的纹素的颜色属性保存对应顶点的位置时需要考虑到的一个小问题吗?

是的,那就是颜色的精度问题。

由于现在rgb分别代表了坐标的x、y、z,因此rgb的精度就要好好考虑了。例如rgba32,每个通道只有8位,也就是某一个方向上的位置只有256种可能性,这对位置来说是一个不好的限制。

那么有没有解决方案呢?

当然还是有的。既然这是一个和颜色的精度相关的问题,那么最简单的方案就是增加精度。例如在写本文的时我的Demo就是采用的这种方式,我使用了RGBAHalf这种纹理格式,而它的精度是每个通道16bit。当然,移动平台上渲染大量角色的需求往往对动画的精确程度的要求没有那么高,因此8bit的精度问题应该也不大。

完整的项目可以到这里到这里下载:

Render-Crowd-Of-Animated-Characters

ref:

【1】GPUInstancing

【2】How to take advantage of textures in the vertex shader

【3】GPU Gems 3 Chapter 2. Animated Crowd Rendering

【4】题图来自《全面战争:阿提拉》

各位如果觉得有趣的话,欢迎点个赞。

-EOF-

最后打个广告,欢迎支持我的书《Unity 3D脚本编程》

欢迎大家关注我的公众号慕容的游戏编程:chenjd01

利用GPU实现大规模动画角色的渲染的更多相关文章

  1. 利用GPU实现大规模动画角色的渲染(转)

    原文: https://www.cnblogs.com/murongxiaopifu/p/7250772.html 利用GPU实现大规模动画角色的渲染 0x00 前言 我想很多开发游戏的小伙伴都希望自 ...

  2. 利用GPU实现无尽草地的实时渲染

    0x00 前言 在游戏中展现一个写实的田园场景时,草地的渲染是必不可少的,而一提到高效率的渲染草地,很多人都会想起GPU Gems第七章 <Chapter 7. Rendering Countl ...

  3. 利用GPGPU计算大规模群落仿真行为

    0x00 前言 在今年6月的Unite Europe 2017大会上 Unity 的CTO Joachim Ante演示了未来Unity新的编程特性--C# Job系统,它提供了编写多线程代码的一种既 ...

  4. 利用GPU实现翻页效果(分享自知乎网)

    https://zhuanlan.zhihu.com/p/28836892?utm_source=qq&utm_medium=social 首发于Runtime 写文章 利用GPU实现翻页效果 ...

  5. "Javascript高性能动画与页面渲染"笔记

    前言:好久没翻阅我的gmail邮箱了,午休时就打开看了一下,看到InfoQ推荐的一篇名为“Javascript高性能动画与页面渲染”文章,粗略的看了一下,很赞!讲的很详细,对好些细节讲的都很好,很通俗 ...

  6. Android利用温度传感器实现带动画效果的电子温度计

    概述 Android利用温度传感器实现带动画效果的电子温度计. 详细 代码下载:http://www.demodashi.com/demo/10631.html 一.准备工作 需要准备一部带有温度传感 ...

  7. 我的Android进阶之旅------>Android利用温度传感器实现带动画效果的电子温度计

    要想实现带动画效果的电子温度计,需要以下几个知识点: 1.温度传感器相关知识. 2.ScaleAnimation动画相关知识,来进行水印刻度的缩放效果. 3.android:layout_weight ...

  8. Mysql 5.7.18 利用 MySQL proxies_priv(模拟角色)实现类似用户组管理

    利用 MySQL proxies_priv(模拟角色)实现类似用户组管理 角色(Role)可以用来批量管理用户,同一个角色下的用户,拥有相同的权限. MySQL5.7.X以后可以模拟角色(Role)的 ...

  9. js课程 5-14 js如何实现控制动画角色走动

    js课程 5-14 js如何实现控制动画角色走动 一.总结 一句话总结:首先是onkeydown事件,然后是改变元素的left和top属性 1.常用键盘事件有哪些? • onkeydown和 onke ...

随机推荐

  1. ES 2015/6 新特性汇总

    ES 2015/6 新特性汇总 箭头函数 箭头函数,通过 => 语法实现的函数简写形式,C#/JAVA8/CoffeeScript 中都有类似语法.与函数不同,箭头函数与其执行下文环境共享同一个 ...

  2. 由SpringMVC中RequetContextListener说起

    零.引言 RequetContextListener从名字结尾Listener来看就知道属于监听器. 所谓监听器就是监听某种动作,在其开始(初始化)和结束(销毁)的时候进行某些操作. 由此可以猜测:该 ...

  3. JVM中class文件探索与解析(一)

    一直想成为一名优秀的架构师的我,转眼已经工作快两年了,对于java内核了解甚少,闲来时间,看看JVM,吧自己的一些研究写下来供大家参考,有不对的地方请指正. 废话不多说,一起来看看JVM中类文件是如何 ...

  4. win7中python3.4下安装scrapy爬虫框架(亲测可用)

    貌似最新的scrapy已经支持python3,但是错误挺多的,以下为在win7中的安装步骤: 1.首先需要安装Scrapy的依赖包,包括parsel, w3lib, cryptography, pyO ...

  5. windows下python连接oracle数据库

    1.首先安装cx_Oracle包2.解压instantclient-basic-windows.x64-11.2.0.4.0.zip到c:\oracle3.拷贝instantclient_11_2下所 ...

  6. bootstrap-datepicker的bug:有日期输入的地方在下个月页面选择当天日期会以当天日期减少一个月显示

    Bug复现详细描述:先选择今日日期,然后点击下个月的某个日期,注意,是直接点击下个月的某个日期,不能通过日期显示tab的下个月箭头进入下个月再来点击某个日期,然后再直接点击本月的今日日期.然后bug会 ...

  7. 【Android Developers Training】 18. 重新创建一个Activity

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  8. 【Android Developers Training】 72. 缩放一个视图

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  9. 怎么使用CURL传输工具发送get或者post指令

    1.先下载CURL,见网盘 2.使用,可以直接到doc,cd到curl.exe目录,然后执行 或者用脚本 Set exeRs = WshShell.Exec("curl.exe -F &qu ...

  10. 构建混合应用方式之WCF中继

    使用VPN或者ER服务建立云服务和本地服务网络通道来搭建混合应用的方式,需要网络设备的配合和比较复杂的网络配置,所以不是特别的方便.如果是不希望对本地网络环境做修改,而只是服务层面的混合,那么可以使用 ...