#c #opengl #glsl #opengl-3
#c #opengl #glsl #opengl-3
Вопрос:
Для контекста:
Я работаю над генеративной 2D-анимацией с openFrameworks. Я пытаюсь реализовать шейдер, который сдвигает заливки некоторых фигур цветом в зависимости от ориентации краев фигур.
В основном это принимает изображение, подобное этому:
и выдает что-то вроде этого:
Обратите внимание, что он намеренно использует цвет только с левой стороны фигуры.
Прямо сейчас мой фрагментный шейдер выглядит следующим образом:
#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) amp;amp; texture(fbo, gl_FragCoord.xy) == vec4(0,0,0,0) ? extrudeColor() : vec4(0,0,0,0);
}
mask
сэмплер — это просто черно-белая версия второго изображения, которую я использую, чтобы избежать вычисления пикселей за пределами фигур.
Шейдер, который у меня есть, работает, но он медленный, и я чувствую, что не использую правильное мышление и кодирование на графическом процессоре.
Актуальный, более общий вопрос:
Я совершенно новичок в glsl и opengl. Есть ли способ сделать такую итерацию по соседним пикселям более эффективной и без такого количества texture()
операций чтения?
Может быть, с использованием матриц? О ЧУДО!
Ответ №1:
Это крайне неэффективный способ решения этой проблемы. Старайтесь избегать условных выражений (if) и циклов (for) в вашем шейдере. Я бы предложил загрузить или сгенерировать единственную текстуру, а затем использовать альфа-маску для создания нужной вам формы. Текстура может оставаться постоянной, в то время как 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)) ;
}
Затем в вашем шейдере фрагментов,
float _P = gl_FragCoord.y - _Position.y / _Height ;
_FragColor = _GetGradientColor (_P) ;
Конечно, все это можно было бы немного оптимизировать, и это генерирует только двухцветный градиент, тогда как похоже, что вам нужно несколько цветов. Быстрый поиск в Google по запросу «генератор линейных градиентов» может предложить вам несколько более приятных альтернатив. Я должен также отметить, что этот простой пример не будет работать для фигур с «отверстиями» в них, но его можно изменить, чтобы сделать это. Если математика шейдера становится слишком сложной, выберите текстуру с альфа-маской.