Light makes depth readable. WebGL has no lights of its own: every model of light is shader arithmetic you write. The thirteenth part of webgl-helper.js adds a sphere, used as BookNest's globe bookend, and a standard vertex shader that hands the fragment shader everything lighting needs, in world space:
// Section 3.9: a unit sphere (BookNest's globe bookend) in GLH.box()'s vertex layout
GLH.sphere = function (rows = 12, columns = 16) {
const vertices = [], indices = [];
for (let r = 0; r <= rows; r++) {
for (let c = 0; c <= columns; c++) { // latitude theta, longitude phi
const theta = r * Math.PI / rows, phi = c * 2 * Math.PI / columns, s = Math.sin(theta);
const p = [s * Math.sin(phi), Math.cos(theta), s * Math.cos(phi)];
vertices.push(...p, ...p, c / columns, 1 - r / rows); // on a unit sphere, normal = p
}
}
for (let r = 0; r < rows; r++) {
for (let c = 0; c < columns; c++) { // two counterclockwise triangles
const a = r * (columns + 1) + c, b = a + columns + 1;
indices.push(a, b, a + 1, a + 1, b, b + 1);
}
}
return { vertices: new Float32Array(vertices), indices: new Uint16Array(indices) };
};
// Section 3.9: a vertex shader passing world-space position, normal and uv to lighting code
GLH.litVS = `#version 300 es
in vec3 aPosition; in vec3 aNormal; in vec2 aUV;
uniform mat4 uViewProjection, uModel; uniform mat3 uNormalMatrix;
out vec3 vPosition, vNormal; out vec2 vUV;
void main() {
vec4 world = uModel * vec4(aPosition, 1.0);
vPosition = world.xyz; vNormal = uNormalMatrix * aNormal; vUV = aUV;
gl_Position = uViewProjection * world;
}`;
GLH.lit = (gl, fs, vs = GLH.litVS) => GLH.load(gl, vs, fs);