Lab: Triangles
In this chapter, you've been learning how to model objects as a collection of triangles that can be transformed. Let's apply this knowledge by writing a renderer that contorts a polygon into several different poses.
Form a group of no more than three people. Host, open your top-level repository directory in Visual Studio Code and run npm run dev
to start up a web server. Then open the command palette via View / Command Palette and enter Live Share: Start Collaboration Session. Share the link with your group.
Transfit
Your first challenge is to transform a shape into a set of target shapes by transforming it through uniforms. Complete these steps:
transfit
.initializeOutlineVaos
. Create six VAOs that will outline six horse-like shapes using these arrays for the positions:
const outlinePositions = [
[-2, -2, 0, -1, -2, 0, -1, -1, 0, 0, -1, 0, 0, -2, 0, 1, -2, 0, 1, 0, 0, 2, 0, 0, 2, 1, 0, 1, 1, 0, 1, 2, 0, 0, 2, 0, 0, 0, 0, -2, 0, 0],
[3, 4, 0, 1.5, 4, 0, 1.5, 5, 0, 0, 5, 0, 0, 4, 0, -1.5, 4, 0, -1.5, 6, 0, -3, 6, 0, -3, 7, 0, -1.5, 7, 0, -1.5, 8, 0, 0, 8, 0, 0, 6, 0, 3, 6, 0],
[3.2679492235183716, -0.7320507764816284, 0.5, 4.133974611759186, -1.2320507764816284, 0.5, 4.633974611759186, -0.3660253882408142, 0.5, 5.5, -0.8660253882408142, 0.5, 5, -1.7320507764816284, 0.5, 5.866025388240814, -2.2320507764816284, 0.5, 6.866025388240814, -0.5, 0.5, 7.732050776481628, -1, 0.5, 8.232050776481628, -0.1339746117591858, 0.5, 7.366025388240814, 0.3660253882408142, 0.5, 7.866025388240814, 1.2320507764816284, 0.5, 7, 1.7320507764816284, 0.5, 6, 0, 0.5, 4.267949223518372, 1, 0.5],
[4, 5, 0.5, 4, 5.5, 0.5, 5, 5.5, 0.5, 5, 6, 0.5, 4, 6, 0.5, 4, 6.5, 0.5, 6, 6.5, 0.5, 6, 7, 0.5, 7, 7, 0.5, 7, 6.5, 0.5, 8, 6.5, 0.5, 8, 6, 0.5, 6, 6, 0.5, 6, 5, 0.5],
[14.732050776481628, 4.901923894882202, 0, 14.232050776481628, 6.200961947441101, 0, 13.366025388240814, 5.450961947441101, 0, 12.866025388240814, 6.75, 0, 13.732050776481628, 7.5, 0, 13.232050776481628, 8.799038052558899, 0, 11.5, 7.299038052558899, 0, 11, 8.598076105117798, 0, 10.133974611759186, 7.848076105117798, 0, 10.633974611759186, 6.549038052558899, 0, 9.767949223518372, 5.799038052558899, 0, 10.267949223518372, 4.5, 0, 12, 6, 0, 13, 3.401923894882202, 0],
[10, 2.9999999999999996, 0, 11, 3, 0, 11, 1.4999999999999998, 0, 12, 1.5, 0, 12, 3, 0, 13, 3, 0, 13, 1.8369702788777518e-16, 0, 14, 3.6739405577555036e-16, 0, 14, -1.4999999999999996, 0, 13, -1.4999999999999998, 0, 13, -3, 0, 12, -3, 0, 12, 0, 0, 10, 0, 0]
];
const outlinePositions = [ [-2, -2, 0, -1, -2, 0, -1, -1, 0, 0, -1, 0, 0, -2, 0, 1, -2, 0, 1, 0, 0, 2, 0, 0, 2, 1, 0, 1, 1, 0, 1, 2, 0, 0, 2, 0, 0, 0, 0, -2, 0, 0], [3, 4, 0, 1.5, 4, 0, 1.5, 5, 0, 0, 5, 0, 0, 4, 0, -1.5, 4, 0, -1.5, 6, 0, -3, 6, 0, -3, 7, 0, -1.5, 7, 0, -1.5, 8, 0, 0, 8, 0, 0, 6, 0, 3, 6, 0], [3.2679492235183716, -0.7320507764816284, 0.5, 4.133974611759186, -1.2320507764816284, 0.5, 4.633974611759186, -0.3660253882408142, 0.5, 5.5, -0.8660253882408142, 0.5, 5, -1.7320507764816284, 0.5, 5.866025388240814, -2.2320507764816284, 0.5, 6.866025388240814, -0.5, 0.5, 7.732050776481628, -1, 0.5, 8.232050776481628, -0.1339746117591858, 0.5, 7.366025388240814, 0.3660253882408142, 0.5, 7.866025388240814, 1.2320507764816284, 0.5, 7, 1.7320507764816284, 0.5, 6, 0, 0.5, 4.267949223518372, 1, 0.5], [4, 5, 0.5, 4, 5.5, 0.5, 5, 5.5, 0.5, 5, 6, 0.5, 4, 6, 0.5, 4, 6.5, 0.5, 6, 6.5, 0.5, 6, 7, 0.5, 7, 7, 0.5, 7, 6.5, 0.5, 8, 6.5, 0.5, 8, 6, 0.5, 6, 6, 0.5, 6, 5, 0.5], [14.732050776481628, 4.901923894882202, 0, 14.232050776481628, 6.200961947441101, 0, 13.366025388240814, 5.450961947441101, 0, 12.866025388240814, 6.75, 0, 13.732050776481628, 7.5, 0, 13.232050776481628, 8.799038052558899, 0, 11.5, 7.299038052558899, 0, 11, 8.598076105117798, 0, 10.133974611759186, 7.848076105117798, 0, 10.633974611759186, 6.549038052558899, 0, 9.767949223518372, 5.799038052558899, 0, 10.267949223518372, 4.5, 0, 12, 6, 0, 13, 3.401923894882202, 0], [10, 2.9999999999999996, 0, 11, 3, 0, 11, 1.4999999999999998, 0, 12, 1.5, 0, 12, 3, 0, 13, 3, 0, 13, 1.8369702788777518e-16, 0, 14, 3.6739405577555036e-16, 0, 14, -1.4999999999999996, 0, 13, -1.4999999999999998, 0, 13, -3, 0, 12, -3, 0, 12, 0, 0, 10, 0, 0] ];
resizeCanvas
function with these two functions:
function resizeCanvas() {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const aspectRatio = canvas.clientWidth / canvas.clientHeight;
const size = 7;
const center = [6, 3];
if (aspectRatio >= 1) {
clipFromWorld = ortho(center[0] - size * aspectRatio, center[0] + size * aspectRatio, center[1] - size, center[1] + size, -1, 1);
} else {
clipFromWorld = ortho(center[0] - size, center[0] + size, center[1] - size / aspectRatio, center[1] + size / aspectRatio, -1, 1);
}
render();
}
function ortho(left: number, right: number, bottom: number, top: number, near: number = -1, far: number = 1) {
return new Float32Array([
2 / (right - left), 0, 0, 0,
0, 2 / (top - bottom), 0, 0,
0, 0, 2 / (near - far), 0,
-(right + left) / (right - left),
-(top + bottom) / (top - bottom),
(near + far) / (near - far),
1,
]);
}
function resizeCanvas() { canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; const aspectRatio = canvas.clientWidth / canvas.clientHeight; const size = 7; const center = [6, 3]; if (aspectRatio >= 1) { clipFromWorld = ortho(center[0] - size * aspectRatio, center[0] + size * aspectRatio, center[1] - size, center[1] + size, -1, 1); } else { clipFromWorld = ortho(center[0] - size, center[0] + size, center[1] - size / aspectRatio, center[1] + size / aspectRatio, -1, 1); } render(); } function ortho(left: number, right: number, bottom: number, top: number, near: number = -1, far: number = 1) { return new Float32Array([ 2 / (right - left), 0, 0, 0, 0, 2 / (top - bottom), 0, 0, 0, 0, 2 / (near - far), 0, -(right + left) / (right - left), -(top + bottom) / (top - bottom), (near + far) / (near - far), 1, ]); }
uniform mat4 clipFromWorld;
in vec3 position;
void main() {
gl_Position = clipFromWorld * vec4(position, 1.0);
}
uniform mat4 clipFromWorld; in vec3 position; void main() { gl_Position = clipFromWorld * vec4(position, 1.0); }
render
:
shaderProgram.setUniformMatrix4fv('clipFromWorld', clipFromWorld);
shaderProgram.setUniformMatrix4fv('clipFromWorld', clipFromWorld);
rgb
uniform. Use the uniform instead of hardcoding the color in the fragment shader. Render all six outlines in black.initializeFillVao
and create geometry for a triangulated horse. Use these positions:
[
-2.0, -2.0, 0, // vertex 0
-1.0, -2.0, 0, // vertex 1
-1.0, -1.0, 0, // vertex 2
0.0, -1.0, 0, // vertex 3
0.0, -2.0, 0, // vertex 4
1.0, -2.0, 0, // vertex 5
1.0, 0.0, 0, // vertex 6
2.0, 0.0, 0, // vertex 7
2.0, 1.0, 0, // vertex 8
1.0, 1.0, 0, // vertex 9
1.0, 2.0, 0, // vertex 10
0.0, 2.0, 0, // vertex 11
0.0, 0.0, 0, // vertex 12
-2.0, 0.0, 0 // vertex 13
]
[ -2.0, -2.0, 0, // vertex 0 -1.0, -2.0, 0, // vertex 1 -1.0, -1.0, 0, // vertex 2 0.0, -1.0, 0, // vertex 3 0.0, -2.0, 0, // vertex 4 1.0, -2.0, 0, // vertex 5 1.0, 0.0, 0, // vertex 6 2.0, 0.0, 0, // vertex 7 2.0, 1.0, 0, // vertex 8 1.0, 1.0, 0, // vertex 9 1.0, 2.0, 0, // vertex 10 0.0, 2.0, 0, // vertex 11 0.0, 0.0, 0, // vertex 12 -2.0, 0.0, 0 // vertex 13 ]

radians
, offsets
, and factors
.position
property in the shader using the uniforms. Apply the rotation first, the scale second, and the translation third. The order matters. Before any transformations, the origin is at the center of the horsey's bounding box, where the neck meets the back.Blender Model
Making models by hand is not sustainable. We need to develop some 3D modeling skills. Find some tutorials and make some interesting object with fewer than 100 triangles. Learn about extrusion and loop cuts. Focus only on the geometry, not color or texture.
Submission
To receive credit for your lab work, follow these steps:
transfit
folders before the host closes the session.#lab
channel in Discord. Tag your group members with @
.Only labs submitted on time will be granted credit. Late labs or forgot-to-submits are not accepted because Monday at noon is when your instructor has time to grade.