4
votes

I am using OpenGL ES 2.0 to develop an Android game in Java. Currently I am writing my own vertex and fragment shader. I encountered a weird problem in my fragment shader: normalize(u_LightPos - v_Position) is DIFFERENT from normalize(normalize(u_LightPos - v_Position)), where u_LightPos is a uniform and v_Position a varying.

Why is normalize() not idempotent? Why do I have to call it twice to get an actually normal (length 1) vector? This is very confusing.

EDIT:

Here is the vertex shader:

uniform mat4 u_MVPMatrix;
uniform mat4 u_MVMatrix;
attribute vec4 a_Position;
attribute vec3 a_Normal;
varying vec3 v_Position;
varying vec3 v_Normal;
void main() {
    v_Position = vec3(u_MVMatrix * a_Position);
    v_Normal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));
    gl_Position = u_MVPMatrix * a_Position;
}

And here is the fragment shader:

precision mediump float;
uniform vec3 u_LightPos;
uniform vec4 u_Color;
varying vec3 v_Position;
varying vec3 v_Normal;
void main() {
    float distance = length(u_LightPos - v_Position);
    vec3 lightVector = normalize(normalize(u_LightPos - v_Position));
    float diffuse = max(dot(v_Normal, lightVector), 0.0);
    gl_FragColor = u_Color * diffuse;
}

If I don't double normalize the lightVector, the dot product will be > 1.1, as I have tested. And no, normalizing v_Normal doesn't change that fact.

1
How much does the length of the first version diverge from the 1? If this is a very small amount I would guess that it is a numerical problem. Have you set the floating point precision in your shader? - BDL
No, it's a significant amount. More than 0.1 Sometimes much more than that, even 10 or more. Precision is precision mediump float. - Kenji
What is the value of u_LightPos and the approximate range of v_Position? I wonder if there could still be a precision issue if those values are very large. Does anything change if you set the precision to highp? - Reto Koradi
You're right. It's a precision issue. The vector doesn't get normalized properly because the vectors differ by too much. Thank you. - Kenji

1 Answers

6
votes

It's a precision issue. Setting the precision to highp resolves the problem. u_LightPos and v_Position differed by too much, resulting in a value that was too large to properly normalize.