Looking through the homeworks, I see that quite a few students have
fallen behind. So rather than give you new work this week, I am going to let
you use the time between now and Thursday March 12 to catch up on
assignment four,
without any late penalty.
If you are having trouble, this is your chance to reach out to
me or to the grader for help.
One note about hw4. I had written the code for
translate()
as:
this.translate = function(x, y, z) {
let m = [1,0,0,0, 0,1,0,0, 0,0,1,0, x,y,z,1]; // (1) CREATE TRANSLATION MATRIX.
value = multiply(m, value); // (2) MULTIPLY TO CHANGE OBJECT value.
return this;
}
You might get better results by reversing the order of the argument in
multiply()
as follows:
this.translate = function(x, y, z) {
let m = [1,0,0,0, 0,1,0,0, 0,0,1,0, x,y,z,1]; // (1) CREATE TRANSLATION MATRIX.
value = multiply(value, m); // (2) MULTIPLY TO CHANGE OBJECT value.
return this;
}
and also do the same when implementing the rotate and scale methods.
Depending on your implementation of
multiply()
,
that ordering might
result in more intuitive motion.
Sometime in the next day or so, I am going to put on-line the notes
from our lectures this week on triangle meshes.
But there is not yet any corresponding homework assigment,
while we are waiting for people to catch up.
For those of you who have been keeping up,
feel free to add more to your assignment for extra credit.
And of course you can get a jump on the new material,
after I have put that up on-line.
Creating a mesh from a triangle strip
In order to send vertex data down from the CPU to the GPU for rendering,
we need to tell the GPU how many values are in each vertex.
If we store only x,y,z position, then each vertex will have 3 floating point values.
On the other hand, if we also store normal, then each vertex will
have 6 floating point values: x,y,z, nx,ny,yz
We call this value the "stride" of the vertex buffer,
since it indicates how large is the stride from one entry
in the vertex buffer to the next.
It is useful to declar a variable that indicates how many floating
point values are in each vertex:
let stride = 6;
We can send vertex data down from the CPU to the GPU by
sending individual triangles as follows, but this is usually inefficient,
since for most shapes, multiple triangles share the same vertex:
gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertices.length/stride);
In many cases, it is better to form a triangle strip,
which zigzags as follows:
1\--3\--5\--7\--9\
| \ | \ | \ | \ | \ ....
0--\2--\4--\6--\8
This allows most vertices to be shared between 3 triangles,
so we don't need to send as much data down from the CPU to the GPU.
We can create a shape as a 2D rectangular mesh of vertices
by sending a single triangle strip for first
the first row, then the second, then the third, etc.
In order to avoid problems of spurious triangles as
between the end of one row and the beginning of the next,
we add two extra vertices, to create degenerate triangles
that won't show up in the final rendering.
Note in the below implementation taht we are passing in,
as the first argument is a function that takes (u,v) as
arguments and returns the data for a single vertex:
let createTriangleMesh = (uvToVertex, nCols, nRows) => {
let mesh = [];
let appendVertex = p => {
for (let n = 0 ; n < p.length ; n++)
mesh.push(p[n]);
}
for (let row = 0 ; row < nRows ; row++) {
let v0 = row / nRows,
v1 = (row+1) / nRows;
for (let col = 0 ; col <= nCols ; col++) {
let u = col / nCols;
appendVertex(uvToVertex(u, v0));
appendVertex(uvToVertex(u, v1));
}
appendVertex(uvToVertex(1, v1));
appendVertex(uvToVertex(0, v1));
}
return mesh;
}
For example, if we want to create a unit sphere with 20 columns
and 10 rows,
and with position (x,y,z) and normal (nx,ny,nz) at
each vertex, we could write:
let vertices = createTriangleArray(uvToSphere, 20, 10);
where:
let uvToSphere = (u, v) => {
let theta = 2 * Math.PI * u,
phi = Math.PI * (v - .5),
x = Math.cos(theta) * Math.cos(phi),
y = Math.sin(theta) * Math.cos(phi),
z = Math.sin(phi);
return [ x,y,z, x,y,z ]; // RETURN POSITION AND NORMAL
}
Since we now have multiple attributes -- poth position and normal -- to send down
the the vertex shader,
in our Javascript
we now need to map out the structure of each vertex in the
vertex buffer more clearly:
let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex.
let aNor = gl.getAttribLocation(program, 'aNor'); // Set aNor attribute for each vertex.
gl.enableVertexAttribArray(aPos);
gl.enableVertexAttribArray(aNor);
let bpe = Float32Array.BYTES_PER_ELEMENT; // Bytes per floating point number
gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 6 * bpe, 0 ); // aPos is 0,1,2 within the 6 slots.
gl.vertexAttribPointer(aNor, 3, gl.FLOAT, false, 6 * bpe, 3 * bpe); // aNor is 3,4,5 within the 6 slots.