Notes for Tuesday March 24 class -- perspective and 3D objects

 

Note: I will be adding some diagrams to these notes in the coming days.

Perspective

Given a focal length f, we want to distort the scene as though the point (0,0,f) is a camera viewpoint.

Objects that are in positive z will appear to be larger, and objects that are in negative z will appear to be smaller.

The matrix that does this is:

0
0
f
0
  ←  
1000
0100
00-1/f0
0001
  ×  
0
0
f
1
We send this matrix down to the GPU as a uniform mat4 variable:
   mat4 uFMat = [ 1,0,0,0, 0,1,0,0, 0,0,-1/f,0, 0,0,0,1 ]
We then use it in the vertex shader.

In the vertex shader, we multiply the vertex by uFMat after (that is, to the left of) multiplying by the transformation matrix uMat.

In particular, we replace this line in the vertex shader:

      vec4 pos = uMatF * vec4(aPos, 1.);
by this line:
      vec4 pos = uPMat * uMatF * vec4(aPos, 1.);

Phong function (parameters → matrix)

Instead of just writing out the 16 numbers of a matrix to specify the parameters for the Phong algorithm, we can define a function to do that for us:
   phong(ambient,diffuse,specular,p)
The arguments are three color vectors ar,g,b, dr,g,b, sr,g,b, and a numerical power p. They can be used to define the materials matrix as follows:
   mat4 uM = [ a0,a1,a2,0, d0,d1,d2,0, s0,s1,s2,p, 0,0,0,1 ]

Various materials using Phong (eg: gold, plastic):

Using the phong function, we can define various convenient materials. Here are two examples:
   let gold        = phong([.1,.03,0], [.12,.05,0], [1,.5,0], 10);
   let red_plastic = phong([.1,0,0], [.5,0,0], [1,1,1], 20);

Matrix methods:

You have already implemented the Matrix class methods identity(), translate(x,y,z), rotateX(θ), rotateY(θ), rotateZ(θ) and scale(x,y,z). In class we talked about a few refinements to the Matrix class.

For example, if there ia only one argument to scale(x,y,z), then we should assume uniform scaling. In other words, if y === undefined, then y = z = x.

You will want both a getValue() and setValue() method. But be sure your getValue() method copies each value from an array. In other words:

        this.setValue = function(v) { // need to copy values
           for (let i = 0 ; i < 16 ; i++)
              value[i] = v[i];
        }
Also, it is useful to multiply a matrix object by another matrix object. In order to do thism we access the values of both matrix objects, and use our internal multiply function:
        this.multiply(matrix) = function(matrix) {
            value = multiply(value, matrix.getValue());
        }

3D Hierarchical Objects:

We went over in the class how a 3D renderable object has the following members:
    this.shape
    this.phong
    this.children[]
    this.matrix
and the following methods:
    this.add(shape, phong) // add a child
    this.child(n)          // get the nth child
    this.draw()            // draw the object and its children.
For convenience, we make all of the matrix operations available as well:
    this.identity = () => this.matrix.identity();
    this.translate = (x,y,z) => this.matrix.translate(x,y,z);
    etc.
The draw method recursively draws an object and all of its children and their children, etc. performing matrix multiplication recursively as we descend the parent to child tree.

Here is my implementation of the draw method:

   this.draw = isChild => {
      if (! isChild)
         matrix.identity();
      matrix.multiply(this.matrix);
      if (this.phong)
         setUniform('Matrix4fv', 'uM', false, this.phong);
      if (this.shape)
         drawShape(this.shape);
      let parentValue = matrix.getValue();
      for (let n = 0 ; n < this.children.length ; n++) {
         matrix.setValue(parentValue);
         this.children[n].draw(true);
      }
   }

Glueing together two triangle meshes:

Given two triangle meshes a and b, we can glue them together to create a single mesh via the following four steps:
   1) add the first mesh
   2) add the last vertex of the first mesh
   3) add the first vertex of the second mesh
   4) add the second mesh

More parametric shapes:

We also discussed triangle meshes in the form of sphere, torus, tube, square and disk.

We have already discussed the parametric definition of a sphere, torus and tube. As we discussed in class, the parametric definition of a circular disk is:

position at u,v = [ cos(2 π u) v , sin(2 π u) v , 0]

normal at u,v = [ 0, 0, 1 ]

The parametric definition of a square is particularly simple:
position at u,v = [ 2u-1 , 2v-1 , 0]

normal at u,v = [ 0, 0, 1 ]

Glueing triangle meshes together:

We also discussed, at a high level, how you can make a triangle mesh in the shape of a cylinder by glueing together three triangle meshes: One for the back cap, one for the open tube, and one for the front cap.

To do that you need to call the glueMeshes function twice -- once for each of the two glue operations -- since it only glues together two meshes at a time.