Code 3

DayTopicDescriptionWebGLGodot
Tue, 13. MayHello Code 3Introduction to the course and setup instructions.Setup editor, hello worldInstall Godot, hello world
Tue, 20. MayRendering PipelineOverview of the rendering pipeline and its stages.Triangle through pipeline
Wed, 21. MaySimple CubeCreating and rendering a simple 3D cube.Create simple 3D geometry
Thu, 22. MayCylinder IMesh Data in Godot I-Modeling and rendering a circular disc
Fri, 23. MayCylinder IIMesh Data in Godot I-Extruding the disc to become a Cylinder
Tue, 27. MayLightingAdd interaction and lightsNormals, lighting, shading
Wed, 28. MayIntroduction to Shaders in GodotShaders are Materials - Materials are Shaders!-How is the rendering pipeline controlled by a game engine
Tue, 03. JuneToon ShaderA Toon Shader made out of cell-coloring and outline drawing-Setup Multipass rendering and implement a gradient lookup from a texture
Wed, 04. JuneSpot lightFixing bugs and improve lightingDebugging in WebGL-
Thu, 05. JuneTexturesTexturesAdding textures-
Fri, 06. JuneProjectProject-Finish shader

For the final project, you can choose between WebGL and Godot. The project will be presented in the last week of the course. Please upload your project to GitHub using this GitHub Classroom link: GitHub Classroom. Add a readme.md file so that we can understand your project. The readme should include:

  • A short description of your project
  • How to run it (e.g., which files to open, how to start the server, etc.)
  • A list of features you implemented
  • A list of features you wanted to implement but didn’t have time for
  • Your name and email address

hfu.li/COD-III

QR-Code QR-Code

May 15, 2025

Subsections of Code 3

Hello Code 3

Goals

Get the development environment set up and running.

  • Visual Studio Code for JavaScript/WebGL
  • Install Godot and get familiar with the script editor

Assignment

Questions

  • TBD

Material

Insights

  • A recent discussion on Bluesky about downgrading from D3D12 to 11 due to the complexity of the API and the need for a more straightforward approach to graphics programming.
Sep 28, 2024

The Rendering Pipeline

Goals

Render one triangle on the screen using WebGL.

Assignment

Use the repository from last time and follow the instructions in the slides:

eyes eyes means that the code is already in the repository and you just need to look at it.

clipboard clipboard means you can copy-paste the code and it should work.

memo memo means that you need to create a new file

o o indicates that you need to do more than just copy-paste the code.

In any case you need to understand what you are doing.

Material

Tasks (for the “Hello WebGL” demo)

In the following, some tasks are listed that need to be implemented in order to better understand the implemented code.

Simple

(small and easy code changes)

  • Change the size of the triangle
  • Change the color of the triangle
  • Change the color of the background

Complex

(needs new functions)

Tasks (for the “Hello 3D” demo)

In the following, some tasks are listed that need to be implemented in order to better understand the implemented code.

Simple

(small and easy code changes)

  • Change the size of the triangles
  • Change the color of the triangles
  • Change the color of the background

Complex

(needs new functions)

  • Render the triangles as points (check webgl-points-lines-triangles for help)
  • Render the triangles as lines (check webgl-points-lines-triangles for help)
  • Change the perspective of the camera (check webgl-camera for help)
  • Change the second parameter in the gl.uniformMatrix4fv(modelMatrixLocation, false, modelViewMatrix); function call to true and check the result.
  • Draw one thousand triangles instead of two.

Insights

May 15, 2025

A Simple Cube

Goals

Flat shaded cube with rotation animation and perspective camera

Assignment

Use the repository from last time and follow the instructions in the slides:

eyes eyes means that the code is already in the repository and you just need to look at it.

clipboard clipboard means you can copy-paste the code and it should work.

memo memo means that you need to create a new file

o o indicates that you need to do more than just copy-paste the code.

In any case you need to understand what you are doing.

Material

List of things to implement extending the cube 3D demo

In the following, some tasks are listed that need to be implemented in order to better understand the implemented code.

