
Notes for Nov 18 class
Surface of revolution
Creating a surface of revolution
Given a spline curve S(v) that defines a radius from v=0 to v=1,
we can define a surface of revolution as follows:
For any (u_{i},v_{j}), the vertex position is:
position_{i,j} = [ S(v_{j}) * cos(2πu_{i}) , S(v_{j}) * sin(2πu_{i}) , 2 * v_{j}  1 ]
To compute the surface normal at this vertex:
d_{x} = cos(2πu_{i})
d_{y} = sin(2πu_{i})
d_{z} = (S(v_{j})  S(v_{j+1})) / 2(v_{j+1}  v_{j})
normal_{i,j} = normalize( [ d_{x} , d_{y}, d_{z} ] )
Using a surface of revolution
The surface of revolution as defined above will be along the z axis, from z = 1 to z = +1.
You need to translate, rotate and scale it to create a shape of desired length and orientation.
Orientation of ribbons
To create a consistent orientation
for a ribbon, you need to follow along
the path of the ribbon from beginning to end,
at each step creating an iterative update to the
ribbon orientation so that the orientation remains consistent,
without unwanted twists.
One way to do this is in two stages.
First, compute the path from beginning to
end, together with orientation.
Then, use that path to build the actual
triangle strip geometry.
The general idea is as follows:
(1) Building the path:
At each step along the path, we use a spline function to compute point P_{i} along the center of the ribbon.
At each step along the path, we can compute direction along the path W_{i}  P_{i+1}  P_{i},k
as well as two mutually perpendicular cross vectors U_{i} and V_{i}.
We will do successive cross products to compute U_{i} and V_{i} at each step, while avoiding twists.
Given initial ribbon direction W_{0} = normalize(P_{1}  P_{0}), choose an initial cross vector U_{0} perpendicular to W_{0}.
Compute V_{0} = W_{0} ⨯ U_{0}
For i = 1 to n along path:
Compute W_{i} = normalize(P_{i+1}  P_{i})
Compute U_{i} = normalize(V_{i1} ⨯ W_{i})
Compute V_{i} = normalize(W_{i} ⨯ U_{i})
(2) Building the ribbon:
To build a ribbon with width 2 * r:
For i = 0 to n along path:
The two vertices at this step along the path are at P_{i}  r * U_{i} and P_{i} + r * U_{i}.
Bump mapping
Bump mapping is an inexpensive way,
created by Jim Blinn in 1977,
to create the illusion of a highly
detailed model, without the expense
of using many triangles.
The trick is to use a texture to
modify the surface normal at each
pixel, before applying a shading
algorithm such as Phong shading.
The result will be that the object
appears to have bumps and other
surface irregularities, even though
the actual geometry is smooth
and featureless.
There are several parts to bump mapping:
 Adding a tangent direction to each vertex in your vertex buffer;
 Creating a bump map image;
 Rendering the bump map texture.
(1) Adding a tangent direction to each vertex:
In order to support bump mapping, you need to add a tangent vector
[tx,ty,tz]
to each vertex in your shapes (cubes, spheres, cylinders, etc).
So instead of storing
VERTEX_SIZE = 8
values per vertex in your vertex buffer, you will need to
store
VERTEX_SIZE = 11
values:
vertex = [ x,y,z, nx,ny,nz, tx,ty,tz, u,v ]
You can compute the tangent at each vertex by taking successive differences between
vertex positions:
tangent_{i,j} = normalize(P_{i+1,j}  P_{i,j})
where P_{i,j} is the position of the vertex at (u_{i},v_{j}).
(2) Creating a bump map image:
You need to use the red,green and blue channels of your texture
image to encode the variations in u,v and w of the normal,
where u is the direction tangent to the surface along texture paramater u, w is the direction of your surface normal,
and v is the binormal, which is perpendicular to both the normal and the tangent,
is the direction tangent to the surface along texture paramater v.
For each of r,g,b, a pixel value between 0 and 255 encodes a value between 1.0 and +1.0.
(3) Rendering the bump map texture:
To render the bump map, you need to do some work in both your vertex shader and in your fragment shader.
In the vertex shader,
after you have transformed the normal and tangent vectors,
you need to compute the binormal:
vBinormal = normalize(cross(vNormal, vTangent));
In the fragment shader, you need to retrieve the values r,g,b from the texture map, and
then use those values to vary the surface normal, before doing Phong shading:
vec4 b = texture2D(uSampler[1], vUV)  0.5; // We are assuming here that the bump map is texture[1].
vec3 normal = normalize(b.r * vTangent + b.g * vBinormal + b.b * vNormal);
Concept of locking for multiuser VR
We will be giving you the software infrastructure next Monday
to do the locking required for interaction between
multiple users. Meanwhile, just so you can get used to the idea,
here is what that interface will look like:
// CREATING A NEW LOCK
let lock1 = client.createLock();
...
// DEFINE ACTION FOR THE PLAYER WHO SUCCESSFULLY GRABS THE LOCK.
// playerID IS THE PLAYER WHO SUCCEEDED IN GRABBING THE LOCK.
// RETURN true TO CONTINUE HOLDING THE LOCK.
// OTHERWISE THE LOCK IS RELEASED AFTER THIS CALL.
lock1.onLock = playerID => { ... }
...
// REQUEST A LOCK. IF SUCCESSFUL, YOUR onLock() CALLBACK WILL BE CALLED.
lock1.requestLock();
Two link IK
When building animated characters, you sometimes
want to connect a shoulder to a wrist, by figuring out the
position of an elbow,
or connect a hip to an ankle, by figuring out the position of a knee.
This process is called inverse kinematics, or IK for short.
In particular, when there is only a single intermediate joint to be computed,
the process is called "two link IK".
High Level Concept:
The elbow is going to be at some point on a sphere
centered at the shoulder with radius
a ,
where
a
is the length of the upper arm.
It is also going to be at some point on a sphere
centered at the wrist with radius
b ,
where
b
is the length of the lower arm.
The intersection of these two spheres is a circle.
If you give a hint as to the direction the elbow is pointing,
that will allow you to compute the point along this circle
which is nearest to your hint direction,
and you can place the elbow there.
Implementation:
Given a dot product function
dot(a,b) ,
and assuming the shoulder is at the origin,
the wrist is at point
C ,
and the hint direction is placed into vector
D
before calling the function below,
the correct elbow direction will be placed into
D
and
also returned as the value of the function:
function ik(a, b, C, D) {
let cc = dot(C,C), x = (1 + (a*a  b*b)/cc) / 2, y = dot(C,D)/cc;
for (let i = 0 ; i < 3 ; i++) D[i] = y * C[i];
y = sqrt(max(0,a*a  cc*x*x) / dot(D,D));
for (let i = 0 ; i < 3 ; i++) D[i] = x * C[i] + y * D[i];
return D;
}
Cool video we saw
At the end of class we watched the 2005 short film
Carlitopolis.
 