Как реализовать 2D лучевой эффект освещения в GLSL
Первоначально это было задано @sydd здесь. Мне было интересно об этом, поэтому я пытаюсь закодировать его, но он был закрыт / удален, прежде чем я смог ответить, вот оно.
Вопрос: Как воспроизвести / реализовать этот эффект освещения 2D лучей в GLSL?
Сам эффект направляет лучи из положения мыши в каждом направлении, накапливая фоновую альфа-карту и цвета, влияющие на силу пикселей.
Таким образом, ввод должен быть:
- положение мыши
- Фоновая текстура карты RGBA
1 ответ
Фоновая карта
Хорошо, я создал тестовую карту RGBA в виде 2-х изображений, одно из которых содержит RGB (слева), а второе с альфа-каналом (справа), чтобы вы могли видеть их оба. Грубые они объединяются, чтобы сформировать единую текстуру RGBA.
Я немного размыл их, чтобы получить лучшие визуальные эффекты по краям.
Лучевое литье
Поскольку это должно работать в GLSL, нам нужно куда-то навести лучи. Я решил сделать это во фрагментном шейдере. Таким образом, алгоритм выглядит так:
- На стороне GL передайте униформу, необходимую для шейдеров. Здесь указывается положение мыши в качестве координаты текстуры, максимальное разрешение текстуры и сила светопропускания.
- На стороне GL нарисуйте четырехугольник, покрывающий весь экран текстурой фона (o blending)
- В шейдере Vertex просто передайте необходимую текстуру и координаты фрагмента
Фрагмент шейдера на каждый фрагмент:
- приведите луч от положения мыши к фактическому положению фрагмента (в координатах текстуры)
- кумулировать / интегрировать свойства света во время движения луча
- остановка, если сила света близка к нулю или позиция фрагмента достигнута.
Вершинный шейдер
// Vertex
#version 420 core
layout(location=0) in vec2 pos; // glVertex2f <-1,+1>
layout(location=8) in vec2 txr; // glTexCoord2f Unit0 <0,1>
out smooth vec2 t1; // texture end point <0,1>
void main()
{
t1=txr;
gl_Position=vec4(pos,0.0,1.0);
}
Фрагмент шейдера
// Fragment
#version 420 core
uniform float transmit=0.99;// light transmition coeficient <0,1>
uniform int txrsiz=512; // max texture size [pixels]
uniform sampler2D txrmap; // texture unit for light map
uniform vec2 t0; // texture start point (mouse position) <0,1>
in smooth vec2 t1; // texture end point, direction <0,1>
out vec4 col;
void main()
{
int i;
vec2 t,dt;
vec4 c0,c1;
dt=normalize(t1-t0)/float(txrsiz);
c0=vec4(1.0,1.0,1.0,1.0); // light ray strength
t=t0;
if (dot(t1-t,dt)>0.0)
for (i=0;i<txrsiz;i++)
{
c1=texture2D(txrmap,t);
c0.rgb*=((c1.a)*(c1.rgb))+((1.0f-c1.a)*transmit);
if (dot(t1-t,dt)<=0.000f) break;
if (c0.r+c0.g+c0.b<=0.001f) break;
t+=dt;
}
col=0.90*c0+0.10*texture2D(txrmap,t1); // render with ambient light
// col=c0; // render without ambient light
}
И наконец результат:
Анимированные 256 цветов GIF:
Цвета в GIF слегка искажены из-за 8-битного усечения. Также, если анимация прекращает обновлять страницу или открывать в программе просмотра decend gfx.