Simple

(small and easy code changes)

  • Change the size of the cube
  • Change the color of the cube
  • Change the position of the cube to the top left of the canvas by modifying the viewport
  • Change the rotation speed of the cube

Complex

(needs new functions)

  • Change the color of the cube so that each edge has the same color on both adjacent faces, but there is a color gradient on each face.
  • Let the cube pulsate (change its size) in a sinusoidal way.
  • Draw a thousand cubes instead of one.
  • Change the camera to an orthographic camera (check gl-matrix for a helper function)

Insights

  • TBD
May 15, 2025

Cylinder

Goals

Flat and rounded surfaces with normals. A cylinder generated in code. In Godot.

  • Understand how procedural geometry can be created within a game engine
  • Grasp how geometry data is structured
  • Improve coding skills

Background

Meshes

In game engines as well as in content creation/3D modelling tools such as blender the term mesh represents three-dimensional geometry.

Meshes in 3D Content Creation Software vs Meshes in Game Engines

With some knowledge in creating meshes in blender we remember that they are made out of vertices (singular: vertex), edges and faces.

  • Vertices are positions in three-dimensional spaces
  • Edges always connect two vertices, and
  • Faces are bound by edges and spanned by vertices. Faces are the visible part of the geometry when a mesh is rendered. The number of vertices (and edges) a face is made of can vary: triangles, quadrilaterals (quads) and polygons with more than four vertices (n-gons) are possible.

Mesh editing features such as loop cut, dissolve, extrude, bevel, boolean, just to name a few, must be able to perform quickly and interactively even on large mesh geometries with huge numbers of vertices, edges and faces.

For that reason, 3D content creation software stores mesh geometry in a way that explicitly keeps adjacency information. That way, queries such as “which vertices is a given face made of”, “which edges belong to a given face”, “what faces/edges are connected to a given vertex” etc. can be answered in short time without searching through the entire mesh geometry for each query.

Storage schemes allowing this kind of information retrieval are complex and will not be covered in this lesson.

Meshes in Game Engines

Most game engines do not allow mesh geometry to be edited and altered as profoundly as in content creation software. The main purpose mesh geometry serves in a game engine is to be rendered in a fast and efficient way. Thus the way geometry data is stored in a game engine is highly optimized on how the underlying rendering hardware (the GPU) expects and processes geometry. In contrast to content creation tools, most game engines store geometry as a set of vertices and triangular faces.

  • Vertices are (at least) positions in space. They can contain additional information such as
    • UVs (texture coordinates)
    • Normals
  • Faces in game engines are (with minor exceptions) always triangular - three vertices span one triangle (tri). Some older GPU software interfaces can additionaly display quads (made of four vertices) or lines (two vertices) instead of tris.

Meshes in Godot

Godot stores 3D Meshes in instances of the Mesh class, serving as a property of MeshInstance3D located in the scene graph.

As godot project editors, we cannot look into the contents of a Mesh instance directly. Most Mesh objects are imported as 3D Geometry from a 3D editing program. To create mesh geometry in GDScript code, we can use a child class of Mesh: the ArrayMesh class. Instancess of ArrayMesh can be used at all places where Mesh instances are required, e. g. as part of a MeshInstance3D scene graph object.

If we want to understand how mesh data is kept internal, close-to-GPU parts of a game engine, we can look a the way we need to organize mesh data in an ArrayMesh instance.

Assignment

1. A Triangle

