small medium large xlarge

Generic-user-small
12 Apr 2016, 11:25
Richard Klassen (2 posts)

Hi,

I’m having difficulty with the second exercise in 12.7 on page 251 of the 2013 edition.

Here’s what I have so far:

/* Heightmap fragment shader */
precision mediump float;
varying vec3 v_Color;
float zValueLinear;

void main() {
    zValueLinear = (1.0 - gl_FragCoord.z) * (100.0 - 0.1); // near - far
    gl_FragColor = vec4((zValueLinear, zValueLinear, zValueLinear, 1.0);
}

The results appear linear for the wrong range. On https://www.opengl.org/discussion_boards/showthread.php/164089-gl_FragCoord-z, one user suggested to use the following:

float Z = gl_ProjectionMatrix[3].z / 
             (gl_FragCoord.z
              * -2.0 
              +  1.0
              -  gl_ProjectionMatrix[2].z);

If ProjectionMatrix is u_Matrix, how do I access the values referenced here?

Thanks!

Generic-user-small
20 Apr 2016, 01:08
Kevin Brothaler (47 posts)

Hi Robert,

Apologies for the delay, I wanted to test things out first before responding, and I haven’t had my head in the math for quite a while. :)

So, first to get access to the matrix, you can modify the program by adding the matrix as a uniform:

uniform mat4 u_ProjectionMatrix;

Then you’d need to pass this in setUniforms:

public void setUniforms(float[] mvpMatrix, float[] projectionMatrix) {
        glUniformMatrix4fv(uMvpMatrixLocation, 1, false, mvpMatrix, 0);
        glUniformMatrix4fv(uProjectionMatrixLocation, 1, false, projectionMatrix, 0);
    }

Also taking care to add uProjectionMatrixLocation to the class and initializing it in the constructor. Then, you’d need to update the call to setUniforms:

heightmapProgram.setUniforms(modelViewProjectionMatrix, projectionMatrix);

In testing though, this method with that shader code just resulted in a black screen for me. Here’s how to do it by just using the fragment shader:

precision mediump float;

varying vec3 v_Color;

highp float get_linear_z_from_z(highp float n, highp float f, highp float z) {
    return (n * z) / ( f - z * (f - n));
}

void main()
{
    float linear_z = get_linear_z_from_z(1.0, 100.0, gl_FragCoord.z);
    gl_FragColor = vec4(linear_z, linear_z, linear_z, 1.0);
}

This uses the near and far from the perspective projection to convert the non-linear z/w value back into a linear z value. When using a perspective projection, you can also use the w value directly:

precision mediump float; 

varying vec3 v_Color;

float get_linear_w_from_w(float n, float f, float inverse_w) {
    float w = 1.0 / inverse_w;
    return (w - n) / (f - n);
}

void main()
{
    float linear_distance = get_linear_w_from_w(1.0, 100.0, gl_FragCoord.w);
    gl_FragColor = vec4(linear_distance, linear_distance, linear_distance, 1.0);
}

gl_FragCoord.w stores 1/Wc (w in clip coordinates), so it’s inverted first and then scaled by the near and far values from the projection matrix; these could be passed in as uniforms to the shader rather than being hardcoded. So these are both from the thread that you mentioned; lots of good methods in there. :)

Hope this helps out! :)

You must be logged in to comment