## CS424 Notes, 7 March 2012

- Projection in Context
- Remember that projection maps eye coordinates to clip coordinates. Projection is the
last transformation that you apply to a vertex. The projection map just has to pick out a box -- rectangular or pyramidal -- whose
contents will be visible when the scene is rendered.
Before projection is applied, the
vertex is in a coordinate system in which the eye is at the origin, looking down the
z-axis. In eye coordinates, the box
is given in terms of limits on x, y, and z. In particular, the near and far
distances specify z-values. In terms of world coordinates, however, the box can be
anywhere. The near and far distances are not z-coordinates but
*they still specify distances from the viewer*. Similarly, the left and right limits are no longer x-coordinates, but they still specify distances to the left or right of the viewer. - To get
`gl_Position`in the vertex shader, a vertex is first multiplied by the modelview transformation then by the projection transformation. This might look like:gl_Position = projection * modelview * vec4(vertexCoords,1.0);

However, since this multiplication will be done for every vertex, it is probably more efficient to multiply the projection by the modelview matrix in JavaScript and pass the combined matrix as a single uniform variable in the vertex shader. Then`gl_Position`might be computed asgl_Postion = projmodelview * vec4(vertexCoords,1.0);

However, as mentioned previously, the modelview matrix might still be necessary in the vertex or fragment shader, since it is used in lighting calculations. - The function
`mat4.multiply(projection,modelview,projmodelview)`can do the multiplication in JavaScript, leaving*projection*and*modelview*unmodified and putting the result in*projmodelview*.

- Remember that projection maps eye coordinates to clip coordinates. Projection is the
last transformation that you apply to a vertex. The projection map just has to pick out a box -- rectangular or pyramidal -- whose
contents will be visible when the scene is rendered.
Before projection is applied, the
vertex is in a coordinate system in which the eye is at the origin, looking down the
z-axis. In eye coordinates, the box
is given in terms of limits on x, y, and z. In particular, the near and far
distances specify z-values. In terms of world coordinates, however, the box can be
anywhere. The near and far distances are not z-coordinates but
- mat4.frustum
`mat4.frustum(left,right,bottom,top,near,far)`creates a projection matrix for a perspective projection in which the view volume is a truncated pyramid. The pyramid extends along the negative z-axis (in eye coordinates) from*-near*to*-far*.*left*and*right*give the limits on the x-coordinate**at the near distance**, that is at the top of the pyramid. Similarly,*bottom*and*top*give the limits on the y-coordinate at the near distance.*near*and*far*should be chosen so that everything that you want to see in the scene is included in that range of distances from the eye. However, you should**not**just make*near*tiny and*far*huge. The reason is that the entire range of values from*near*to*far*is mapped onto the depth buffer, which generally only has 16 bits per pixel. That means that the depth buffer can only distinguish 65,536 different depths. Suppose that*far - near*is 65,536 and that your scene actually occupies only one unit in the z-direction. In that case, every point in your scene is at the same depth, as far as the depth buffer is concerned, and the depth buffer algorithm becomes completely useless! The moral is to use a near/far range that is reasonably well adapted to the actual dimensions of your scene.- The fact that
*left, right, bottom, and top*represent distances at the near distance is unfortunate. It would be nicer if they represented distances at the center of view, that is, at the distance from*eye*to*center*in*mat4.lookAt*. However, viewing and projection are completely separate operations for mat4, and there is no connection between them. Fortunately, there is a simple conversion that you can use: Suppose that d is the distance from the eye to the center of view and that you want the visible ranges of x and y values at that distance to be*xmin*to*xmax*and*ymin*to*ymax*. Then the values of*left*,*right*,*bottom*, and*top*for*mat4.frustum*can be computed asleft = (near/d)*xmin; right = (near/d)*xmax; bottom = (near/d)*ymin; top = (near/d)*ymax;

- Note that the x and y ranges are usually symmetric about about 0. That is
*left = -right*and*bottom = -top*. However, there is no requirement that this be true. If it's not, then the view volume is a tilted pyramid in which the axis is not perpendicular to the base. (I find it easier not to think about this possibility.)

