320 likes | 400 Views
Surface Normals and Lighting. Vectors Normals Lighting Shading models Backface culling The world is curved. Vector Math. Vector : A point in space. Space can be one-dimensional... Two-dimensional... Three-dimensional... Or four--or more!. (but only at 8:14AM exactly!). Vector Math.
E N D
Surface Normals and Lighting Vectors Normals Lighting Shading models Backface culling The world is curved...
Vector Math • Vector: A point in space. • Space can be one-dimensional... • Two-dimensional... • Three-dimensional... • Or four--or more! (but only at 8:14AM exactly!)
Vector Math • Vectors are points in space; points in space are vectors. • We usually use the term point to refer to a location in space. • We usually use the term vector to refer to the arrow that goes there from the origin. • They mean the same thing. • A one-dimensional vector is called a scalar. • P=[x, y, z] is a vector. • N=12 is a scalar. • [-2, 16] is a vector. • π is a scalar.
Vector Math: Sample code class Vec { private: float m_x, m_ y, m_ z; public: Vector(void) { x = y = z = 0; } Vector(float x, float y, float z) { m_x = x; m_y = y; m_z = z; } Vector(const Vector &V) { m_x = V.x(); m_y = V.y(); m_z = V.z(); } float x() { return m_x; } float y() { return m_y; } float z() { return m_z; } };
Vector Math • The length of a vector is found using the Pythagorean Theorem. • P = [x, y, z] • length(P) = sqrt(x2+y2+z2) • Putting that in code: public: float length(void) { return sqrt(m_x*m_x + m_y*m_y + m_z*m_z); }
Vector Math • If you multiply a vector by a scalar, you change the length of the vector but keep the same direction: • P = [ x, y ] • P*2 = [ x*2, y*2 ] • P*3 = [ x*3, y*3 ] • P*-42 = [ x*-42, y*-42 ] • If you divide a vector by its own length, that’s called normalising the vector. • A normalised vector has length=1; it’s called a unit vector.
Vector Math • If you subtract one point from another, you get the vector which goes between them. • If you have two points in space, A and B: • then the vector V = B-A is the vector from A to B: B A B A
Vector Math: Sample code • More handy code: public: Vec operator +(const Vec &V) { return Vec(m_x+V.x(), m_y+V.y(), m_z+V.z()); } Vec operator -(const Vec &V) { return Vec(m_x-V.x(), m_y-V.y(), m_z-V.z()); } Vec operator *(float f) { return Vec(m_x*f, m_y*f, m_z*f); } Vec operator /(float f) { return (f!=0) ? (*this)*(1.0/f) : (*this)/0.000001; } • Using length() to normalize: public: Vec normalized(void) { return (*this)/length(); }
Vector Math • Multiplying vectors • A vector times a scalar is a new vector in the same direction as the old vector. A vector times another vector is...? • There are two ways to multiply two vectors: • Dot Product: • f = A * B • f = A.x*B.x + A.y*B.y + A.z*B.z; • Cross Product: • N = A x B • N.x = A.y*B.z - A.z*B.y • N.y = -A.x*B.z + A.z*B.x • N.z = A.x*B.y - A.y*B.x
A Vector Math C θ • Dot Products • A dot product takes two vectors and returns a scalar. • In one-dimensional numbers, a dot product /is/ multiplication as we know it; x*y=z, all scalars. • A few of the properties of a dot product: • The dot product of two unit vectors is equal to the cos() of the angle between them. • If the dot product of two unit vectors is positive, the angle between them is less than ninety degrees. • If the dot product is zero, the angle is exactly ninety. • If the dot product is less than zero, the angle between the two vectors is greater than ninety degrees. B
Vector Math • A cross product finds a vector which is exactly perpendicular to the two vectors being crossed. • A single vector lies on a line. Three points define a plane. The cross product of two vectors is a vector parallel to the normal of their plane. • (And oddly enough, the length of the resulting vector is exactly the area of the parallelogram whose two sides are formed by the two vectors.)
Vector Math: Sample code • The dot product: float operator *(const Vec &V) { return m_x*V.x() + m_y*V.y() + m_z*V.z(); } • The cross product: Vec operator ^(const Vec &V) { Vec crossProduct; crossProduct.m_x = m_y*V.m_z - m_z*V.m_y; crossProduct.m_y = m_z*V.m_x - m_x*V.m_z; crossProduct.m_z = m_x*V.m_y - m_y*V.m_x; return crossProduct; }
Vector Math • Working with points in space: • To find the angle between two vectors, you dot the vectors and take the inverse cosine ( acos() ) of the dot. • You can find the angle between two intersecting lines by subtracting the intersection point from points on the lines, normalising the vectors, and taking the acos() of the dot. • To find the line perpendicularto the two lines, subtract the points, normalise the vectorswhich result, then cross them. • V=B-A goes from A to B!
Surface Normals • Using the techniques just covered, you can find the normal to a polygonal surface at any point. • Remember, 3D objects are made of polygons. • Polygons are planar, made of points in space. • You can find the normal to a polygon by picking any three points on the poly, subtracting one of them from each of the other two, and crossing the vectors that result. P2 Normal P3 P1 Normal = (P2-P1) x (P5-P1) P5 P4
Surface Normals • Sometimes your data isn’t stored as a set of polygons. • If you’re working with an implicit surface, you can still find the surface normal. You just need to calculate the nearby points on the surface by nudging u and v a tiny bit. • Take a look at the GL_QUADS code on week 6’s optional handout for an example. P2 Normal P1 Normal = (P2-P1) x (P3-P1) P3
Surface Normals: The Right-Hand Rule • One question is: okay, so you can find the normal to a surface. Which side of the surface does it point away from? • The answer is: use the Right-Hand Rule. Curl your right hand clockwise, from the first vector being crossed towards the second vector being crossed. Your thumb points in the direction of the normal. • The OpenGL default is to assume that the vertices in a polygon are arranged in counterclockwise (anticlockwise) order around the normal.
Surface Normals - Why bother? • So what’s the point of finding the normal to the surface at a point or on a face? • (1) This is the lynchpin to lighting. • (2) It can also be used for backface culling, an optimisation often used to speed up rendering times. • Lighting uses surface normals to figure out how the light should bounce off of a surface at any particular point. • Backface culling uses normals to test whether the face is visible to the user at all; using this quick test, if the face couldn’t be visible, it won’t be rendered or processed at all. Speedy!
Lighting • Light sources in computer graphics break down into two categories: • Point lights • Directional lights • Light itself breaks down into three types of light: • Ambient light • - The omnidirectional, floating, everywhere lighting • Diffuse light • - Light that gets scattered around by the surface • Specular light • - Shiny light that bounces to the eye of the viewer
Lighting • Ambient lighting is the light that permeates the world. • Directionless, sourceless, omnipresent. • Think of the way, when you’re in fog, light seems to be everywhere at once. • Diffuse lighting is the light that bounces off the surface. • Diffuse lighting shows the curve of the surface, wherever you’re from. Think of how a clay ball shades from the light. • Specular lighting is the light that bounces off the surface directly into your eye. • Some surfaces are shinier than others. Consider a steel ball versus a ball of chalk.
Positioning the light • The position of the light is irrelevant in the ambient lighting calculations. • The position of the light is relevant in calculating diffuse lighting, but the position of the camera is not. • The position of the light AND the position of the camera are both used in calculating specular highlights. Ambient Diffuse Specular
Lighting in OpenGL • The following sample code will set up a simple light: • float amb[4] = { 0.3, 0.3, 0.3, 1.0 }; • float dif[4] = { 0.5, 0.5, 0.5, 1.0 }; • float pos[4] = { 0, 0, -10, 0 }; • glLightfv(GL_LIGHT0, GL_AMBIENT, amb); • glLightfv(GL_LIGHT0, GL_DIFFUSE, dif); • glLightfv(GL_LIGHT0, GL_POSITION, pos); • glEnable(GL_LIGHT0); • glEnable(GL_LIGHTING); • glEnable(GL_COLOR_MATERIAL); • GL_LIGHT0 is the first possible light in GL.
Lighting in OpenGL - specular highlights • When using specular lighting in OpenGL, the default ‘shininess’ setting used in the lighting calculations is set to the maximum possible shininess: • To see a clear highlight, you’ll need to turn down the shine: glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 10); // possible range: 0--128.
Positioning a light in OpenGL • The position of a light in GL is just like the position of a glVertex(), transformed by the current matrix transforms. • That means that to properly position a light in the scene, you need to call glLightfv(GL_LIGHT0, GL_POSITION, pos) after you’ve set your camera’s position with gluLookAt(). • Order of operations in your render routine will be: • Clear color and depth buffers; load identity matrix • Load camera position • Load light(s) position(s) • Render geometry
Shading Models • There are a couple of different ways to shade your model: Flat Shading Smooth Shading
Shading Models • In flat shading, the renderer uses the normal of the polygon to shade the entire polygon uniformly, from corner to corner. P2 P3 P1 P5 P4
Shading models • In smooth rendering, the renderer uses the normal at each corner to calculate the shading of the surface at that exact spot, then interpolates the color across the polygon from corner to corner. • This is called Gouraud shading. • An even more advanced technique is Phong shading. In Phong shading, the normals themselves are interpolated across the polygon, producing an even smoother shading effect--but at the cost of much higher rendering time.
Shading models in OpenGL • To specify the shading model in OpenGL: • Smooth shading: • glShadeModel(GL_SMOOTH); • Flat shading: • glShadeModel(GL_FLAT); • Note that solid wireframe models look best in flat shading, but most curving surfaces look best in smooth shading.
Backface Culling • Another use of normals is backface culling, an optimisation to speed up rendering. • If you have a solid object, opaque, then you can’t see the back of it because the front is in the way. • So you can optimize rendering and never even process any polygon on the back of the object. • How to tell if it’s on the back? Well, one sure-fire trick is to see if the polygon faces away from the camera. If we see the poly’s back then there must be some other poly between that poly and our camera.
Backface Culling • You can tell whether a polygon faces away from the camera from its normal. • If the normal of the polygon doesn’t point towards the camera, it must point away from us; which means we can cull it. • To test whether the normal points towards us or away, take the dot product of the normal of the polygon with the camera’s view vector. If the dot product is positive, then the two vectors point in the same direction; cull away! Positive dot products Negative dot products
Backface Culling in OpenGL • By default, backface culling is enabled in OpenGL. • To enable backface culling: • glEnable(GL_CULL_FACE); • To disable backface culling: • glDisable(GL_CULL_FACE); • To choose between culling fronts or backs: • glCullFace(GL_FRONT); • glCullFace(GL_BACK); // (Default)