Solar System Simulation with Phong Lighting
3D solar system with real-time Phong shading. (CS 405 course project)
CS 405 (Computer Graphics) Project 3 had two parts: finish a hierarchical scene graph so transformations propagate correctly from parent to child, and add real per-pixel lighting to a WebGL animation of the Sun, Earth, and Moon - then extend the scene yourself.

Propagating transforms through the scene graph
The starter code left SceneNode.draw unimplemented. Each node needs to combine its own TRS (translation-rotation-scale) transform with the transform it inherited from its parent, across four matrices at once (MVP, ModelView, Normal, Model), then hand the combined result down to its children recursively:
draw(mvp, modelView, normalMatrix, modelMatrix) {
var transformedMvp = MatrixMult(mvp, this.trs.getTransformationMatrix());
var transformedModelView = MatrixMult(modelView, this.trs.getTransformationMatrix());
var transformedNormals = MatrixMult(normalMatrix, this.trs.getTransformationMatrix());
var transformedModel = MatrixMult(modelMatrix, this.trs.getTransformationMatrix());
if (this.meshDrawer) {
this.meshDrawer.draw(transformedMvp, transformedModelView, transformedNormals, transformedModel);
}
this.children.forEach((child) =>
child.draw(transformedMvp, transformedModelView, transformedNormals, transformedModel)
);
}This is what makes the Moon orbit the Earth while the Earth orbits the Sun, without either body's code knowing about the other - the Moon node is just a child of the Earth node, and the recursion takes care of composing the motion.
Phong lighting in the fragment shader
The base shader only had flat ambient light. I added the diffuse and specular terms of the Phong reflection model directly in GLSL, using a fixed light position and a Phong exponent of 8.0 for the specular highlight:
float ambient = 0.35;
vec3 lightdir = normalize(lightPos - fragPos);
float diffuseFactor = dot(normal, lightdir);
diff = max(diffuseFactor, 0.0);
vec3 viewDir = normalize(-vPosition);
vec3 reflectDir = reflect(-lightdir, normal);
float specularFactor = dot(viewDir, reflectDir);
spec = pow(max(specularFactor, 0.0), phongExp); // phongExp = 8.0
gl_FragColor = texture2D(tex, vTexCoord) * (ambient + diff + spec);The Sun itself is flagged isLightSource and skips this calculation entirely, so it renders as a flat, fully-lit texture instead of being shaded by its own light.
Extending the scene: adding Mars
The last task was scene composition rather than shader work: add Mars as a new child node of the Sun, with its own texture, an offset position (-6 units on the X-axis), a uniform 0.35 scale, and a rotation speed 1.5x the Sun's - proving the scene graph from Task 1 could take a new body without any special-casing.
Result
A working solar system animation with correct hierarchical orbital motion across four bodies (Sun, Earth, Moon, Mars) and physically-inspired ambient/diffuse/specular lighting on each of them.