GPU Animation-02-实现
「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。
作为一名优秀的开发者,对于技术的探求应该是永无止步的,如何让自己更进步,那就只有不停的学习,不停的充电.而这些都都是说起来容易做起来难,坚持才是难的地方 --蛙哈哈
本人学习的这个方案是一个开源项目,项目地址:github.com/chenjd/Rend…,咱们可以共同学习一下.
核心就是按照一定的频率对角色动画进行采样(记录每一刻所有顶点的位置信息),并利用纹理的纹素的颜色属性,将顶点数据记录在纹理上面.
最终可以绘制出这样一张纹理,这张纹理上的横坐标代表每一个顶点的位置,纵坐标代表时间,就可以通过一个二维的数组将一个动作的所有顶点位置都记录下来.
接下来就看一下如何将一个正常的角色模型,通过烘焙工具,装换为GPU Animation 模型.
项目中集成了一个工具叫做 AnimMapBake,将一个模型拖入角色选择框,并选择要输出的类型,工具就可以自动获取角色身上的组件信息,自动进行烘焙.
最终一个模型需要输出三个东西,分别是:
- 记录了所有顶点信息的纹理
- 将动画纹理与角色纹理相结合的材质球
- 将SkinnedMeshRenderer替换为MeshRenderer的并添加了新生成的材质球的模型
最后生成的模型,在Vertex Shader和GPU Instancing的加持下,就可以进行万人同屏不卡.
先说几个核心的模块
private void BakePerAnimClip(AnimationState curAnim)
{
var curClipFrame = 0;
float sampleTime = 0;
float perFrameTime = 0;
curClipFrame = Mathf.ClosestPowerOfTwo((int)(curAnim.clip.frameRate * curAnim.length));
perFrameTime = curAnim.length / curClipFrame; ;
var animMap = new Texture2D(_animData.Value.MapWidth, curClipFrame, TextureFormat.RGBAHalf, true);
animMap.name = string.Format($"{_animData.Value.Name}_{curAnim.name}.animMap");
_animData.Value.AnimationPlay(curAnim.name);
for (var i = 0; i < curClipFrame; i++)
{
curAnim.time = sampleTime;
_animData.Value.SampleAnimAndBakeMesh(ref _bakedMesh);
for(var j = 0; j < _bakedMesh.vertexCount; j++)
{
var vertex = _bakedMesh.vertices[j];
animMap.SetPixel(j, i, new Color(vertex.x, vertex.y, vertex.z));
}
sampleTime += perFrameTime;
}
animMap.Apply();
_bakedDataList.Add(new BakedData(animMap.name, curAnim.clip.length, animMap));
}
curAnim.clip.frameRate(对关键帧进行采样的帧率) * curAnim.length(该动画剪辑的长度(以秒为单位)) = 总帧数
ClosestPowerOfTwo 这个方法的意思是: 返回最接近此参数的2的幂的值.
Debug.Log(Mathf.ClosestPowerOfTwo(7)); // prints 8
Debug.Log(Mathf.ClosestPowerOfTwo(19)); // prints 16
curAnim.length / curClipFrame = 每一帧动画的间隔时间
for循环总帧数,获取每一帧动画时,顶点的位置信息,并将顶点信息记录在纹理上.