在 glsl 中处理未定义、NaN 或 Inf 值。数学错误的思考

发布于 2024-12-19 18:02:04 字数 4322 浏览 7 评论 0原文

我仍然没有摆脱 glsl 着色器(GL 2.0)中的一些恼人的错误...我已经寻找这个问题的解决方案超过 3 周了,但我没有得到任何结果。谷歌上有很多例子和解释,但没有一个真正有效。

首先,这是我的fragmentshader代码。之后我将解释到底问题是什么:

//fragmentshader
const int MAX_POINT_LIGHTS = LL.MAX_POINT_LIGHTS;
const int MAX_DIR_LIGHTS = LL.MAX_DIR_LIGHTS;
const int MAX_TEXTURES = LL.MAX_TEXTURES;
//float-precision
precision mediump float;
//varyings
varying vec2 texcoord;
varying vec3 normal;
varying vec3 position;
varying float depth;
//pointlights
uniform float pl_range[MAX_POINT_LIGHTS];
uniform vec3 pl_position[MAX_POINT_LIGHTS];
uniform vec3 pl_color[MAX_POINT_LIGHTS];
uniform vec3 pl_specular[MAX_POINT_LIGHTS];
//dirlights
uniform vec3 dl_direction[MAX_DIR_LIGHTS];
uniform vec3 dl_position[MAX_DIR_LIGHTS];
uniform vec3 dl_color[MAX_DIR_LIGHTS];
uniform vec3 dl_specular[MAX_POINT_LIGHTS];
//cameras
uniform vec3 camera;
//fog
uniform vec4 uVecFog;
uniform float uFogNear;
uniform float uFogFar;
//material
uniform vec3 mat_diff;
uniform vec3 mat_spec;
uniform float mat_shine;
uniform float mat_elum;
//texture
uniform sampler2D uTexture;
//ambientlight
vec4 ambient = vec4(0.0,0.0,0.0,0.0);

void main() {

    vec3 N = normalize(normal);
    vec3 V = normalize(camera - position);
    vec4 diff  = vec4(0.0,0.0,0.0,0.0);
    vec4 spec = vec4(0.0,0.0,0.0,0.0);
    //dirlights
    for(int i=0;i<MAX_DIR_LIGHTS;i++) {
        vec3 L = -normalize(dl_direction[i]);
        vec3 R = -normalize(reflect(L,N));
        float RV = max(dot(R,V),0.0);
        float S = pow(max(dot(R,V),0.0),mat_shine);
        float F = clamp(dot(L,N),0.0,1.0);
        diff += vec4(F * mat_diff * dl_color[i],1.0);
        spec = vec4(S * mat_spec * dl_specular[i],1.0);
    }

    //pointlights
    for(int i=0;i<MAX_POINT_LIGHTS;i++) {
        vec3 L = normalize(pl_position[i] - position);
        vec3 R = -normalize(reflect(L,N));
        float D = distance(pl_position[i],position);
        float DF = clamp(1.0 - D/pl_range[i],0.0,1.0);
        float S = pow(max(dot(R,V),0.0),mat_shine);
        float F = clamp(DF * max(dot(L,N),0.0),0.0,1.0);
        diff += vec4(F * mat_diff * pl_color[i],1.0);
        spec += vec4(DF * S * mat_spec * pl_specular[i],1.0);
    }
    //fog
    float fogDensity = 0.0;
    if (depth > uFogFar) {
        fogDensity = 1.0;
    } else if(depth > uFogNear) {
        float newDepth = depth-uFogNear;
        fogDensity = newDepth/(uFogFar-uFogNear);
    } else if (depth < uFogNear) {
        fogDensity = 0.0;
    }
    vec4 vecFog = uVecFog * fogDensity;
    //texturecolor
    vec4 colortex = texture2D(uTexture,vec2(texcoord.x,-texcoord.y));
    //final fragmentcolor
    gl_FragColor = mix((ambient + colortex * (diff + vec4(mat_diff * mat_elum,1.0)) + spec),vecFog,fogDensity);
}