- mat4.perspective
`mat4.perspective(fovy,aspect,near,far)`is an alternative function for creating a perspective projection matrix. In this method,*near*and*far*have the same meanings as in`mat4.frustum`. The first parameter,*fovy*, is the "field of view in the y direction". It represents an angle,**measured in degrees**between the top plane of the view pyramid and the bottom plane. The value must be between 0 and 180; a typical value is 45. The*aspect*parameter is the ratio between the horizontal size of the pyramid, from left to right, and the vertical size, measured from bottom to top. The aspect is almost always set to match the aspect ratio of the "viewport" where the image is drawn; for WebGL, this generally means canvas.width/canvas.height.- Note that
*mat4perspective*always produces a view in which the x and y ranges are symmetric about 0. - There is no similar function in standard OpenGL, but one is provided in the common
OpenGL utility library GLU. the name of the function in that library is
*gluPerspective*. - The definition of
*mat4.perspective*in gl-matrix.js is very simple:mat4.perspective = function (fovy, aspect, near, far, dest) { var top = near * Math.tan(fovy * Math.PI / 360.0), right = top * aspect; return mat4.frustum(-right, right, -top, top, near, far, dest); };

(The mathematicians in the audience can figure this one out, if they want.)

- mat4.ortho
`mat4.ortho(left,right,top,bottom,near,far)`sets up an orthographic projection in which the view volume is a rectangular solid. The first four parameters give the limits on x and y (in eye coordinates); since this is an orthographic projection, the distances don't depend on the distance from the eye. The last two parameters give the limits on z, relative to the eye. That is, a point is in the view volume if its z-coordinate, in eye coordinates, is between*-near*and*-far*.- The odd thing about this is that there really is no natural concept of eye position for an orthographic projection. There is no point at which all the lines of view converge (or, if there is one, it is a point at infinite distance). The "eye" is really just the origin in the eye coordinate system.
- In fact, there is no requirement that
*near*and*far*be positive numbers. For example, in`mat4.ortho(-5,5,-5,-5,5)`, the view volume extends from -5 to 5 in the x, y, and z directions (in eye coordinates). The surprise here is that the first z-limit, -5, actually refers to the z-coordinate 5 on the positive z-axis, while the second z-limit, 5, actually refers to the z-coordinate -5 on the negative z-axis. Remember that*near*and*far*are given in terms of distance from the eye, not in terms of z-coordinates. For symmetric view volumes, the distinction doesn't matter, but for`mat4.ortho(-5,5,-5,5,5,10)`, you need to remember that the z-limits, 5 to 10, correspond to eye-coordinate z-values from -5 to -10. It's easiest to think of them as meaning that things between 5 and 10 units in front of the eye are visible in the scene. - In the example from last time, the view and
projection are set up with
modelview = mat4.lookAt( [5,5,10], [0,0,0], [0,1,0] ); and projection = mat4.ortho(-3,3,-3,3,9,15);

The distance from the eye, [5,5,10], to the view center, [0,0,0], is about 12. (More accurately, about 12.247.) The near and far distances are chosen to be about three units on either side of this distance, so that the view center is about in the center of the view volume and the range of z values is the same size as the range of x and y. (What would happen if you changed "ortho" to "frustum" for the projection matrix?)

- Homogeneous Coordinates
- It's time to start talking about the fourth coordinate!
- To represent affine transforms in 3D, we use 4-by-4 matrices and vectors with four coordinates, (x,y,z,w). To transform (x,y,z), add a 1 in the w-position to get (x,y,z,1). Multiply by the affine transform matrix, and then discard the 1 in the resulting vector.
- However,
**perspective projection is not an affine transform**! When you multiply a vector (x,y,z,1) by a perspective projection matrix, the resulting vector will have a w-coordinate that is not equal to 1. So, how can we interpret the result as a point in 3D? - The answer is provided by
*homogeneous coordinates*. The short answer is that a vector (x,y,z,w), where w is any non-zero number, represents the point (x/w,y/w,z/w) in three-dimensional space. In homogeneous coordinates, the point (1,2,3) could be represented by (1,2,3,1), (2,4,6,2), (-1,-2,-3,-1), (0.5,1,1.5,0.5), or even (π,π*2,π*3,π). The division by w to give (x/w,y/w,z/w) is called*perspective division*when it is done in connection with perspective transformations. - Graphics hardware is perfectly capable of dealing with vectors given in homogeneous coordinates.
In fact, homogeneous coordinates are its native language.
When you compute
*gl_Position*using a perspective projection matrix, the result will very likely have a w-coordinate that is not equal to one. You do not have to worry about this; the graphics hardware will do the perspective division and get the correct point. - Vectors of the form (x,y,z,0), in which the w-coordinate is 0, do not represent points in ordinary three-dimensional space. However, this vector can be interpreted as representing a "point at infinity in the direction of (x,y,z)." In fact, this interpretation is used in standard OpenGL when specifying the position of a light. A light whose position is a "point at infinity" is a directional light, in which all the rays of light come from the same direction and are parallel to each other.

- If we have any extra time, we'll discuss some examples of modeling, viewing, and projecting in 3D