🔧 TODO

  • In the Godot Editor, add a MeshInstance3D object object to your turntable scene and add a new ArrayMesh to its Mesh property in the Inspector.

  • Make sure the object is placed centered on top of the turntable although it does not yet contain any visible geometry.

  • Attach a new GDScript to the MeshInstance3D object.

  • Add a simple triangle in the code’s _ready() function according to the example code provided in the ArrayMesh documentation. Other than the code found there,

    • DO NOT create new instances of ArrayMesh and MeshInstance3D (DON’T CALL ArrayMesh.new() or MeshInstance3D.new()). Your code is already part of a MeshInstance3D object and its mesh property already contains an instantiated ArrayMesh.
  • Your code should look like this:

    func _ready():
      var vertices = PackedVector3Array()
      vertices.push_back(Vector3(0, 0.3, 0))
      vertices.push_back(Vector3(0.3, 0, 0))
      vertices.push_back(Vector3(0, 0, 0.3))
    
      # Initialize and arrange the data for the single surface
      var arrays = []
      arrays.resize(Mesh.ARRAY_MAX)
      arrays[Mesh.ARRAY_VERTEX] = vertices
    
      # Add the data as a surface to the ArrayMesh stored in the mesh property
      mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)	
  • Make sure that you can see the triangle when running your turntable application. You might need to turn the table to make the triangle appear.

  • Toy around with different vertex positions of the triangle!

  • Add three more triangles to define two sides of a cube: two triangles for one of the front sides and two triangles for the top. If you cannot figure out the 3D coordinates of the vertices: Draw a scribble of what you intend to create in a 3D coordinate system!

💡Insights

  • What is a PackedVector3Array?
  • Draw an image how the vertices array is a part of the arrays array.
  • Why is the triangle shown only from one side? What happens if you exchange two of the three vertices within the vertices array?
  • What coordinate system are the vertex-coordinates in? How are they affected by the MeshInstance3D’s transform properties?
  • Why can’t we see a color difference in the two sides of the cube?

2. Normals and Indices

Start with the above mentioned two sides of a cube: Your vertices array should contain 12 entries (= 4 triangles, = 2 squares).

🔧 TODO Normals

  • Add another PackedVector3Array named normals to the surface arrays and store it at the index Mesh.ARRAY_NORMAL array position.
  • Add (using push_back) the same number of normal vectors to the normals array as there are vertices vectors in the vertices array.
  • Choose the normals for the upwards-looking triangles to direct along the positive Y-axis
  • Choose the normals for the sidewards-looking triangles to direct into the triangles’ direction.

💡Insights

  • What are the correct normal directions?
  • Each of the two faces consist of two triangles. For each of the faces: How can we re-use the four vertices and four normals to define two triangles spanning them?
  • Why do we need 12 entries in the vertices and normals array although there are only 8 different combinations of normals/vertices and only six different positions?

🔧 TODO Indices

  • Add a PackedInt32Array named indices to the surface arrays and store it at the index Mesh.ARRAY_INDEX array position.
  • Reduce the vertices and the normals arrays to contain only eight (instead of 12) entries
  • Create a list of indices into the vertices and normals arrays with three consecutive indices making up a triangle. Store that index list in the indices array.
  • Make sure your two squares look like before.

3. A Disc

💡Think! Draw! Think again!

  • Imagine a disc made out of triangles with each triangle spanning the disc’s center point and two vertices on the disc’s rim. Let’s call such a triangle, wich could be seen as a slice of a pizza or a cake, a segment.
  • In this exercise, we want to create an algorithm that takes the count of segments as an input parameter and builds a disc shaped surface made out of segments triangles. For example: Build a disc out of 8 segments (8 pizza slices).
  • For a given segments value: How many vertices, normals and indices will you need?
  • To create the disc within the X-Z-plane of Godot’s 3D coordinate system: How would you calculate the X and the Z coordinate of segments points equally distributed on an imaginative circle with a given radius? Hints:
    • Equally distributed means: all segments (pizza slices) have the same angle.
    • Draw the X-Z-Axis with the circle. Let the X-Axis be at angle 0. Draw the first slice.
    • Draw a rectangular triangle connecting the upper slice vertex directly with the X-Axis.
    • Remember the rules you learned about rectangular triangles’ edge lengths, angles, and sin and cos back in the 8th or 9th grade at school! Apply those rules to your image and think about the values that are given (angle, radius) and the information you want to calculate (x, z)!

