Эффективно перебирать соседние пиксели в фрагментном шейдере - PullRequest
0 голосов
/ 03 апреля 2019

Для контекста:

Я работаю над генеративной 2D-анимацией с OpenFrameworks. Я пытаюсь реализовать шейдер, смещающий заливки некоторых фигур цветом, в зависимости от ориентации краев фигур.

В основном это изображение выглядит так: input

и выплевывает что-то вроде этого: output

Обратите внимание, что он намеренно берет только цвет с левой стороны фигуры.

Прямо сейчас мой фрагментный шейдер выглядит так:

#version 150

out vec4 outputColor;

uniform sampler2DRect fbo;
uniform sampler2DRect mask;

vec2 point;
vec4 col;
float x,i;
float delta = 200;

vec4 extrudeColor()
{
        x = gl_FragCoord.x > delta ? gl_FragCoord.x - delta : 0;
        for(i = gl_FragCoord.x; i > x; i--)
        {
            point = vec2(i, gl_FragCoord.y);
            if(texture(fbo, point) != vec4(0,0,0,0)){
                col = texture(fbo, point);
                return vec4(col.r, col.g, col.b, (i-x)/delta);
            }
        }
        return vec4(0,0,0,1);
}

void main()
{
    outputColor = texture(mask, gl_FragCoord.xy) == vec4(1,1,1,1) && texture(fbo, gl_FragCoord.xy) == vec4(0,0,0,0) ? extrudeColor() : vec4(0,0,0,0);
}

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

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

Актуальный, более общий вопрос:

Я совершенно новичок в glsl и opengl. Есть ли способ сделать этот тип итерации через соседние пиксели более эффективным и без стольких texture() операций чтения?

Может быть, с использованием матриц? ИДК!

1 Ответ

0 голосов
/ 11 апреля 2019

Это крайне неэффективный способ решения этой проблемы.Старайтесь избегать условных (если) и циклов (для) в вашем шейдере.Я бы предложил загрузить или сгенерировать одну текстуру, а затем использовать альфа-маску для создания нужной фигуры.Текстура может оставаться постоянной, в то время как 2-х или 8-битная маска может быть сгенерирована для каждого кадра.

Альтернативным методом будет использование нескольких униформ и выгрузка данных "на строку" в массив:

#version 440 core

uniform sampler2D _Texture ; // The texture to draw
uniform vec2 _Position ; // The 'object' position (screen coordinate)
uniform int _RowOffset ; // The offset in the 'object' to start drawing
uniform int _RowLength ; // The length of the 'row' to draw
uniform int _Height ; // The height of the 'object' to draw

in vec2 _TexCoord ; // The texture coordinate passed from Vertex shader

out vec4 _FragColor ; // The output color (gl_FragColor deprecated)

void main () {
  if (gl_FragCoord.x < (_Position.x + _RowOffset)) discard ;
  if (gl_FragCoord.x > (_Position.x + _RowOffset + _RowLength)) discard ;
  _FragColor = texture2D (_Texture, _TexCoord.st) ;
}

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

const vec4 _Red = vec4 (1, 0, 0, 1) ;
const vec4 _Green vec4 (0, 1, 0, 0) ;

vec4 _GetGradientColor (float _P /* Percentage */) {
  float _R = (_Red.r * _P + _Green.r * (1 - _P) / 2 ;
  float _G = (_Red.g * _P + _Green.g * (1 - _P) / 2 ;
  float _B = (_Red.b * _P + _Green.b * (1 - _P) / 2 ;
  float _A = (_Red.a * _P + _Green.a * (1 - _P) / 2 ;
  return (vec4 (_R, _G, _B, _A)) ;
}

Затем в Frag Shader,

float _P = gl_FragCoord.y - _Position.y / _Height ;
_FragColor = _GetGradientColor (_P) ;

Вывод шейдера

Конечно, все это можно немного оптимизировать, и это создает только 2-цветный градиент, в то время как похоже, что вам нужно несколькоцвета.Быстрый поиск в Google по запросу «генератор линейного градиента» может дать вам более приятные альтернативы.Следует также отметить, что этот простой пример не будет работать для фигур с «отверстиями» в них, но его можно изменить, чтобы сделать это.Если математика шейдера становится слишком тяжелой, выберите текстуру с параметром альфа-маски.

...