SceneKit Rigged Character Animation可提高性能

时间:2016-12-26 09:07:18

标签: ios objective-c macos core-animation scenekit

我有*.DAE个字符,每个字符有45-70个骨头, 我希望屏幕上有大约100个动画角色。

然而,当我有~60个字符时,动画需要大约13毫秒的更新循环,这是非常昂贵的,并且让我几乎没有其他任务的空间。

我将动画“CAAnimationGroup”设置为网格SCNNode 当我想交换动画时,我将fadeOut设置为0.2并删除之前的动画,并将FadeIn的新动画添加到0.2。 - >这不好吗?我应该暂停上一个动画并播放一个新动画吗?还是更糟?

是否有更好的方法可以使用GPU或其他东西在SceneKit中为装配好的角色制作动画?

请让我开始正确的方向来减少更新循环中的动画开销。

更新 通过Bug雷达联系Apple后,我通过电子邮件收到了这个问题:

  

我们将在未来的更新中解决此问题   让我们知道,只要我们有测试版,您就可以进行测试和验证   这个问题。

     

感谢您的耐心等待。

所以让我们等一下,看看Apple的工程师将在多大程度上加强它:)。

1 个答案:

答案 0 :(得分:3)

如果顶点的影响小于4,则SceneKit会在GPU上执行骨架动画。从文档中,转载below

仅当此几何源中 componentsPerVector计数为4或更少时,SceneKit才会在GPU上执行骨架动画。较大的矢量会导致基于CPU的动画并大幅降低渲染性能。

我使用以下代码来检测动画是否在GPU上完成:

- (void)checkGPUSkinningForInScene:(SCNScene*)character
                          forNodes:(NSArray*)skinnedNodes {
  for (NSString* nodeName in skinnedNodes) {
    SCNNode* skinnedNode =
        [character.rootNode childNodeWithName:nodeName recursively:YES];
    SCNSkinner* skinner = skinnedNode.skinner;
    NSLog(@"******** Skinner for node %@ is %@ with skeleton: %@",
          skinnedNode.name, skinner, skinner.skeleton);
    if (skinner) {
      SCNGeometrySource* boneIndices = skinner.boneIndices;
      SCNGeometrySource* boneWeights = skinner.boneWeights;
      NSInteger influences = boneWeights.componentsPerVector;
      if (influences <= 4) {
        NSLog(@" This node %@ with %lu influences is skinned on the GPU",
              skinnedNode.name, influences);
      } else {
        NSLog(@" This node %@ with %lu influences is skinned on the CPU",
              skinnedNode.name, influences);
      }
    }
  }
}

您传递SCNScene以及附加SCNSkinner的节点名称,以检查动画是在GPU还是CPU上完成。

然而,GPU上还有另外一条关于动画的隐藏信息,即如果你的骨骼有超过60个骨骼,它将无法在GPU上执行。知道这一点的技巧是打印默认的顶点着色器,方法是将无效的着色器修改器条目附加为explained in this post

顶点着色器包含以下与蒙皮相关的代码:

#ifdef USE_SKINNING
uniform vec4 u_skinningJointMatrices[60];

....

    #ifdef USE_SKINNING
  {
    vec3 pos = vec3(0.);
    #ifdef USE_NORMAL
    vec3 nrm = vec3(0.);
    #endif
  #if defined(USE_TANGENT) || defined(USE_BITANGENT)
    vec3 tgt = vec3(0.);
    #endif
    for (int i = 0; i < MAX_BONE_INFLUENCES; ++i) {
#if MAX_BONE_INFLUENCES == 1
        float weight = 1.0;
#else
        float weight = a_skinningWeights[i];
#endif
      int idx = int(a_skinningJoints[i]) * 3;
      mat4 jointMatrix = mat4(u_skinningJointMatrices[idx], u_skinningJointMatrices[idx+1], u_skinningJointMatrices[idx+2], vec4(0., 0., 0., 1.));
            pos += (_geometry.position * jointMatrix).xyz * weight;
      #ifdef USE_NORMAL
            nrm += _geometry.normal * mat3(jointMatrix) * weight;
      #endif
      #if defined(USE_TANGENT) || defined(USE_BITANGENT)
            tgt += _geometry.tangent.xyz * mat3(jointMatrix) * weight;
      #endif
    }
    _geometry.position.xyz = pos;

这显然意味着你的骨骼​​应限制在60块骨头。

如果您的所有角色都具有相同的骨架,那么我建议您使用上述提示检查动画是在CPU还是GPU上执行。否则,您可能必须修复角色骨架,每个顶点的骨骼少于60个,影响不超过4个。