Путаница свечения в реальном времени

Так что у меня есть довольно простая 2D-игра в реальном времени, в которую я пытаюсь добавить немного хорошего свечения. Чтобы привести его к своей основной форме, это просто круги и ложь, нарисованная на черной поверхности. И если вы рассматриваете сцену с точки зрения цветового пространства hsv, все цвета (кроме черного) имеют значение "v", равное 100%.

В настоящее время у меня есть своего рода "накопительный" буфер, где текущий кадр объединяется с предыдущим кадром. Он работает, используя два внеэкранных буфера и черную текстуру.

  1. Буфер один активирован -------------
  2. Линии и точки нарисованы
  3. Буфер один деактивирован
  4. Буфер два активирован -------------
  5. Буфер два содержимого рисуется как полный экран
  6. Черная текстура прорисована с небольшой прозрачностью на весь экран
  7. Буфер один разрисован
  8. Буфер два деактивирован
  9. На экране активирован буфер -------
  10. Содержимое буфера 2 выводится на экран

Прямо сейчас все "запаздывания" происходят из-за задержек на процессоре. GPU справляется со всем этим очень хорошо.

Так что я подумал о том, чтобы, возможно, попытаться оживить вещи, добавив к ним эффект свечения. Я подумал, возможно, для шага 10, вместо использования обычного текстурного шейдера, я мог бы использовать тот, который рисует текстуру, кроме свечения!

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

  • Размытие вещей. Главным образом, что некоторые люди утверждают, что размытие по Гауссу может быть сделано в режиме реального времени, в то время как другие говорят, что вы не должны. Также люди упоминают другой тип размытия, называемого размытым фокусом, который я не знаю, что это такое.
  • Большинство примеров, которые я могу найти, используют XNA. Мне нужно иметь тот, который написан на языке шейдеров, как OpenGL ES 2.0.
  • Некоторые люди называют это светом, другие называют это цветением
  • Различные режимы смешивания? можно использовать для добавления свечения к исходной текстуре.
  • Как совместить вертикальное и горизонтальное размытие? Возможно, за один розыгрыш?

Во всяком случае, процесс, как я понимаю, для рендеринга свечения, таким образом,

  1. Вырежьте из него темные данные
  2. Размыть данные о свете (используя гауссиан?)
  3. Смешайте световые данные поверх оригинала (смешивание экрана?)

До сих пор я дошел до того, что у меня есть шейдер, который рисует текстуру. Как выглядит мой следующий шаг?

//Vertex
percision highp float;

attrivute vec2 positionCoords;
attribute vec2 textureCoords;
uniform mat4 matrix;
uniform float alpha;
varying vec2 v_texcoord;
varying float o_alpha;

void main()
{
   gl_Position = matrix * vec4(positionCoords, 0.0, 1.0);
   v_texcoord = textureCoords.xy;
   o_alpha = alpha;
}



//Fragment
varying vec2 v_texcoord;
uniform sampler2D s_texture;
varying float o_alpha;

void main()
{
     vec4 color = texture2D(s_texture, v_texcoord);
     gl_FragColor = vec4(color.r, color.g, color.b, color.a - o_alpha);
}

И это реально сделать в режиме реального времени?

Изменить: я, вероятно, хочу сделать 5px или меньше размытия

2 ответа

Для решения ваших начальных вопросов путаницы:

  • Любой вид фильтра размытия будет эффективно распределять каждый пиксель в BLOB-объект на основе его исходного положения и накапливать этот результат аддитивно для всех пикселей. Разница между фильтрами заключается в форме капли.
    • Для размытия по Гауссу этот шарик должен быть плавным градиентом, постепенно растушевываясь по краям. Вы, вероятно, хотите размытие по Гауссу.
    • Размытие "фокуса" было бы попыткой эмулировать камеру не в фокусе: вместо постепенного затухания до нуля, его капля будет растягивать каждый пиксель по кругу с резкими краями, давая слегка другой эффект.
  • Для простого однопроходного эффекта вычислительные затраты пропорциональны ширине размытия. Это означает, что узкое (например, 5px или менее) размытие, вероятно, будет возможным в качестве эффекта однопроходного режима в реальном времени. (Можно добиться широкого размытия по Гауссу в режиме реального времени, используя несколько проходов и пирамиду с несколькими разрешениями, но я бы рекомендовал сначала попробовать что-нибудь попроще...)
  • Вы можете разумно назвать эффект "свечение" или "цветение". Однако для меня "свечение" означает узкое размытие, приводящее к неоноподобному эффекту, в то время как "цветение" означает использование широкого размытия для имитации визуального эффекта ярких объектов в визуальной среде с высоким динамическим диапазоном.
  • Режим наложения определяет, как то, что вы рисуете, комбинируется с существующими цветами в целевом буфере. В OpenGL активируйте смешивание с glEnable(GL_BLEND) и установите режим с glBlendFunc(),
  • Для узкого размытия вы должны уметь выполнять горизонтальную и вертикальную фильтрацию за один проход.

