当前位置:   article > 正文

GPU 加速动画渲染方案_gpuanimator

gpuanimator

GPU 加速动画渲染方案

GPU Animator 解决的问题

同屏动画太多导致的 CPU 蒙皮计算压力太大。比如 MOBA 类游戏的几十个小兵,或者竞技场周围的吃瓜群众等等,
这些动画一般不需要很好的效果,可以尝试使用多种优化手段来降低效果(也可以保持效果基本不变)、占用内存、消耗GPU 来降低 CPU 的压力。

GPU Animator 原理

有两种 GPU 加速模式:

  1. 缓存每一帧顶点坐标,顶点着色器根据帧数和顶点编号获取顶点数据进行渲染,此为方案 A;
  2. 缓存每一帧骨骼变换矩阵,顶点着色器计算蒙皮,此为方案 B;

方案A

Step1 编辑器每帧运行 AnimationClip,获取转换后的顶点坐标保存到数组中,根据数组长度计算需要的 Texture 最小尺寸,(Texture用来保存顶点数据)。

  1. private void CalculateTextureSize(ref int textureSize, int totalCount)
  2. {
  3. textureSize = 2;
  4. while (textureSize * textureSize < totalCount)
  5. {
  6. textureSize = textureSize << 1;
  7. }
  8. }

然后把顶点数据 xyz 当成 rgb,保存到 Texture 中,保存成引擎可以支持的任意资源格式都可以。

Step2 运行时,设置我们想要播放动画的速度,Update 驱动帧 CurrentFrame,计算顶点的开始 Index,设置给 Shader,

  1. //因为每一帧都要记录所有定点的位置,所以顶点的开始Index就是totalVerts * currentFrame
  2. currentPixelIndex = totalVerts * currentFrame;

Step3 Shader 渲染部分,

SV_VertexID,我们用他来区分每一个顶点,加上我们上一步计算的出来的 PixelIndex,就是我们 Vertex 的坐标,如何转成 UV 呢?
根据我们顶点保存的规则,来计算,很简单,如下

  1. inline float4 getUV(float startIndex)
  2. {
  3. float y = (int)(startIndex / _SkinningTexSize);
  4. float u = (startIndex - y * _SkinningTexSize) / _SkinningTexSize;
  5. float v = y / _SkinningTexSize;
  6. return float4(u, v, 0, 0);
  7. }

获取 UV 之后,根据 UV 坐标取我们计算好缓存起来的 vertex 坐标输出到片段着色器中。

  1. VertexOutput vert(VertexInput input, uint vid : SV_VertexID)
  2. {
  3. VertexOutput output;
  4. UNITY_SETUP_INSTANCE_ID(input);
  5. float startPixelIndex = _StartPixelIndex + vid;
  6. float4 uv = getUV(startPixelIndex);
  7. float4 vertex = tex2Dlod(_SkinningTex, uv);
  8. output.vertex = UnityObjectToClipPos(vertex);
  9. output.uv = input.uv;
  10. return output;
  11. }

这个方案:

  1. 优点几乎不占用cpu和gpu消耗;
  2. 缺点动画文件体积较大(30帧左右,大概3M大小);

方案B

Step1 类似方案A,记录每一帧的关节 matrix,使用三个 rgb 来保存 matrix 的三行。

  1. meshTexturePixels[pixelIndex] = new Color(matrix.m00, matrix.m01, matrix.m02, matrix.m03);
  2. pixelIndex++;
  3. meshTexturePixels[pixelIndex] = new Color(matrix.m10, matrix.m11, matrix.m12, matrix.m13);
  4. pixelIndex++;
  5. meshTexturePixels[pixelIndex] = new Color(matrix.m20, matrix.m21, matrix.m22, matrix.m23);
  6. pixelIndex++;

最后保存到 Texture 里,不一样的一点是,需要保存一个新的 Mesh 文件,因为骨骼的顺序必须和我们保存的 Texture 对应上。

Step2
perFramePixelsCount,上文我们讲了需要三个像素。totalJoints,是我们Mesh的总关节数。同样的,Update 驱动帧 CurrentFrame。

currentPixelIndex = pixelsStartIndex + totalJoints * currentFrame * perFramePixelsCount;

Step3 Shader 渲染部分,
根据设置的 StartPixelIndex,取Matrix,然后计算顶点、法线等

  1. inline float4x4 getMatrix(float startIndex)
  2. {
  3. float4 row0 = tex2Dlod(_SkinningTex, getUV(startIndex));
  4. float4 row1 = tex2Dlod(_SkinningTex, getUV(startIndex + 1));
  5. float4 row2 = tex2Dlod(_SkinningTex, getUV(startIndex + 2));
  6. return float4x4(row0, row1, row2, float4(0, 0, 0, 1));
  7. }
  8. VertexOutput vert(VertexInput input)
  9. {
  10. VertexOutput output;
  11. UNITY_SETUP_INSTANCE_ID(input);
  12. float startPixelIndex = _StartPixelIndex;
  13. float4 index = input.uv1;
  14. float4 weight = input.uv2;
  15. float4x4 matrix1 = getMatrix(startPixelIndex + index.x * 3);
  16. float4x4 matrix2 = getMatrix(startPixelIndex + index.y * 3);
  17. float4x4 matrix3 = getMatrix(startPixelIndex + index.z * 3);
  18. float4x4 matrix4 = getMatrix(startPixelIndex + index.w * 3);
  19. float4 vertex = mul(matrix1, input.vertex) * weight.x;
  20. vertex = vertex + mul(matrix2, input.vertex) * weight.y;
  21. vertex = vertex + mul(matrix3, input.vertex) * weight.z;
  22. vertex = vertex + mul(matrix4, input.vertex) * weight.w;
  23. float4 normal = mul(matrix1, input.normal) * weight.x;
  24. normal = normal + mul(matrix2, input.normal) * weight.y;
  25. normal = normal + mul(matrix3, input.normal) * weight.z;
  26. normal = normal + mul(matrix4, input.normal) * weight.w;
  27. output.vertex = UnityObjectToClipPos(vertex);
  28. output.normal = UnityObjectToWorldNormal(normal);
  29. output.uv = input.uv;
  30. return output;
  31. }

这个方案:

  1. 优点动画文件体积比原生动画文件还小,不占用CPU,GPU计算蒙皮;
  2. 缺点GPU压力;

总结

处理大规模角色,同时不要求动画效果很精细,内存够用的情况,可以采用方案A。
处理逻辑压力大、CPU消耗多并且渲染压力小的情况,可以选择方案B。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/116847
推荐阅读
相关标签
  

闽ICP备14008679号