
Yesterday I stumbled upon this small math note that explains how to compute the normal from a procedural heightmap:

Here is a small demo that puts in practice the calcNormal() function to compute the normal vector in the vertex shader.
You can download the GeeXLab demo from THIS LINK (the demo is also available in the full code sample pack in the gl-32/heightmap-normal/ folder). As usual, GeeXLab can be downloaded from the THIS PAGE.
For any feedback, a thread is available HERE on Geeks3D forums.

The vertex shader:
#version 150
in vec4 gxl3d_Position;
in vec4 gxl3d_TexCoord0;
in vec4 gxl3d_Normal;
out vec4 v_normal;
out vec4 v_lightdir;
out vec4 v_eyedir;
uniform mat4 gxl3d_ProjectionMatrix; // GeeXLab auto-uniform.
uniform mat4 gxl3d_ModelViewMatrix; // GeeXLab auto-uniform.
uniform mat4 gxl3d_ViewMatrix; // GeeXLab auto-uniform.
uniform vec4 light_position;
float f(float x, float z)
{
return sin(x) * cos(z);
}
vec3 calc_normal(float x, float z)
{
float eps = 0.0001;
return normalize(vec3(
f(x-eps, z) - f(x+eps, z),
2.0*eps,
f(x, z-eps) - f(x, z+eps)
));
}
void main()
{
vec4 P = gxl3d_Position;
P.y = f(P.x, P.z);
vec3 N = calc_normal(P.x, P.z);
vec4 view_position = gxl3d_ModelViewMatrix * P;
gl_Position = gxl3d_ProjectionMatrix * view_position;
v_normal = gxl3d_ModelViewMatrix * vec4(N, 0.0);
v_eyedir = -view_position;
vec4 lp = gxl3d_ViewMatrix * light_position;
v_lightdir = lp - view_position;
}
The fragment shader:
#version 150
in vec4 v_normal;
in vec4 v_lightdir;
in vec4 v_eyedir;
out vec4 FragColor;
void main()
{
vec3 albedo = vec3(0.9, 0.7, 0.5);
vec3 N = normalize(v_normal.xyz);
vec3 L = normalize(v_lightdir.xyz);
float NdotL = max(dot(N, L), 0.0);
vec3 color = albedo * vec3(0.4);
color += albedo * NdotL;
vec3 E = normalize(v_eyedir.xyz);
vec3 R = reflect(-L, N);
float specular = pow(max(dot(R, E), 0.0), 64.0);
color += vec3(0.8, 0.8, 0.8) * specular;
FragColor.rgb = color;
FragColor.a = 1.0;
}