Как сделать шейдер карты нормалей с ограниченным диапазоном? - PullRequest
0 голосов
/ 09 июля 2020

У меня есть простой шейдер карты нормалей для 7 источников света, и он работает на весь экран. Как, черт возьми, заставить его работать только на ограниченном расстоянии? Я попытался рассчитать расстояние между светом и пикселем и просто «если», если расстояние слишком велико, но у меня это не работает.

    varying vec4 v_color;
    varying vec2 v_texCoords;
    
    uniform vec3 lightColor[7];
    uniform vec3 light[7];
    
    uniform sampler2D u_texture;
    uniform sampler2D u_normals;
    uniform vec2 resolution;
    uniform bool useNormals;
    uniform bool useShadow;
    uniform float strength;
    uniform bool yInvert;
    uniform bool xInvert;
    uniform vec4 ambientColor;
    
    void main() {
    
    // sample color & normals from our textures
    vec4 color = texture2D(u_texture, v_texCoords.st);
    vec3 nColor = texture2D(u_normals, v_texCoords.st).rgb;
    
    // some bump map programs will need the Y value flipped..
    nColor.g = yInvert ? 1.0 - nColor.g : nColor.g;
    nColor.r = xInvert ? 1.0 - nColor.r : nColor.r;
    
    // this is for debugging purposes, allowing us to lower the intensity of our bump map
    vec3 nBase = vec3(0.5, 0.5, 1.0);
    nColor = mix(nBase, nColor, strength);
    
    // normals need to be converted to [-1.0, 1.0] range and normalized
    vec3 normal = normalize(nColor * 2.0 - 1.0);
    
    vec3 sum = vec3(0.0);
    
    for ( int i = 0; i < 7; ++i ){
    
    vec3 currentLight = light[i];
    vec3 currentLightColor = lightColor[i];
    // here we do a simple distance calculation
    
    vec3 deltaPos = vec3( (currentLight.xy - gl_FragCoord.xy) / resolution.xy, currentLight.z );
    
    vec3 lightDir = normalize(deltaPos * 1);
    float lambert = clamp(dot(normal, lightDir), 0.0, 1.0);
    
    vec3 result = color.rgb;
    result = (currentLightColor.rgb * lambert);
    result *= color.rgb;
    sum += result;
    }
    
    
    vec3 ambient = ambientColor.rgb * ambientColor.a;
    vec3 intensity = min(vec3(1.0), ambient + sum); // don't remember if min is critical, but I think it might be to avoid shifting the hue when multiple lights add up to something very bright.
    vec3 finalColor = color.rgb * intensity;
    
    
    //finalColor *= (ambientColor.rgb * ambientColor.a);
    gl_FragColor = v_color * vec4(finalColor, color.a);
    }

изменить:

моя карта экран редактора

крупный план деталей

1 Ответ

1 голос
/ 09 июля 2020

Вам нужно измерить длину светового дельта-вектора и использовать ее для ослабления.

Сразу после линии lightDir вы можете поместить что-то вроде этого, но вам придется настроить FALLOFF, чтобы получить нужное расстояние. FALLOFF должно быть больше 0. В качестве отправной точки значение 0,1 даст вам радиус света около 4 единиц. Меньшие значения увеличивают радиус. Вы даже можете определить его как параметр каждого источника света (сделайте их vec4 s).

float distance = length(deltaPos);
float attenuation = 1.0 / (1.0 + FALLOFF * distance * distance);
float lambert = attenuation * clamp(dot(normal, lightDir), 0.0, 1.0);

Эта формула затухания имеет колоколообразную кривую. Если вы хотите, чтобы кривая имела заостренный кончик, что, возможно, более реалистично c (хотя, вероятно, бессмысленно для 2D-освещения), вы можете добавить второй параметр (которому вы можете изначально присвоить значение 0,1, а затем увеличивать его):

float attenuation = 1.0 / (1.0 + SHARPNESS * distance + FALLOFF * distance * distance);

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

Также , не умножайте на целое число. Это приведет к сбою компиляции шейдера на некоторых устройствах:

vec3 lightDir = normalize(deltaPos * 1); // The integer 1 is unsupported.
...