Alumpath
Dartmouth College Logo

Dartmouth College - COSC 77 – Computer Graphics

Phong-Shaded Solar System Animation

Assignment Problem:

Create an animation of a simplified solar system (e.g., Sun, Earth, Moon) with Phong shading using OpenGL (or a modern graphics API wrapper like LWJGL for Java, or WebGL for JavaScript if specified). The planets should orbit and rotate. Implement vertex and fragment shaders for lighting. Allow basic keyboard camera controls.

Our Suggested Approach

1. Understand Modern OpenGL Rendering Pipeline (Programmable Shaders):

  • Vertex Shader: Processes each vertex. Responsible for transformations (model, view, projection matrices) and passing attributes (like normals, texture coordinates) to the fragment shader.
  • Fragment Shader: Processes each pixel (fragment). Responsible for calculating the final color of the fragment, typically by implementing lighting models like Phong.
  • Vertex Buffer Objects (VBOs): Store vertex data (positions, normals, colors, texture coordinates) on the GPU.
  • Vertex Array Objects (VAOs): Configure how vertex attributes are read from VBOs.
  • Shader Programs: Linked vertex and fragment shaders.
  • Uniforms: Variables passed from the CPU application to shaders (e.g., transformation matrices, light properties, camera position).

2. Scene Setup and Object Modeling:

  1. Celestial Bodies: Represent Sun, Earth, Moon as spheres. Create sphere geometry (vertices, normals, and indices for glDrawElements).
  1. Transformations:
  • Model Matrix: Translates, rotates, scales each celestial body to its correct position and orientation in world space.
  • View Matrix: Transforms world space coordinates to camera/view space (camera position and orientation).
  • Projection Matrix: Transforms view space to clip space (perspective or orthographic projection).
  1. Hierarchy: Moon orbits Earth, Earth orbits Sun. This can be managed by composing model matrices (e.g., Moon's matrix = Earth's matrix * Moon's local orbit/rotation).

3. Implementing Phong Shading in Shaders:

  1. Phong Reflection Model: Color = Ambient + Diffuse + Specular
  1. Vertex Shader Responsibilities:
  • Transform vertex position to clip space: gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(vertexPosition, 1.0);
  • Transform vertex normal to view space: viewSpaceNormal = mat3(transpose(inverse(modelViewMatrix))) * vertexNormal; (modelViewMatrix is view*model)
  • Calculate vertex position in view space: viewSpacePosition = vec3(modelViewMatrix * vec4(vertexPosition, 1.0));
  • Pass viewSpaceNormal and viewSpacePosition to the fragment shader (as varying or out variables).
  1. Fragment Shader Responsibilities (per fragment):
  • Normalize the incoming viewSpaceNormal.
  • Light Properties (Uniforms): Light position (in view space), light color (ambient, diffuse, specular).
  • Material Properties (Uniforms): Object's ambient, diffuse, specular reflectivity (colors), and shininess coefficient.
  • Ambient Term: ambientColor = materialAmbientColor * lightAmbientColor;
  • Diffuse Term:
  1. Light direction vector L = normalize(lightPosition_viewSpace - fragmentPosition_viewSpace);
  1. diffuseIntensity = max(dot(viewSpaceNormal, L), 0.0);
  1. diffuseColor = materialDiffuseColor * lightDiffuseColor * diffuseIntensity;
  • Specular Term:
  1. View direction vector V = normalize(-fragmentPosition_viewSpace); (from fragment to eye)
  1. Reflection vector R = reflect(-L, viewSpaceNormal);
  1. specularIntensity = pow(max(dot(R, V), 0.0), materialShininess);
  1. specularColor = materialSpecularColor * lightSpecularColor * specularIntensity;
  • Final Color: gl_FragColor = vec4(ambientColor + diffuseColor + specularColor, 1.0); (Clamp if necessary).

4. Animation Loop:

  1. In each frame:
  1. Update time.
  1. Calculate new positions/rotations for Earth and Moon based on time (orbital angles, rotation angles).
  1. Update model matrices accordingly.
  1. Update view matrix based on camera controls.
  1. Clear screen buffers (glClear).
  1. Use the shader program.
  1. Set uniforms (matrices, light properties, material properties, camera position for specular calculation if needed).
  1. Bind VAO for Sun, draw Sun.
  1. Bind VAO for Earth, update its model matrix uniform, draw Earth.
  1. Bind VAO for Moon, update its model matrix uniform, draw Moon.
  1. Swap buffers.

5. Keyboard Camera Controls:

  • Implement functions to modify camera position (e.g., move forward/backward, strafe left/right, move up/down) or camera target/angles (pan, tilt, zoom) based on keyboard input.
  • Recalculate the view matrix when camera parameters change.

Illustrative Code Snippet

glsl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// --- Vertex Shader (Conceptual GLSL) ---
/*
#version 330 core
layout (location = 0) in vec3 aPos;   // Vertex position
layout (location = 1) in vec3 aNormal; // Vertex normal

out vec3 FragPos_viewSpace; // Output to fragment shader
out vec3 Normal_viewSpace;  // Output to fragment shader

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat3 normalMatrix; // transpose(inverse(mat3(model*view))) or mat3(model*view) if no non-uniform scaling

void main() {
    vec4 viewPos = view * model * vec4(aPos, 1.0);
    FragPos_viewSpace = vec3(viewPos);
    // Normal_viewSpace = normalize(normalMatrix * aNormal); // More robust
    Normal_viewSpace = normalize(mat3(view * model) * aNormal); // Simpler if no non-uniform scaling
    gl_Position = projection * viewPos;
}
*/

// --- Fragment Shader (Conceptual GLSL for Phong) ---
/*
#version 330 core
out vec4 FragColor;

in vec3 FragPos_viewSpace;
in vec3 Normal_viewSpace;

struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};
uniform Material material;

struct Light {
    vec3 position_viewSpace; // Light position in view space
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform Light light;

uniform vec3 viewPos_viewSpace; // Camera position in view space

void main() {
    // Ambient
    vec3 ambient = light.ambient * material.ambient;

    // Diffuse
    vec3 norm = normalize(Normal_viewSpace);
    vec3 lightDir = normalize(light.position_viewSpace - FragPos_viewSpace);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * (diff * material.diffuse);

    // Specular
    vec3 viewDir = normalize(viewPos_viewSpace - FragPos_viewSpace);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * (spec * material.specular);
    
    FragColor = vec4(ambient + diffuse + specular, 1.0);
}
*/

// --- Main Application Loop (e.g., C++ with GLFW/GLEW or Java with LWJGL - Conceptual) ---
/*
void mainLoop() {
    while (!windowShouldClose()) {
        float currentTime = getTime();
        // processInput(window, camera); // Update camera based on keyboard

        // Update planet positions/rotations
        float earthOrbitAngle = currentTime * 0.5f;
        mat4 earthModel = translate(mat4(1.0f), vec3(cos(earthOrbitAngle)*EARTH_ORBIT_RADIUS, 0, sin(earthOrbitAngle)*EARTH_ORBIT_RADIUS));
        earthModel = rotate(earthModel, currentTime * EARTH_ROTATION_SPEED, vec3(0,1,0));

        float moonOrbitAngle = currentTime * 2.0f;
        mat4 moonLocalTransform = translate(mat4(1.0f), vec3(cos(moonOrbitAngle)*MOON_ORBIT_RADIUS, 0, sin(moonOrbitAngle)*MOON_ORBIT_RADIUS));
        mat4 moonModel = earthModel * moonLocalTransform; // Moon orbits Earth
        moonModel = rotate(moonModel, currentTime * MOON_ROTATION_SPEED, vec3(0,1,0));
        
        mat4 sunModel = scale(mat4(1.0f), vec3(SUN_SCALE)); // Sun at origin, maybe rotating

        // Get view and projection matrices from camera
        // mat4 view = camera.GetViewMatrix();
        // mat4 projection = perspective(fov, aspect_ratio, near_plane, far_plane);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        shaderProgram.use();
        // shaderProgram.setMat4("projection", projection);
        // shaderProgram.setMat4("view", view);
        // shaderProgram.setVec3("viewPos_viewSpace", camera.Position_viewSpace); // Or transform camera.Position to view space
        // shaderProgram.setLightProperties(...); // Set light uniforms

        // Draw Sun
        // shaderProgram.setMaterialProperties_Sun(...);
        // shaderProgram.setMat4("model", sunModel);
        //glBindVertexArray(sunVAO);
        //glDrawElements(...) / glDrawArrays(...);

        // Draw Earth
        // shaderProgram.setMaterialProperties_Earth(...);
        // shaderProgram.setMat4("model", earthModel);
        // glBindVertexArray(earthVAO);
        // glDrawElements(...);

        // Draw Moon
        // ... same for Moon with moonModel ...

        swapBuffers();
        pollEvents();
    }
}
*/

Explanation & Key Points

Programmable Shaders (Vertex and Fragment)

Modern OpenGL relies on shaders written in GLSL. The Vertex Shader transforms each vertex's position and prepares data (like normals) for the next stage. The Fragment Shader calculates the final color of each pixel, implementing the lighting model.

Transformation Matrices (Model, View, Projection)

Objects are positioned, oriented, and scaled in the scene using a Model matrix. The View matrix transforms the scene from world coordinates to the camera's viewpoint. The Projection matrix (usually perspective) defines the viewing frustum and maps 3D coordinates to 2D screen coordinates.

Phong Shading Model

Phong shading calculates lighting by combining three components for each light source: Ambient (general background light), Diffuse (light scattered सरफेस from a surface, dependent on light angle), and Specular (shiny highlights, dependent on light angle and view angle). These are calculated per-fragment for smooth results.

Normal Vectors and Normal Matrix

Normals are vectors perpendicular to a surface, crucial for lighting calculations. When model transformations (especially non-uniform scaling) are applied, normals must be transformed correctly using the transpose of the inverse of the upper-left 3x3 part of the model-view matrix (the 'normal matrix').

Hierarchical Transformations for Orbits

To make the Moon orbit the Earth, and the Earth orbit the Sun, hierarchical transformations are used. The Moon's final position is calculated by applying its local orbital/rotational transform relative to the Earth's already transformed position.

Animation Loop

The main game/animation loop updates object positions (based on time for orbits and rotations), processes user input for camera control, clears the screen, sets up shader uniforms (like matrices and light/material properties), and then draws each object.

Vertex Data (VAOs/VBOs)

Sphere geometry (vertex positions, normals) is defined and uploaded to the GPU using Vertex Buffer Objects (VBOs). Vertex Array Objects (VAOs) configure how this data is laid out and accessed by the vertex shader.

Keyboard Camera Control

Implementing camera controls allows the user to move around the scene (e.g., zoom, pan, orbit). This involves updating the View matrix based on keyboard inputs.

Key Concepts to Master

3D Graphics PipelineOpenGL/WebGLShaders (GLSL - Vertex & Fragment)Phong Shading (Ambient, Diffuse, Specular)Transformation Matrices (Model, View, Projection)Normal Vectors & Normal MatrixHierarchical ModelingAnimation LoopVertex Buffer Objects (VBOs)Vertex Array Objects (VAOs)Uniform Variables in ShadersVector Math (dot product, normalize, reflect)

How Our Tutors Elevate Your Learning

  • OpenGL/Graphics API Setup: Helping you set up the OpenGL context, load shaders, create VBOs/VAOs, and draw basic shapes.
  • Shader Writing (GLSL): Guiding you through writing the vertex shader for transformations and the fragment shader for Phong lighting calculations (dot products, normalization, etc.).
  • Matrix Transformations: Explaining matrix multiplication order for model, view, and projection, and how to implement hierarchical transformations for the orbits.
  • Lighting Logic: Clarifying how the ambient, diffuse, and specular components are calculated and combined, and how to pass light/material properties as uniforms.
  • Debugging Shaders: Shader debugging can be tricky. Helping you use tools (like RenderDoc if available for your platform) or print-debugging techniques (e.g., outputting intermediate values as colors) to find issues.
  • Camera Implementation: Assisting with the math and logic for implementing keyboard-based camera controls (e.g., updating camera position/target and recalculating the view matrix).
  • Generating Sphere Geometry: Discussing algorithms or providing code snippets to generate vertex and normal data for a sphere.

Need Personalized Help with Your Assignment?

Our human experts provide tailored guidance for your specific course assignments, helping you understand concepts and succeed.