Here is some code from my test application
fragment shader:
uniform sampler2D NMAP;
varying vec4 L;
varying vec4 H;
varying vec2 P;
varying float q;
const int ns = 40;
void main(void)
{
gl_TexCoord[0].s /= q;
float dB = 1.0 / ns;
vec2 dC = P * dB;
vec2 C = gl_TexCoord[0].st + dC;
float cH = texture2D(NMAP, C.st).a;
float pH = 0.0;
float B = 1.0 - dB;
while (cH < B)
{
pH = cH;
B -= dB;
C += dC;
cH = texture2D(NMAP, C.st).a;
}
float t = (B + dB - pH) / (cH - pH + dB);
C += P * (dB * t - dB);
vec4 N = texture2D(NMAP, C.st);
vec3 Nt = N.xyz;
Nt = Nt * 2.0 - 1.0;
Nt = normalize(Nt);
float diff = max(dot(Nt, normalize(L.xyz)), 0.0);
float spec = max(dot(Nt, normalize(H.xyz)), 0.0);
spec = pow(spec, 512.0);
gl_FragColor = gl_FrontMaterial.ambient + (diff *
gl_FrontLightProduct[0].diffuse + spec *
gl_FrontLightProduct[0].specular);
gl_FragColor.a = smoothstep(0.0, 1.0, N.a);
}
vertex shader:
const vec4 EYE = vec4(0.0, 0.0, 0.0, 1.0);
const vec4 SRC = vec4(0.0, 0.0, 0.0, 1.0);
varying vec4 L;
varying vec4 H;
varying vec2 P;
varying float q;
uniform float s;
void main(void)
{
vec3 T;
vec3 B;
vec3 N;
N = normalize(gl_Normal);
T = normalize(vec3(N.z, 0.0, -N.x));
B = cross(N, T);
mat4 iTBN = mat4(
T.x, B.x, N.x, 0.0,
T.y, B.y, N.y, 0.0,
T.z, B.z, N.z, 0.0,
0.0, 0.0, 0.0, 1.0
);
vec4 E = gl_ModelViewMatrixInverse * EYE - gl_Vertex;
E = iTBN * E;
L = gl_ModelViewMatrixInverse * SRC - gl_Vertex;
L = iTBN * L;
H = E + L;
P = -E.xy * s / E.z;
q = sqrt(1.0 - gl_Vertex.y * gl_Vertex.y);
gl_Position = ftransform();
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[0].s *= q;
}
rendering routine:
glBegin(GL_QUADS);
for (int i = 0; i < T; ++i)
for (int j = 0; j < P; ++j)
{
float3 N = 0.25f * (
vertices[(i + 0) * (P + 1) + j + 0] +
vertices[(i + 0) * (P + 1) + j + 1] +
vertices[(i + 1) * (P + 1) + j + 1] +
vertices[(i + 1) * (P + 1) + j + 0]);
glNormal3fv(N);
glTexCoord2f(0.0f, 0.0f);
glVertex3fv(vertices[(i + 0) * (P + 1) + j + 0]);
glTexCoord2f(1.0f, 0.0f);
glVertex3fv(vertices[(i + 0) * (P + 1) + j + 1]);
glTexCoord2f(1.0f, 1.0f);
glVertex3fv(vertices[(i + 1) * (P + 1) + j + 1]);
glTexCoord2f(0.0f, 1.0f);
glVertex3fv(vertices[(i + 1) * (P + 1) + j + 0]);
}
glEnd();
Here verices represents array of points on unit sphere (normal vectors
are the same in fact)
float3 N = 0.25f * … - flat normal calculation.