SceneKit Rigged Character Animation повышает производительность

Я имею *.DAE файлы для персонажей каждый имеет 45-70 костей, я хочу, чтобы на экране было около 100 анимированных персонажей.

Однако, когда у меня ~60 символов, анимация занимает ~13 мс цикла обновления, что очень дорого, и почти не оставляет места для других задач.

Я устанавливаю анимацию "CAAnimationGroup" для сетки SCNNode когда я хочу поменять анимацию, я удаляю предыдущую анимацию с fadeOut установите на 0,2 и добавив новую анимацию с FadeIn также установите на 0,2. -> Это плохо? Стоит ли просто приостановить предыдущую анимацию и воспроизвести новую? или это еще хуже?

Есть ли более эффективные способы анимации фальсифицированных персонажей в SceneKit может быть, с помощью графического процессора или что-то?

Пожалуйста, приведите меня в правильном направлении, чтобы уменьшить издержки анимации в цикле обновления.

Обновление После обращения в Apple через Bug radar я получил эту проблему по электронной почте:

Эта проблема решается в будущем обновлении, и мы сообщим вам, как только у нас будет бета-версия, которую вы сможете протестировать и проверить.

Спасибо за терпеливость.

Итак, давайте подождем и посмотрим, насколько инженеры Apple улучшат его:).

1 ответ

Решение

SceneKit выполняет скелетную анимацию на графическом процессоре, если ваши вершины имеют менее 4 влияний. Из документов, воспроизведенных ниже:


SceneKit выполняет скелетную анимацию на графическом процессоре, только если число компонентов PerVector в этом источнике геометрии равно 4 или меньше. Большие векторы приводят к анимации на базе процессора и резко снижают производительность рендеринга.


Я использовал следующий код, чтобы определить, выполняется ли анимация на графическом процессоре:

- (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.

Однако есть еще одна скрытая информация об анимации на графическом процессоре, которая заключается в том, что если ваш скелет содержит более 60 костей, он не будет выполняться на графическом процессоре. Хитрость заключается в том, чтобы напечатать вершинный шейдер по умолчанию, добавив недопустимую запись модификатора шейдера, как описано в этом посте.

Вершинный шейдер содержит следующий код, связанный со скинами:

#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-х влияний на вершину.

Другие вопросы по тегам