package classwork; import javax.media.opengl.*; import java.awt.event.KeyEvent; import jocode.*; import jomodel.*; /** * GLART_4_light_triangles_smooth.java * * Demonstrate smoothing by averaging normals across shared triangles. * Shines a light on a three triangles arranged to create a curved surface. * * Assigns normals to each vertex. Vertices that have more than one * neighboring triangle will get the average normal of the two or three * neighbor triangles. * * Use arrow keys to rotate model. */ public class GLART_4_light_triangles_smooth extends JOApp { float rotation = 0; // Color value for the overall scene lighting float sceneAmbientLight[] = { 0.18f, 0.1f, .15f, 1f }; // Color values for light float lightDiffuse[] = { 1f, 1f, .8f, 1f }; float lightSpecular[] = { 1f, 1f, .8f, 1f }; float lightAmbient[] = { 0.5f, 0.5f, .4f, 1f }; // Light position: if last value is 0, then this describes light direction. If 1, then light position. float lightPosition[] = { -4f, 3f, 3f, 1f }; /** * Main function just creates and runs the application. */ public static void main(String args[]) { GLART_4_light_triangles_smooth app = new GLART_4_light_triangles_smooth(); app.run(); } /** * Initialize OpenGL * */ public void setup() { // Select the Projection Matrix (controls perspective) gl.glMatrixMode(GL.GL_PROJECTION); gl.glLoadIdentity(); // Reset The Projection Matrix // Define perspective glu.gluPerspective( 45.0f, // Field Of View (float)getWidth() / (float)getHeight(), // aspect ratio 0.1f, // near Z clipping plane 100.0f); // far Z clipping plane // Select The Modelview Matrix (controls model orientation) gl.glMatrixMode(GL.GL_MODELVIEW); // make sure OpenGL correctly layers objects gl.glEnable(GL.GL_DEPTH_TEST); // OpenGL won't draw backward facing triangles ("back faces") gl.glEnable(GL.GL_CULL_FACE); // turn lighting on (does not create a light) gl.glEnable(GL.GL_LIGHTING); // Create a light // diffuse is the color of direct light from this light source // ambient is the color of scattered light from this source // position is where the light is, or it's direction setLight( GL.GL_LIGHT1, lightDiffuse, lightAmbient, lightSpecular, lightPosition ); // overall scene lighting setAmbientLight(sceneAmbientLight); // How to shade faces: // GL_FLAT -- each face has same lighting (looks faceted) // GL_SMOOTH -- lighting is blended across face gl.glShadeModel(GL.GL_SMOOTH); } /** * rotate scene if right or left arrow keys are pressed */ public void keyDown(int keycode) { if(keycode == KeyEvent.VK_RIGHT) { rotation -= .5f; } else if(keycode == KeyEvent.VK_LEFT) { rotation += .5f; } } /** * Render the scene. */ public void draw() { // Vertices for three triangles (three verts each) JOVector[] tri1 = new JOVector[3]; JOVector[] tri2 = new JOVector[3]; JOVector[] tri3 = new JOVector[3]; // Normals for three triangles (three normals each) JOVector[] norms1 = new JOVector[3]; JOVector[] norms2 = new JOVector[3]; JOVector[] norms3 = new JOVector[3]; // triangle vertices (center) tri1[0] = new JOVector( 0f, .75f, 0f); // top tri1[1] = new JOVector(-1f, -1f, 0f); // lower left tri1[2] = new JOVector( 1f, -1f, 0f); // lower right // triangle vertices (left) tri2[0] = new JOVector(-1f, -1f, 0f); // bottom tri2[1] = new JOVector( 0f, .75f, 0f); // top right tri2[2] = new JOVector(-2f, .75f, -.8f); // top left // triangle vertices (right) tri3[0] = new JOVector( 1f, -1f, 0f); // bottom tri3[1] = new JOVector( 2f, .75f, -.8f); // top right tri3[2] = new JOVector( 0f, .75f, 0f); // top left // calculate normal of each triangle JOVector normal1 = JOVector.getNormal(tri1[0], tri1[1], tri1[2]); JOVector normal2 = JOVector.getNormal(tri2[0], tri2[1], tri2[2]); JOVector normal3 = JOVector.getNormal(tri3[0], tri3[1], tri3[2]); // average normals of center and left tri JOVector normAvg12 = JOVector.add(normal1,normal2); normAvg12.div(2).normalize(); // average normals of center and right tri JOVector normAvg13 = JOVector.add(normal1,normal3); normAvg13.div(2).normalize(); // average normals of all three JOVector normAvg123 = JOVector.add(normal1,normal2).add(normal3); normAvg123.div(3).normalize(); boolean useFaceNormals = true; if (useFaceNormals) { // use one face normal for all verts in the triangle // each triangle will look flat with sharp edges // triangle vertices center norms1[0] = normal1; // top (shared by all) norms1[1] = normal1; // lower left (shared) norms1[2] = normal1; // lower right (shared) // triangle vertices left norms2[0] = normal2; // bottom (shared) norms2[1] = normal2; // top right (shared by all) norms2[2] = normal2; // top left // triangle vertices right norms3[0] = normal3; // bottom (shared) norms3[1] = normal3; // top right norms3[2] = normal3; // top left (shared by all) } else { // use averaged normals: at each vertex calculate the average normal // of all the verts that meet at that point. This smooths the edges // of the triangles. // triangle vertices center norms1[0] = normAvg123; // top (shared by all) norms1[1] = normAvg12; // lower left (shared) norms1[2] = normAvg13; // lower right (shared) // triangle vertices left norms2[0] = normAvg12; // bottom (shared) norms2[1] = normAvg123; // top right (shared by all) norms2[2] = normal2; // top left // triangle vertices right norms3[0] = normAvg13; // bottom (shared) norms3[1] = normal3; // top right norms3[2] = normAvg123; // top left (shared by all) } // Clear screen and depth buffer gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // Select The Modelview Matrix (controls model orientation) // and reset the coordinate system to center of screen gl.glMatrixMode(GL.GL_MODELVIEW); gl.glLoadIdentity(); // Where is the 'eye' glu.gluLookAt( 0f, 0f, 5f, // eye position 0f, 0f, 0f, // target to look at 0f, 1f, 0f); // which way is up // lines will be 2 pixels wide gl.glLineWidth(2); // Draw the triangles and normals gl.glPushMatrix(); { // rotate scene around Y axis gl.glRotatef(rotation, 0,1,0); gl.glColor4f(1, 0, 0, 1); drawTriangle(tri1,norms1); gl.glColor4f(0, 1, 0, 1); drawTriangle(tri2,norms2); gl.glColor4f(0, 0, 1, 1); drawTriangle(tri3,norms3); } gl.glPopMatrix(); } public void drawTriangle(JOVector[] verts, JOVector[] norms) { // draw a lit triangle gl.glBegin(GL.GL_TRIANGLES); { // normal and vertex 1 gl.glNormal3f(norms[0].x, norms[0].y, norms[0].z); gl.glVertex3f(verts[0].x, verts[0].y, verts[0].z); // normal and vertex 2 gl.glNormal3f(norms[1].x, norms[1].y, norms[1].z); gl.glVertex3f(verts[1].x, verts[1].y, verts[1].z); // normal and vertex 3 gl.glNormal3f(norms[2].x, norms[2].y, norms[2].z); gl.glVertex3f(verts[2].x, verts[2].y, verts[2].z); } gl.glEnd(); // draw normals for each vertex drawNormal(verts[0],norms[0]); drawNormal(verts[1],norms[1]); drawNormal(verts[2],norms[2]); } /** * Draw a red line to indicate the normal. Disable lighting * so we can use glColor(). ReEnable lighting before returning. */ public void drawNormal(JOVector point, JOVector normal) { // draw a red line to indicate where the normal is gl.glDisable(GL.GL_LIGHTING); // turn off so we can use glColor() gl.glBegin(GL.GL_LINES); { gl.glVertex3f(point.x, point.y, point.z); gl.glVertex3f(point.x+normal.x, point.y+normal.y, point.z+normal.z); } gl.glEnd(); gl.glEnable(GL.GL_LIGHTING); } }