Как сделать нестандартную форму света в GLSL, используя расстояние от fragcoord до позиции света - PullRequest
1 голос
/ 02 мая 2020

Я читал 2d light shader в игрушке шейдеров, которую можно использовать для создания 2d (точечного) света. https://www.shadertoy.com/view/4dfXDn

vec4 drawLight(vec2 p, vec2 pos, vec4 color, float range)
    {
        float ld = length(p - pos);  
        if (ld > range) return vec4(0.0);
        float fall = (range - ld)/range;
        fall *= fall;
        return (fall) * color;
    }

void main() {
    vec2 p = gl_FragCoord.xy;
    vec2 c = u_resolution.xy / 2.0;

    vec4 col = vec4(0.0);

    vec2 lightPos = vec2(c);
    vec4 lightCol = vec4(1.000,0.25,0.000,1.000);

    col += drawLight(p, lightPos, lightCol, 400.0);

    gl_FragColor = col;
}

Однако я не могу понять, как с помощью этого сделать еще одну "форму" света? Как я могу изменить функцию drawLight, чтобы иметь другой параметр, который изменяет исходный источник света, например, 1,0 - это круговой источник света, а 0,25 - это четырехугольный источник света?

1 Ответ

1 голос
/ 02 мая 2020

в вашем коде

float ld = length(p - pos);

Вычисляет ваше расстояние от света равномерно во всех направлениях (евклидово расстояние). Если вы хотите другое затенение, измените уравнение ...

Например, вы можете вычислить минимальное перпендикулярное расстояние до многоугольного источника света следующим образом:

Вершина:

// 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;                 // fragment position <0,1>
void main()
    {
    t1=txr;
    gl_Position=vec4(pos,0.0,1.0);
    }

Фрагмент:

// Fragment
#version 420 core
uniform sampler2D txrmap;   // texture unit
uniform vec2 t0;            // mouse position <0,1>
in smooth vec2 t1;          // fragment position <0,1>
out vec4 col;

// light shape
const int n=3;
const float ldepth=0.25;    // distance to full disipation of light
const vec2 lpolygon[n]=     // vertexes CCW
    {
    vec2(-0.05,-0.05),
    vec2(+0.05,-0.05),
    vec2( 0.00,+0.05),
    };
void main()
    {
    int i;
    float l;
    vec2 p0,p1,p,n01;
    // compute perpendicular distance to edges of polygon
    for (p1=vec2(lpolygon[n-1]),l=0.0,i=0;i<n;i++)
        {
        p0=p1; p1=lpolygon[i];          // p0,p1 = edge of polygon
        p=p1-p0;                        // edge direction
        n01=normalize(vec2(+p.y,-p.x)); // edge normal CCW
//      n01=normalize(vec2(-p.y,+p.x)); // edge normal CW
        l=max(dot(n01,t1-t0-p0),l);
        }
    // convert to light strength
    l = max(ldepth-l,0.0)/ldepth;
    l=l*l*l;
    // render
//  col=l*texture2D(txrmap,t1);
    col = l*vec4(1.0,1.0,1.0,0.0);
    }

Я использовал подобный код Как реализовать эффект 2D лучевого освещения в GLSL в качестве отправной точки, следовательно, немного отличается имена переменных.

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

lpolygon[n] - это форма света относительно положения света t0, а t1 - это положение фрагмента. Он должен быть в обмотке CCW, иначе вам нужно будет отменить нормальное вычисление (мое представление перевернуто, чтобы оно могло выглядеть как CW, а не как). Я использовал диапазон <0,1>, так как вы можете использовать его в качестве координаты текстуры напрямую ...

Вот скриншот:

preview

Вот некоторые объяснения:

explanations

Для аналитической формы вам необходимо использовать аналитическое вычисление расстояния ...

...