🔧 TODO

  • Create an algorithm building the disc surface out of triangles
    • Initialize the arrays vertices, normals and indices
    • Add a first center vertex and normal
    • Add a first rim vertex and normal at the X-Axis
    • Start a for loop. In the loop body,
      • Add the next vertex to the vertices array on the rim (using the sin and cos formulae). Add its corresponding normal to the normals array showing up along the global Y-Axis.
      • Add a triangle made out of the center vertex, the vertex added in the last loop iteration (or the first rim vertex at all (on the X-Axis)). This means adding three indices to the indices array.
    • After finishing the loop with the penultimate triangle, close the disc (add the last pizza slice) connecting the center vertex, the vertex added in the last loop iteration and the very first vertex on the rim (the one on the X-axis). This will add a last triplet of vertices to the vertex array.

3. A Cylinder

  • Make the existing disk the top of a cylinder (move it up).
  • Build a second disk to be the bottom of the cylinder (facing down).
  • Generate a third surface with each rectangular segment (made out of two triangles) covering a part of the rounded lateral surface of the cylinder. How would you calculate the normals for the lateral surface’s vertices to make this lateral surface segments appear as one rounded surface (a.k.a “smooth shaded”)?

Cylinder Part II

All contents presented on this day this day is covered by the previous chapter (Cylinder)

Lighting

Goal

We first restructure the code to better understand the transformations. Then we add controls to interact with the cube. After that, we add a light source and implement basic lighting calculations.

Assignment

Use the repository from last time and follow the instructions in the slides:

eyes eyes means that the code is already in the repository and you just need to look at it.

clipboard clipboard means you can copy-paste the code and it should work.

memo memo means that you need to create a new file

o o indicates that you need to do more than just copy-paste the code.

In any case you need to understand what you are doing.

Material

Tasks (for the “Interactive cube” demo)

In the following, some tasks are listed that need to be implemented in order to better understand the implemented code.

Simple

(small and easy code changes)

  • Change the position of the cube by modifying the model matrix
  • Change the camera so that the cube is viewed from the top
  • Change the rotation speed of the cube (the impact of the mouse or sliders needs to be changed)
  • Experiment with the mouse controls and observe how the cube rotates. Why does it not always rotate in the same direction as the mouse is moved?

Complex

(needs new functions)

  • Add mouse wheel support to zoom in and out of the cube.
  • Draw a thousand cubes instead of one. Take care that all cubes are rotated simultaneously.
  • Change the camera to an orthographic camera (check webgl-camera for help)

Tasks (for the “Illuminated cube” demo)

In the following, some tasks are listed that need to be implemented in order to better understand the implemented code.

Simple

(small and easy code changes)

  • Change the position of the light sources
  • Change the camera and observe the highlights on the cube
  • Change the color of the light sources
  • Add checkboxes to turn lighting on and off

Complex

(needs new functions)

  • Add more light sources to the scene.
  • Animate the light sources so that they move around the cube.
  • Add a slider to control the intensity of the light sources.
  • Add some control to change the color of the light sources.

Questions

  • TBD

Material

  • TBD

Insights

  • TBD

Introduction to Rendering and Shaders in Godot

The Rendering Pipeline

In order to render a picture from the data contained within the scene tree (or scene graph) in a game engines such as Godot, all data needs to be prepared and presented to the rendering pipeline. This is done by traversing the scene graph. During this process, each node within the scene is visited once and all the data it contains is sent to the rendering pipeline. A typical scene graph traversal is performed “depth first”, that is, whenever a node with children and siblings is currently visited, the next step will be to first visit its list of children and then continue with its next sibling.

01 01

Information such as the transformation (position, rotation, scale) as well as material settings are used to control the state of the rendering pipeline. The geometry (the meshes) present at the various scene nodes is directly “pumped” through the pipe. The pipeline’s task is then to generate a pixel image out of the geometry under the various current settings (also called the render state).

🪛 TODO

Imagine (or better, generate) a scene with hierarchies (such as a simple solar system) and perform a depth-first traversal with pen and paper. In which order are the scene nodes visited?

02 02

Render Pipeline Sub Processes

While this task can be broken down in quite a number of individual steps, each performed one after the other, we will only look at two sub-processes performed within the rendering pipeline here:

03 03

  1. The Geometry part takes each incoming vertex with its (x, y, z) coordinates present in Model Space (the coordinates that the vertex was assigned when it was modeled, e. g. in Blender’s Edit Mode). It then converts the vertex’ coordinates into Screen Space. To to this, it applies all transformations present in the path from the scene root to the model. In addition it performs the (inverted) camera transformation. As a result, the vertex new coordinates are then in screen space, allowing to identify the pixel in the resulting image where the respective vertex will be positioned.

    🪛 TODO

    In the above solar system example, what transformations must be applied to all vertices of the Moon mesh?

    As the information which vertices span which triangles do not change, after the Geometry step each triangle of the geometry can be “placed” on the to-be-generated pixel image.

  2. With the knowledge which pixels in the resulting pixel images will be covered by which triangle, each of these pixels can now be assigned a color. This is the task of the Raster part. For each pixel a color calculation is performed, typically taking into account the pixel’s Normal (where the mesh surface at the given screen pixel is “looking at”), the lighting situation (where is light coming from? In which intensity and color?), the material’s base color (its albedo) and/or, if present, the texture(s) to use for the material, the texture coordinate (UV) and possible vertex colors present at the screen pixel in question. For any of that information only present at the mesh’s vertices (such as normal, UV, vertex colors…), a weighted average is calculated for the pixel based on the distance of the pixel to the containing triangle’s vertices. All the pixel-related information present as an input to calculate an individual pixel are called a Fragment.

Shaders in Game Engines

In game engines, the exact operations to be performed to transform each vertex in the Geometry part and to calculate each fragments resulting pixel color are described in the materials stored in the scene graph. When using standard materials (such as in Godot), the shader code to use is defined by the engine. All a user has to do is to set the parameters (the transformations on each node as an input to the vertex shader and the various material settings such as albedo, transparency, roughness, emissive, … as input to the fragment shader).

Most engines allow users to define their own shader code together with their own set of shader parameters. In addition, most engines allow shader authors to access the standard shader input variables such as transformations given by the scene hierarchy and their transformations, the light settings, etc.

04 04

Shaders in Godot

Godot users can define their own shaders using a ShaderMaterial. This type of resource can be applied everywhere, where a material can be set, e.g. in the material setting for a mesh’s surface.

A ShaderMaterial must be given the file name of the shader code (with the “.gdshader” extension). When creating a new shader code file, an empty shader is created.

shader_type spatial;

void vertex() {
	// Called for every vertex the material is visible on.
}

void fragment() {
	// Called for every pixel the material is visible on.
}

Although both, vertex and fragment shaders contain an empty implementation, the shader already performs vertex transformation according to the scene settings and a simple pixel color calculation.

To switch off the vertex transformation, add

render_mode skip_vertex_transform;

to the top of the shader.

To switch off any color calculation, add

render_mode unshaded;

to the top of the shader.

🪛 TODO

Read the Godot Shader documentation and toy around with some simple shader settings.

In the vertex shader (with skip_vertex_transform set), implement your own vertex transformation by multiplying each vertex and normal with the ModelView matrix:

VERTEX = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);

Add a fraction of the normal to the vertex resulting in the geometry appearing thicker or thinner depending on how big the fraction is.

In the (unshaded) fragment shader, set the resulting color of the current fragment/pixel by applying a three-dimensional color (rgb, e.g. green: (0, 1, 0)) value scaled by the angle between a light direction directly from the current camera’s viewing direction and the fragment’s normal to the Albedo

ALBEDO = vec3(0, 1, 0) * dot(vec3(0, 0, 1), NORMAL);

Make the parameters such as the “thickness” used in the vertex shader or the base color or even the light direction in the fragment shader uniform variables and see how they can be controlled within Godot’s User Interface.

Toon Shading

01 01

Multipass Rendering

In Real-Time rendering many visual effects result from a combination of the same mesh being passed through the rendering pipeline more than once with each of these passes having a different shader performing the rendering.