给出以下变量值,其中:

  • pl_ 是点光源值,
  • dl_ 是方向光值,
  • mat_diff 是材质的漫反射值,
  • 还有 mat_spec、mat_elum、mat_shine 用于材质镜面反射、发光和光泽度,
  • N = 法线,
  • V = 到顶点的视图矢量,
  • L = 到顶点的光矢量,
  • R = 反射矢量,
  • D =光距离,
  • DF = 混合光的距离因子,
  • S = 计算的镜面反射因子,
  • F = L、N
  • 和 diff 的点积,计算的漫反射/规格矢量的规格矢量和。

现在出现如下问题: 点光源本身运行完美。定向灯本身也可以完美运行。 但它们在一起不会很好地工作。我来具体说明一下问题: 我最多有 4 个点光源和 4 个方向光。为了进行测试,我使用了 4 个点光源和 1 个方向光。

  • 如果我注释掉定向灯的 for 循环计算,我得到以下结果: 方向光已禁用,点光源已启用
  • 如果我注释掉点光源的 for 循环,则会得到以下结果: 点光源已禁用,方向光已启用
  • 如果我启用了两个 for 循环,则会发生以下情况: 两种光源类型均已启用

定向光似乎完全被忽略。汽车仅由点光源照亮。

所以,我发现: 如果我改变 for 循环的顺序,点光源 ->定向光,而不是暗光 ->点光源,效果交换:忽略点光源,计算方向光。

我也发现,这个问题一定是由:pow()函数或除以零引起的,但是: 如果我注释掉以下代码行:

spec = vec4(S * mat_spec * dl_specular[i],1.0);
spec += vec4(DF * S * mat_spec * pl_specular[i],1.0);

我可以获得以下结果:

spec +=lines commented out

->通常为点光和定向光计算漫反射颜色!

所以我想到了如何消除这个问题,因为我认为0乘以0会导致数学错误。除以零也是如此...... 如何避免未定义的值?

android-glsl 中似乎不存在 isnan() 或 isinf() 函数。

安迪解决方案或提示我可以做得更好,这会起作用吗? 如果任何答案能有所帮助,我会很高兴。三周以来这让我很生气。非常感谢各位帮助者! :)

有关代码的更多信息: https://github.com/Chrise55/Llama3D

I still didn't get rid of some annoying bugs in my glsl shader (GL 2.0)... I searched for a solution of this problem for over 3 weeks now, but I didn't get any. There are many examples and explanations on google, but none of them really works.

First of all, here is my fragmentshader code. After that I will explain what exactly the problem is:

//fragmentshader
const int MAX_POINT_LIGHTS = LL.MAX_POINT_LIGHTS;
const int MAX_DIR_LIGHTS = LL.MAX_DIR_LIGHTS;
const int MAX_TEXTURES = LL.MAX_TEXTURES;
//float-precision
precision mediump float;
//varyings
varying vec2 texcoord;
varying vec3 normal;
varying vec3 position;
varying float depth;
//pointlights
uniform float pl_range[MAX_POINT_LIGHTS];
uniform vec3 pl_position[MAX_POINT_LIGHTS];
uniform vec3 pl_color[MAX_POINT_LIGHTS];
uniform vec3 pl_specular[MAX_POINT_LIGHTS];
//dirlights
uniform vec3 dl_direction[MAX_DIR_LIGHTS];
uniform vec3 dl_position[MAX_DIR_LIGHTS];
uniform vec3 dl_color[MAX_DIR_LIGHTS];
uniform vec3 dl_specular[MAX_POINT_LIGHTS];
//cameras
uniform vec3 camera;
//fog
uniform vec4 uVecFog;
uniform float uFogNear;
uniform float uFogFar;
//material
uniform vec3 mat_diff;
uniform vec3 mat_spec;
uniform float mat_shine;
uniform float mat_elum;
//texture
uniform sampler2D uTexture;
//ambientlight
vec4 ambient = vec4(0.0,0.0,0.0,0.0);

