OpenGL ES 2 (iOS) Morph / Animate между двумя наборами вершин - PullRequest
3 голосов
/ 30 декабря 2011

У меня есть два набора вершин, используемых в качестве линейной полосы:

  1. Vertexes1
  2. Vertexes2

Важно знать, что эти вершины имеют ранее неизвестные значения, поскольку они являются динамическими.

Я хочу сделать анимированный переход (морфинг) между этими двумя. Я придумал два разных способа сделать это:

Вариант 1:

Установите форму времени в вершинном шейдере, которая идет от 0 - 1 , где я могу сделать что-то вроде этого:

// Inside main() in the vertex shader
float originX = Position.x;
float destinationX = DestinationVertexPosition.x;
float interpolatedX = originX + (destinationX - originX) * Time;

gl_Position.x = interpolatedX;

Как вы, вероятно, видите, здесь есть одна проблема: как мне получить "DestinationVertexPosition" там?

Вариант 2:

Выполните вычисление интерполяции вне вершинного шейдера, где я перебираю каждую вершину, создаю третий набор вершин для интерполированных значений и использую его для визуализации:

// Pre render
// Use this vertex set to render
InterpolatedVertexes

for (unsigned int i = 0; i < vertexCount; i++) {
   float originX = Vertexes1[i].x;
   float destinationX = Vertexes2[i].x;
   float interpolatedX = originX + (destinationX - originX) * Time;

   InterpolatedVertexes[i].x = interpolatedX;
}

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

Теперь, из двух вариантов, я чувствую, что первый определенно лучше с точки зрения производительности, учитывая то, что происходит на уровне шейдера, и мне не нужно создавать новый набор вершин каждый раз, когда "Время" обновлено.

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

  1. Обсуждение лучших способов достижения желаемых результатов в OpenGL ES 2 (iOS).
  2. Дискуссия о том, как вариант 1 может быть реализован должным образом, либо путем предоставления "DestinationVertexPosition" , либо путем изменения идеи каким-либо образом, чтобы лучше достичь того же результата.
  3. Обсуждение того, как Вариант 2 может быть реализован.

Ответы [ 2 ]

10 голосов
/ 30 декабря 2011

В ES 2 вы задаете такие атрибуты, которые вам нравятся - поэтому нет проблем с указанием атрибутов для origin и destination и выполнением линейной интерполяции между ними в вершинном шейдере. Однако вам не следует делать это компонент за компонентом, так как ваш код предполагает, что вы хотите, поскольку графические процессоры являются векторными процессорами, а функция mix GLSL сделает линейное смешивание, которое вы хотите. Так, например (с очевидной неэффективностью и допущениями)

int sourceAttribute = glGetAttribLocation(shader, "sourceVertex");
glVertexAttribPointer(sourceAttribute, 3, GL_FLOAT, GL_FALSE, 0, sourceLocations);

int destAttribute = glGetAttribLocation(shader, "destVertex");
glVertexAttribPointer(destAttribute, 3, GL_FLOAT, GL_FALSE, 0, destLocations);

И

gl_Position = vec4(mix(sourceVertex, destVertex, Time), 1.0);
3 голосов
/ 30 декабря 2011

Ваши два варианта здесь имеют компромисс: предоставьте дважды геометрию один раз и интерполируйте между ней, или предоставьте только один набор геометрии, но сделайте это для каждого кадра. Вы должны взвесить размер геометрии и ширину полосы загрузки.

Учитывая мой опыт работы с устройствами iOS, я настоятельно рекомендую вариант 1. Загрузка новой геометрии на каждый кадр может быть чрезвычайно дорогой на этих устройствах.

Если вершины постоянны, вы можете загрузить их один раз в один или два объекта буфера вершин (VBO) с установленным флагом GL_STATIC_DRAW. Серия PowerVR SGX имеет аппаратную оптимизацию для работы со статическими VBO, поэтому с ними очень быстро работать после начальной загрузки.

Что касается загрузки двух наборов вершин для использования в одном шейдере, геометрия - это просто еще один входной атрибут для вашего шейдера. Вы можете иметь один, два или более наборов вершин, подаваемых в один вершинный шейдер. Вы просто определяете атрибуты, используя код как

attribute vec3 startingPosition;
attribute vec3 endingPosition;

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

vec3 finalPosition = startingPosition * (1.0 - fractionalProgress) + endingPosition * fractionalProgress;

Редактировать: Томми указывает на операцию mix(), о которой я забыл, и это лучший способ выполнить вышеупомянутую интерполяцию вершин.

Чтобы проинформировать вашу шейдерную программу о том, где взять второй набор вершин, вы должны использовать почти такой же glVertexAttribPointer() вызов второго набора геометрии, как первый, только указывая на этот VBO и атрибут .

Обратите внимание, что вы можете выполнять это вычисление как вектор, а не разбивать все три компонента по отдельности. Это не очень хорошо с точностью по умолчанию highp на современных чипах PowerVR SGX, но может быть быстрее на будущих, чем делать этот компонент за раз.

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

Единственный случай, когда я слышал, что вариант 2 (загрузка новой геометрии в каждом кадре) может быть предпочтительным, - это особые случаи, когда использование фреймворка Accelerate для векторной манипуляции с геометрией оказывается быстрее, чем выполнение скининга на -gpu. Я помню, что люди Unity говорили об этом однажды, но я не могу вспомнить, было ли это для действительно маленьких или действительно больших наборов геометрии. Вариант 1 был быстрее во всех случаях, когда я работал сам с собой.

...