Частицы, управляемые текстурой, в WebGL / OpenGL с использованием шейдера

Я пытаюсь решить следующую задачу:

  • Есть сетка (план) NxM точек
  • У меня есть текстура, в которой каждый пиксель представляет собой силовое поле для позиции X, Y
  • Я хотел бы итеративно обновить исходную сетку, используя текстуру силового поля (и шейдер)

Я предполагаю, что мы могли бы довольно легко сделать это с помощью «геометрического шейдера», но его нет в WebGL. Следовательно, как я могу использовать программу, управляемую GLSL, для обновления положения вершин в моей сетке? Как я понял, обычный вершинный шейдер действительно преобразует источник во что-то еще за один проход, теперь есть способ передать этот вывод обратно в шейдер во время следующей итерации?

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


person Flavien Volken    schedule 26.02.2013    source источник


Ответы (1)


Да, используйте фрагментный шейдер. Сохраните состояние вашей сетки в текстуре (для большего удобства, в текстуре с плавающей запятой, хотя для этого требуется расширение) и используйте фрагментный шейдер для вычисления новой текстуры, которая является следующей итерацией. Затем при рисовании вершинный шейдер считывает текстуру, чтобы определить координаты вершины для отображения.

Я реализовал систему частиц, используя эту технику. Вот фрагментный шейдер, который обновляет состояние, поэтому вы можете понять, как он работает. Для каждой частицы пара смежных текселей (обеспечивающих 8 плавающих элементов) хранит ее положение и скорость.

#ifdef GL_ES
precision highp float;
#endif

uniform sampler2D uState;
uniform float uResolution;
uniform float uDT;
uniform float uHarmonicAccel;

vec4 lookup(float offset) {
  return texture2D(uState, (gl_FragCoord.xy + vec2(offset, 0.0)) / uResolution);
}

void main(void) {
  bool updatingPosition = mod(gl_FragCoord.x, 2.0) < 1.0;
  vec4 posp, velp;
  if (updatingPosition) {
    posp = lookup(0.0);
    velp = lookup(1.0);
  } else {
    posp = lookup(-1.0);
    velp = lookup(0.0);
  }
  vec3 pos = vec3(posp);
  vec3 vel = vec3(velp);

  // velocity update is applied to position as well
  vel += uHarmonicAccel * uDT * normalize(pos);

  if (updatingPosition) {
    gl_FragColor = vec4(pos + uDT * vel, posp[3]);
  } else {
    gl_FragColor = vec4(vel, velp[3]);
  }
}
person Kevin Reid    schedule 26.02.2013
comment
Использовать размерную текстуру N x 2 x Vec4 будет даже проще, чем с двумя соседними текселями. Хороший трюк, кстати :-) - person Flavien Volken; 04.03.2013
comment
@FlavienVolken Это осторожно при использовании текстуры, которая очень велика в одном измерении. У меня такое впечатление, что иногда такое может не поддерживаться. - person Kevin Reid; 04.03.2013