To simulate the impression of a hand-drawn image in comic-book-style, we can combine two render passes.

  1. The first pass (cell pass) renders the geometry with usual transformation (no other than the transformation performed in Godot’s standard vertex shader). In the fragment shader, the object’s base color (either one color for the entire object or a color looked up from a texture accompanying the model) is shaded (darkened) in one of a very small number of discrete different brightness levels (e.g. three: light, medium, dark). This simulates the effect how a comic painter tries to visualize lighting: lacking a huge number of differently shaded color pencils for each color, they will draw the cell-like impression with hard transitions from one brightness level to the next.

  2. The second pass (outline pass) inflates the geometry in the vertex shader using the “normal-trick” from the last lesson. The result is entirely painted in black (or a user-defined color) in the fragment shader. This will generate the dark outline around the objects.

It might have sounded more intuitive to first mention the outline pass and then the cell pass, because the color is somewhat expected to paint over the inflated black silhouette, we will find out that it does not play a role in which order the passes are performed since making the cell-colored pixels replace the black outline will be realized purely by having the colored geometry being placed in front of the black inflated geometry within 3D space. Let’s find out how this is accomplished:

Multipass Rendering in Godot

Setting up multipass rendering in Godot is easy: Each ShaderMaterial resource allows to have a Next Pass property set. Simply assign another ShaderMaterial to that and you have the geometry associated with the outer ShaderMaterial travel twice through the rendering pipeline.

02 02

The Outline shader

Implementing the outline shader is straightforward. The vertex shader is configured to perform the standard transformation (NO render_mode skip_vertex_transform!!). The code blows up the geometry by adding a fraction of the normal to each vertex. The amount of this fraction will then become the stroke width and can be set by users when implemented as a uniform.

The fragment shader is even simpler: With render_mode unshaded set, no lighting calculation at all is performed. Instead, each pixel is assigned the same color. Preferably black or dark colors will generate the comic-bookish outline look .

shader_type spatial;

render_mode unshaded;
render_mode cull_front;

uniform vec3 outline_color : source_color;
uniform float stroke_width;

void vertex() {
	VERTEX += NORMAL * stroke_width;
}

void fragment() {
	ALBEDO = outline_color;
}

The most interesting part is the solution to how the black pixels of the silhouette are positioned behind the colored pixels from the cell pass. This is done by painting only the “rear-sided” triangles - those which are facing away from the camera. This way the (rather) concave inside of a half shell of the geometry is painted in black which will then be filled with the colored geometry. To achieve this, the cull mode which normally culls all triangles facing away from the viewer is reverted by the statement render_mode cull_front at the top of the shader.

The Cell Shader

To achieve the cell like look, we need to map the direction a pixel is looking at in 3D space (its normal) to one of the available brightness levels. There are many ways how this can be accomplished. We will use a 2D lookup table that’s easy to create and in addition gives us some creative headroom to define the look.

We create the 2D lookup table as a 2D pixel image that will be used as a texture in the cell pass’ fragment shader. Using an image painting program, we can paint a comic-like (cell-shaded) sphere. Typically one would draw a highlight using nested cells with increasing brightness at some upper left or right part of the sphere.

03 03

The sphere itself is not displayed as it is better to fill the entire square texture with the lowest brightness level. To generate the image, draw a helper circle (or imagine one) fitting exactly into the square texture and make sure not to paint any brightness highlights outside that circle. Make sure to NOT export that helper circle (the dotted line in the above image) to your shader texture. We will call this texture the gradient.

In the cell pass’ fragment shader we can use the normal at the to-be-rendered pixel to select which of the brightness cells should be used by projecting the normal onto the gradient texture and use the brightness of the texture pixel found there.

04 04

The black normal in the above image is (by calculation) positioned in the middle of the gradient texture and scaled to not exceed the gradient texture even when it would be in the image plane itself. Then it is projected (green dotted line) onto the image yielding texture coordinates to look up the pixel in the gradient texture. The (gray scale) color found there is used to multiply (shade) the object’s base color.

shader_type spatial;
render_mode unshaded;