void main() {

    vec3 N = normalize(normal);
    vec3 V = normalize(camera - position);
    vec4 diff  = vec4(0.0,0.0,0.0,0.0);
    vec4 spec = vec4(0.0,0.0,0.0,0.0);
    //dirlights
    for(int i=0;i<MAX_DIR_LIGHTS;i++) {
        vec3 L = -normalize(dl_direction[i]);
        vec3 R = -normalize(reflect(L,N));
        float RV = max(dot(R,V),0.0);
        float S = pow(max(dot(R,V),0.0),mat_shine);
        float F = clamp(dot(L,N),0.0,1.0);
        diff += vec4(F * mat_diff * dl_color[i],1.0);
        spec = vec4(S * mat_spec * dl_specular[i],1.0);
    }

    //pointlights
    for(int i=0;i<MAX_POINT_LIGHTS;i++) {
        vec3 L = normalize(pl_position[i] - position);
        vec3 R = -normalize(reflect(L,N));
        float D = distance(pl_position[i],position);
        float DF = clamp(1.0 - D/pl_range[i],0.0,1.0);
        float S = pow(max(dot(R,V),0.0),mat_shine);
        float F = clamp(DF * max(dot(L,N),0.0),0.0,1.0);
        diff += vec4(F * mat_diff * pl_color[i],1.0);
        spec += vec4(DF * S * mat_spec * pl_specular[i],1.0);
    }
    //fog
    float fogDensity = 0.0;
    if (depth > uFogFar) {
        fogDensity = 1.0;
    } else if(depth > uFogNear) {
        float newDepth = depth-uFogNear;
        fogDensity = newDepth/(uFogFar-uFogNear);
    } else if (depth < uFogNear) {
        fogDensity = 0.0;
    }
    vec4 vecFog = uVecFog * fogDensity;
    //texturecolor
    vec4 colortex = texture2D(uTexture,vec2(texcoord.x,-texcoord.y));
    //final fragmentcolor
    gl_FragColor = mix((ambient + colortex * (diff + vec4(mat_diff * mat_elum,1.0)) + spec),vecFog,fogDensity);
}

There are give following variable-values, where:

  • pl_ are pointlightvalues,
  • dl_ are directionallightvalues,
  • mat_diff is the diffuse-value for the material,
  • also mat_spec, mat_elum, mat_shine for material specular, elumination and shininess,
  • N = Normal,
  • V = View-Vector to Vertex,
  • L = Light-Vector to Vertex,
  • R = Reflection Vector,
  • D = Lightdistance,
  • DF = Distancefactor for blending light,
  • S = Calculated Specularityfactor,
  • F = Dotproduct from L,N
  • and diff, spec vectorsum of the calculated diffuse/spec-vectors.

Now there occures following problem:
Pointlights themself run perfect. Directionallights themself run perfect, too.
But together they won't work very well. I'll precise the problem:
I've got 4 Pointlights and 4 Directionallights for maximum. For testing i've used 4 Pointlights and 1 Directionallight.

  • If i commented out the for-loop calculation the directionallights, i got following result:
    Directionallights are disable, pointlights enabled
  • If i commented out the for-loop for pointlights, i got following result:
    Pointlights are disabled, directionallights enabled instead
  • If i enabled both for-loops, following happens:
    Both lightsource types are anabled

The directionallight seems to be ignored completly. The car is lighted only by the pointlights instead.

So, what I found out:
If I change the order of the for-loops, pointlights -> directionallights, instead of dirlights -> pointlights, the effect swaps: pointlights are ignored, directionallight is computed.

What I found out too, this problem must be up to: pow() function or dividing by zero, but:
If I comment out follwing lines of code:

spec = vec4(S * mat_spec * dl_specular[i],1.0);
spec += vec4(DF * S * mat_spec * pl_specular[i],1.0);

i can get following result:

spec += lines commented out

-> The diffuse color is computed normally for point AND directionallights!

So I thought of how to eliminate this problem, because I thought about 0 powered by 0 leads to a math error. Dividing by zero, too....
How can I avoid values that are undefined?

isnan() or isinf() functions seem not to exist in android-glsl.

Andy solutions or tipps what I could do better, that this will work?
I would be glad about any answer if it could help a little bit. This makes me mad since 3 weeks. Thanks so much for helpers! :)

More information about the code:
https://github.com/Chrise55/Llama3D

Chrise

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文