【问题标题】:How to make normal map shader with limited range?如何制作范围有限的法线贴图着色器?
【发布时间】:2020-10-29 10:11:27
【问题描述】:

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

编辑:

my map editor screen

close-up of details

【问题讨论】:

  • "[...] 但这对我不起作用。" - 什么不起作用?实际结果是什么,预期结果是什么?

标签: opengl libgdx glsl shader lwjgl


【解决方案1】:

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

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 发布了this helpful chart 你可以一起玩,以直观地查看参数如何改变曲线。

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

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

【讨论】:

  • 你太棒了! :) 答案真的很有帮助。我使用这个衰减公式:1.0 / ( attenuation.x + (attenuation.y*distance) + (attenuation.z*distance*distance) 它的工作完美:)
猜你喜欢
  • 2012-01-12
  • 1970-01-01
  • 2013-03-20
  • 1970-01-01
  • 2021-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多