uniform vec3 albedo : source_color;
uniform sampler2D tex : source_color;
uniform sampler2D gradient : source_color;

void vertex() {
}

void fragment() {
	vec3 cell = texture(gradient, vec2(0.5, -0.5) * NORMAL.xy + vec2(0.5, 0.5)).rgb;
	vec3 tex_col = texture(tex, UV).rgb;
	ALBEDO = albedo * tex_col * cell;
}

The shader accomplishing this has nothing to do in the vertex shader (other than the standard shader would do). The texture lookup in the gradient with the scaled and translated normal yields the cell gray scale color value. This is then combined with a given overall base color (albedo) and any given color texture associated with the object using a texture lookup with the UV coordinates averaged from the UVs provided at the mesh’s vertices.

Spotlight

Adding a spotlight in WebGL.

Plan

We start with the debug code, you get here: GitHub Classroom

  • Fix the mouse rotation
  • Fix the camera and view matrix if needed

We then add normals and a point light source which we turn into a spot light (using step function in shader)

Slides

Tasks

  • Make it a soft spot with smoothstep and a double slider for the inner and outer radius

Insights

  • TBD

Textures

Goal

We now add a texture to all faces of our cube.

Assignment

Use the repository from last time and follow the instructions in the slides:

eyes eyes means that the code is already in the repository and you just need to look at it.

clipboard clipboard means you can copy-paste the code and it should work.

memo memo means that you need to create a new file

o o indicates that you need to do more than just copy-paste the code.

In any case you need to understand what you are doing.

Material

Tasks (for the “Textured cube” demo)

In the following, some tasks are listed that need to be implemented in order to better understand the implemented code.

Simple

(small and easy code changes)

  • Change the texture by using a different image file.
  • Use texture coordinates larger than 1.0 to repeat the texture on the cube.
  • Change the TEXTURE_WRAP_S and TEXTURE_WRAP_T parameters to CLAMP_TO_EDGE or MIRROR_REPEAT and observe the difference.
  • Assign the texture coordinates as color using outColor = vec4(vTexcoord.s, vTexcoord.t, 0.0f, 1.0f); in the fragment shader. This will help you understand how texture coordinates are mapped to the cube’s surface.
  • Change the appearance of the texture by modifying the texture color using a variable in the fragment shader, e.g., vec3 texColor = texture(uTexture, vTexcoord);. Then modify this variable before assigning it to outColor.

Complex

  • Use different texture coordinates so that each side of the cube looks different. Try to map this texture to the cube: Dice Texture. You might start with these coded textures for debugging: UV Test, UV Checker Map.
  • Load two different textures and switch between them using a key press (e.g., spacebar).

Insights

  • TBD

Exam Submission

Learning Objectives

To pass this module’s exam, you have to show that you are

  • Capable of procedurally generating 3D geometry
  • Capable of setting up the rendering pipeline to transform vertices
  • Capable of setting up the rendering pipeline to calculate pixel colors

Assignment

To prove you meet the learning objectives,

EITHER, IN WebGL/JavaScript

  • procedurally generate mesh geometry other than a cube (e.g., a cylinder)

  • AND EITHER

    • re-build the Godot Toon Shader in WebGL/JavaScript
  • OR

    • Create a shader combining texture coloring and light calculation as individually treated in the WebGL/JS lectures

OR, IN Godot

  • procedurally create geometry other than a Cylinder using an ArrayMesh.
  • Create a Shader varying vertex positions and color in a cyclic way over TIME.
  • As examples, consider
    • A mesh geometry made out of simple grass leaeves (each made out of four triangles) arranged on a rectangular grid. A vertex shader making each grass leave wave by positioning each vertex in the x-z axis and controlling the degree of movement by its y-coordinate. A fragment shader that accompanies the grass’ waving movement by cycling through different shades of green.
    • A mesh geometry forming a regular grid of quadrilateral-like shaped pairs of triangles. A vertex shader applying sin and cos movement on the y-axis of the geometry to form water-like waves. A fragment shader accompanying the wave movement with different shades of blue.