|
Notes for Thursday March 26 class -- parametric surfaces
Making a cube
We already know we can create a square as a triangle strip
consisting of two triangles.
By glueing together six such squares, we can create a
triangle strip in the shape of a cube.
I went over my implementation of this in class, which
you are free to use in your own work.
You can also use this as a model for creating
other shapes that you might want to piece together
in creative ways:
let cube = [];
for (let s = -1 ; s <= 1 ; s += 2) { // Loop thru two faces for each axis
let v = [ new Array(stride),
new Array(stride),
new Array(stride),
new Array(stride) ];
for (let i = 0 ; i < 3 ; i++) { // Loop thru x,y,z axes
let j = (i + 1) % 3,
k = (i + 2) % 3;
for (let m = 0 ; m < 4 ; m++) { // Loop thru the 4 corners of one face
v[m][i] = m > 1 ? s : -s; // Position
v[m][j] = m & 1 ? 1 : -1;
v[m][k] = s;
v[m][i+3] = 0; // Normal
v[m][j+3] = 0;
v[m][k+3] = s;
v[m][6] = (1 + v[m][i]) / 2; // u,v
v[m][7] = (1 + v[m][j]) / 2;
}
cube = glueMeshes(cube, v[0].concat(v[1].concat(v[2].concat(v[3]))));
}
}
Playing with parametric surfaces
In class we showed that you can play around quite a bit with
parametric surfaces. If you make the shape of the surface
depend on time, then you can get really fun animating shapes,
which create a kind of visual music.
The accompanying image is a snapshot of one of the animating shapes
we created in class.
This particular example is a torus that is continually
twisting itself.
You too can create animating shapes, if you create a new
triangle mesh every animation frame, and use time in the
definition of your triangle mesh at each value of u,v.
|
|
Adding u,v to each vertex
In order to create textures, it will be useful to have
access to the parametric values u,v at each vertex.
We handle this by doing several things.
For one thing, we increase stride from 6 to 8, and
we append u,v to each vertex in the vertex buffer.
For example, we used to define each vertex of a square as follows:
let uvToSquare = (u,v) => [ 2*u-1,2*v-1,0, 0,0,1 ];
Here is what the same function looks like when we add u,v into each vertex.
Note the addition of u,v on the end:
let uvToSquare = (u,v) => [ 2*u-1,2*v-1,0, 0,0,1, u,v ];
We also need to do the following things:
- declare
attribute vec2 aUV in lib4.js
- declare
aUV in the vertex shader
- assign
aUV to varying vec2 vUV in the vertex shader
- declare
vUV in fragment shader
- add texture amplitude
ta and frequency tf to the phong parameters
- use
ta and tf to create textures in the fragment shader
We used procedural noise to show different kinds of texture mapping.
If you texture based on
vPos
(the 3D position that is transformed
by a matrix every animation frame), then
you will get a solid texture that
looks as though your shape is moving through a solid block of material.
If you want a solid texture that moves together with your shape,
then you need to base your texture directly on vertex attribute
aPos ,
without any matrix transformations.
In order to do this, we created a
varying vec3 vaPos ,
which is just assigned the value of
aPos
at every vertex.
Creating an open cone
We modified our definition of an open tube to create an open cone.
Not only do we need to use
v
to taper the tube, but we also need to modify
the surface normal, because the surface of the cone inclines
toward the positive z axis:
let coneNxy = Math.sqrt(4/5);
let coneNz = Math.sqrt(1/5);
let uvToOpenCone = (u,v) => {
let theta = 2 * Math.PI * u;
let x = Math.cos(theta) * (1-v),
y = Math.sin(theta) * (1-v),
z = 2 * v - 1;
return [ x,y,z, coneNxy * x,coneNxy * y,coneNz, u,v ];
}
Creating a cone with an end cap
To put an end cap on the cone, we glue it together
with a disk shaped triangle mesh.
As we discussed in class, we pass a third argument
to
createTriangleMesh().
That argument gets passed
by
createTriangleMesh()
into
uvToDisk
as a third argument.
The
uvToDisk
function uses that third argument
to translate the disk in
z
to
-1.
let cone = glueMeshes(openCone,
createTriangleMesh(uvToDisk, 30, 2, -1));
In order to make this work I modified uvToDisk() as follows.
Note the
if/else
statement. That's where I use
the optional third argument
s ,
where
s=-1
or
s=1 ,
to shift the
z
position of the disk to either
z=-1
or
z=1:
let uvToDisk = (u,v,s) => {
let theta = 2 * Math.PI * u,
x = Math.cos(theta),
y = Math.sin(theta),
z = 0;
if (s === undefined)
s = 1;
else
z = s;
return [ v*x*s,v*y,z, 0,0,s, u,v ];
}
Homework 6, due before class on Thursday April 2
First grab hw6.zip to use as your starting point.
I have left several places in hw6 unimplemented, for you to fill in.
You should implement the following in
lib4.js:
- The
glueMeshes()
function;
-
To create the primitive shapes that I left out,
implement
uvToSphere(), uvToTorus()
and
uvToTube()
with the
u,v
values added to the end of each vertex
(since
stride
is now 8).
You now have the components you need
to create really interesting scenes,
so in
index.html
I would like you to make an interesting
and original scene
that shows off the power of hierarchical
animated 3D objects.
I would like you to be creative, and
make something fun and exciting,
ideally reflecting your own interests
(music, architecture, games, science, cool mechanisms, etc.).
There are lots of opportunities for
extra credit here:
-
You can create your own primitive shapes,
perhaps a tetrahedron, octahedron, dodecahedron or icosahedron.
-
Create various kinds of procedural texture,
built using either
vaPos
or
vUV,
or a combination of the two.
- Create procedural textures that
vary some other property of appearance,
not just color.
Those are just a few suggestions for extra credit.
I am sure you can think of all sorts of interesting
things to try. This is your space,
and your chance to play.
|
| |