Для быстрой однопроходной полноэкранной выборки вам нужно будет определить приращение пикселей в исходной текстуре. Это быстрее всего определить статически, так что ваш фрагментный шейдер не должен вычислять его во время выполнения:

float dx = 1.0 / x_resolution_drawn_over;
float dy = 1.0 / y_resolution_drawn_over;

Вы можете сделать 3-пиксельное (1,2,1) размытие по Гауссу за один проход, установив режим выборки текстуры на GL_LINEARи взять 4 образца из исходной текстуры t следующее:

float dx2 = 0.5*dx;  float dy2 = 0.5*dy;  // filter steps

[...]

vec2 a1 = vec2(x+dx2, y+dy2);
vec2 a2 = vec2(x+dx2, y-dy2);
vec2 b1 = vec2(x-dx2, y+dy2);
vec2 b2 = vec2(x-dx2, y-dy2);
result = 0.25*(texture(t,a1) + texture(t,a2) + texture(t,b1) + texture(t,b2));

Вы можете сделать 5-пиксельное (1,4,6,4,1) размытие по Гауссу за один проход, установив режим выборки текстуры на GL_LINEARи взять 9 образцов из исходной текстуры t следующее:

float dx12 = 1.2*dx; float dy12 = 1.2*dy;  // filter steps
float k0 = 0.375; float k1 = 0.3125;  // filter constants

vec4 filter(vec4 a, vec4 b, vec4 c) {
  return k1*a + k0*b + k1*c;
}

[...]

vec2 a1 = vec2(x+dx12, y+dy12);
vec2 a2 = vec2(x,      y+dy12);
vec2 a3 = vec2(x-dx12, y+dy12);
vec4 a = filter(sample(t,a1), sample(t,a2), sample(t,a3));

vec2 b1 = vec2(x+dx12, y     );
vec2 b2 = vec2(x,      y     );
vec2 b3 = vec2(x-dx12, y     );
vec4 b = filter(sample(t,b1), sample(t,b2), sample(t,b3));

vec2 c1 = vec2(x+dx12, y-dy12);
vec2 c2 = vec2(x,      y-dy12);
vec2 c3 = vec2(x-dx12, y-dy12);
vec4 c = filter(sample(t,c1), sample(t,c2), sample(t,c3));

result = filter(a,b,c);

Я не могу сказать вам, будут ли эти фильтры осуществимы в реальном времени на вашей платформе; 9 образцов / пиксель в полном разрешении могут быть медленными.

Любой более широкий гауссиан делает выгодными отдельные горизонтальные и вертикальные проходы; существенно более широкое гауссово потребовало бы методов с несколькими разрешениями для работы в реальном времени. (Обратите внимание, что, в отличие от гауссовских фильтров, такие как размытие "фокус" неразделимы, это означает, что они не могут быть разделены на горизонтальные и вертикальные проходы...)

Все, что сказал @comingstorm, правда, но есть гораздо более простой способ. Не пишите размытие или светитесь сами. Поскольку вы работаете на iOS, почему бы не использовать CoreImage, у которого есть ряд интересных фильтров на выбор и которые уже работают в реальном времени? Например, у них есть фильтр Блума, который, скорее всего, даст желаемые результаты. Также интерес может быть фильтр мрака.

Цепочка фильтров CoreImage намного проще, чем написание шейдеров. Вы можете создать CIImage из текстуры OpenGL через [+CIImage imageWithTexture:size:flipped:colorSpace:],

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