package classwork; import java.nio.*; import java.awt.event.KeyEvent; import javax.media.opengl.*; import jocode.*; import jomodel.*; /** * A basic shadow demo: render a model and cast an opaque shadow on the floor. * Uses a matrix to project the object onto a plane. * * Hit the L key to toggle between the eye viewpoint and the light viewpoint. * Hit the Z key to zoom in/out. * HIT the P key to pause/unpause the rotation. */ public class GLART_9_Shadow extends JOApp { JOModel obj; int texture; float rotateModelY = 0; boolean showLightViewpoint = false; boolean paused = false; boolean zoomed = true; // will hold the shadow matrix (see setup() and makeShadowMatrix()) FloatBuffer fShadowMatrix; // light position: if last value is 0, then this describes light direction. float lightPosition[] = { -3f, 10f, 3, 1f }; // equation for the plane the shadow will fall on (the normal for the plane, 0,1,0) float[] rightWallPlane = new float[] {-1f,0f,0f,2f}; float[] leftWallPlane = new float[] {1f,0f,0f,2f}; float[] floorPlane = new float[] {0f,1f,0f,0f}; float[] plane = floorPlane; /** * create and run the application. */ public static void main(String args[]) { GLART_9_Shadow app = new GLART_9_Shadow(); windowTitle = "Shadow Demo"; displayWidth = 800; displayHeight = 600; app.run(); } /** * Initialize the environment */ public void setup() { // load model obj = new JOModel("models/cow.obj"); obj.regenerateNormals(); // prepare a texture texture = makeTexture("images/Flag_of_the_United_States.png"); // color of overall scene lighting float ambient[] = { .1f, .1f, .1f, 1f }; // color of light source float lightDiffuse[] = { 1f, 1f, .9f, 1f }; // direct light float lightSpecular[] = { 1f, 1f, .9f, 1f }; // highlight float lightAmbient[] = { .3f, .3f, .4f, 1f }; // scattered light // setup material JOMaterial material = new JOMaterial(); material = new JOMaterial(); material.setDiffuse(new float[] {1f,1f,1f,1f}); material.setAmbient(new float[] {.8f,.8f,.8f,1f}); material.apply(); // Select the Projection Matrix (controls perspective) gl.glMatrixMode(GL.GL_PROJECTION); gl.glLoadIdentity(); // Define perspective glu.gluPerspective( 30.0f, // Field Of View getWidth()/getHeight(), // aspect ratio 0.01f, // near Z clipping plane 1000.0f); // far Z clipping plane // Select The Modelview Matrix (controls model orientation) gl.glMatrixMode(GL.GL_MODELVIEW); gl.glLoadIdentity(); // Reset The Modelview Matrix // make sure OpenGL correctly layers objects gl.glEnable(GL.GL_DEPTH_TEST); // turn on texturing gl.glEnable(GL.GL_TEXTURE_2D); // don't draw backward facing triangles ("back faces") gl.glEnable(GL.GL_CULL_FACE); // turn lighting on gl.glEnable(GL.GL_LIGHTING); // set the background color gl.glClearColor(.5f, .5f, .8f, 1); // overall scene lighting setAmbientLight(ambient); // Create a light setLight( GL.GL_LIGHT1, lightDiffuse, lightAmbient, lightSpecular, lightPosition ); ////////////////////////////////////////////////////////////// // For Shadows: define a matrix based on light position and shadow surface float[] fShadowMatrixArray = new float[16]; makeShadowMatrix(fShadowMatrixArray, lightPosition, plane); System.out.println(); for (int i=0; i < 16; i++) { System.out.print(fShadowMatrixArray[i] + ","); } System.out.println(); // convert the float array to a FloatBuffer fShadowMatrix = allocFloats(fShadowMatrixArray); /////////////////////////////////////////////////////////////// } /** * Render the scene. */ public void draw() { if (!paused) { rotateModelY += .2; } // Clear screen and depth buffer gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // reset the coordinate system to center of screen gl.glMatrixMode(GL.GL_MODELVIEW); gl.glLoadIdentity(); // Place the viewpoint at the light position or eye position if (showLightViewpoint) { glu.gluLookAt( lightPosition[0], lightPosition[1], lightPosition[2], 0, 0, 0, 0f, 1.8f, 0f); } else { glu.gluLookAt( 0, 5, (zoomed? 15 : 25), // camera position 0, (zoomed? 2 : 5), 0, 0f, 1f, 0f); } // draw a sphere at the light position gl.glDisable(GL.GL_LIGHTING); gl.glBindTexture(GL.GL_TEXTURE_2D,0); gl.glColor4f(1,1,1,1); gl.glPushMatrix(); { gl.glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]); gl.glScalef(.2f,.2f,.2f); renderSphere(); } gl.glPopMatrix(); gl.glEnable(GL.GL_LIGHTING); // draw shadow cast on floor plane drawShadow(); // draw the object normally drawObject(); } /** * draw the object that will be shadowed */ public void drawObject() { gl.glPushMatrix(); { gl.glTranslatef(0,1.8f,0); // move up a little (off the floor) gl.glScalef(.5f,.5f,.5f); // scale model down a little gl.glRotatef(rotateModelY, 0, 1, 0); // rotate around vertical axis obj.renderTextured(texture); } gl.glPopMatrix(); } /** * Draw the shadowed object projected onto a plane from the lights point of view. * This will create the right shape for a shadow, but the object will still be * fully textured. To make the object look like a shadow we turn off lighting * and texture and draw with a flat gray. Turn off the depth test so the shadow * will be drawn on top of the floor (it won't be hidden by the floor). */ public void drawShadow() { // no texture gl.glDisable(GL.GL_TEXTURE_2D); // no light gl.glDisable(GL.GL_LIGHTING); // no depth testing gl.glDisable(GL.GL_DEPTH_TEST); // dark gray color gl.glColor4f(.3f, .3f, .35f, 1f); // draw the shadow onto floor gl.glPushMatrix(); { // multiply the current modelview matrix with the shadow matrix gl.glMultMatrixf(fShadowMatrix); // draw the object to be shadowed // the shadow matrix above will flatten the object against the plane drawObject(); } gl.glPopMatrix(); // re-enable settings gl.glEnable(GL.GL_TEXTURE_2D); gl.glEnable(GL.GL_DEPTH_TEST); gl.glEnable(GL.GL_LIGHTING); } /** * Make a matrix that will transform the geometry as if it is projected onto * the given plane, from the viewpoint of the given light position. Any * object drawn while this matrix is active will be squashed flat against * a wall or floor plane. */ void makeShadowMatrix(float[] fDestMat, float[] fLightPos, float[] fPlane) { float dot; // dot product of plane and light position dot = fPlane[0] * fLightPos[0] + fPlane[1] * fLightPos[1] + fPlane[2] * fLightPos[2] + fPlane[3] * fLightPos[3]; // first column fDestMat[0] = dot - fLightPos[0] * fPlane[0]; fDestMat[4] = 0.0f - fLightPos[0] * fPlane[1]; fDestMat[8] = 0.0f - fLightPos[0] * fPlane[2]; fDestMat[12] = 0.0f - fLightPos[0] * fPlane[3]; // second column fDestMat[1] = 0.0f - fLightPos[1] * fPlane[0]; fDestMat[5] = dot - fLightPos[1] * fPlane[1]; fDestMat[9] = 0.0f - fLightPos[1] * fPlane[2]; fDestMat[13] = 0.0f - fLightPos[1] * fPlane[3]; // third column fDestMat[2] = 0.0f - fLightPos[2] * fPlane[0]; fDestMat[6] = 0.0f - fLightPos[2] * fPlane[1]; fDestMat[10] = dot - fLightPos[2] * fPlane[2]; fDestMat[14] = 0.0f - fLightPos[2] * fPlane[3]; // fourth column fDestMat[3] = 0.0f - fLightPos[3] * fPlane[0]; fDestMat[7] = 0.0f - fLightPos[3] * fPlane[1]; fDestMat[11] = 0.0f - fLightPos[3] * fPlane[2]; fDestMat[15] = dot - fLightPos[3] * fPlane[3]; } /** * */ public void keyUp(int keycode) { if(keycode == KeyEvent.VK_L) { showLightViewpoint = !showLightViewpoint; } else if(keycode == KeyEvent.VK_Z) { zoomed = !zoomed; } else if(keycode == KeyEvent.VK_P) { paused = !paused; } } }