如何制作范围有限的法线贴图着色器?

How to make normal map shader with limited range?

我有用于 7 盏灯的简单法线贴图着色器,它可以在整个屏幕上工作。怎么让它只在有限的距离内工作?我尝试计算光和像素之间的距离,如果距离太大,则简单 'if' 但这对我不起作用。

    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);
    }

编辑:

my map editor screen

close-up of details

您需要测量光增量矢量的长度并使用它来衰减。

lightDir 行之后,你可以放这样的东西,但是你必须调整 FALLOFF 常量以获得你想要的距离。 FALLOFF 必须大于 0。作为起点,值 0.1 将为您提供大约 4 个单位的光半径。较小的值会增大半径。您甚至可能想将其定义为每个灯的参数(使它们成为 vec4s)。

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

这个衰减公式有一个钟形曲线。如果你想让曲线有一个尖尖的尖端,这可能更真实(虽然对于 2D 照明可能毫无意义),你可以添加第二个参数(你可以最初给出 0.1 的值并从那里增加):

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

有人 on this question posted this helpful chart 你可以一起玩,直观地看到参数如何改变曲线。

另外,不要乘以整数。这将导致着色器在某些设备上无法编译:

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