Как реализовать 2D лучевой эффект освещения в GLSL

Первоначально это было задано @sydd здесь. Мне было интересно об этом, поэтому я пытаюсь закодировать его, но он был закрыт / удален, прежде чем я смог ответить, вот оно.

Вопрос: Как воспроизвести / реализовать этот эффект освещения 2D лучей в GLSL?

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

Таким образом, ввод должен быть:

  • положение мыши
  • Фоновая текстура карты RGBA

1 ответ

Решение
  1. Фоновая карта

    Хорошо, я создал тестовую карту RGBA в виде 2-х изображений, одно из которых содержит RGB (слева), а второе с альфа-каналом (справа), чтобы вы могли видеть их оба. Грубые они объединяются, чтобы сформировать единую текстуру RGBA.

    Карта RGB альфа карта

    Я немного размыл их, чтобы получить лучшие визуальные эффекты по краям.

  2. Лучевое литье

    Поскольку это должно работать в GLSL, нам нужно куда-то навести лучи. Я решил сделать это во фрагментном шейдере. Таким образом, алгоритм выглядит так:

    1. На стороне GL передайте униформу, необходимую для шейдеров. Здесь указывается положение мыши в качестве координаты текстуры, максимальное разрешение текстуры и сила светопропускания.
    2. На стороне GL нарисуйте четырехугольник, покрывающий весь экран текстурой фона (o blending)
    3. В шейдере Vertex просто передайте необходимую текстуру и координаты фрагмента
    4. Фрагмент шейдера на каждый фрагмент:

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

Вершинный шейдер

// 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.

свет внутри полупрозрачного объекта

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