/** * @(#) softshadow.java * @(#) author: Tom McReynolds, SGI (converted to Java by Ron Cemer) */ /* Using the accumulation buffer for soft shadows. */ /* Demonstrate the use of accumulation buffer to create soft shadows */ import java.applet.*; import java.awt.*; import java.awt.event.*; import java.lang.*; import java.util.*; import java.io.*; import java.util.*; import gl4java.GLContext; import gl4java.awt.GLCanvas; public class softshadow extends Applet { private static final int SPHERE=1, CONE=2, LIGHT=3, LEFTWALL=4, FLOOR=5; private static final int X = 0, Y = 1, Z = 2; private static final int A = 0, B = 1, C = 2, D = 3; private static final int NONE = 0, SHADOW = 1; private static final int TEXDIM = 256; softshadowCanvas canvas = null; /* Initialize the applet */ public void init() { Dimension d = getSize(); setLayout(new BorderLayout()); canvas = new softshadowCanvas(d.width, d.height); add("Center", canvas); } /* Start the applet */ public void start() { } /* Stop the applet */ public void stop() { } /* Destroy the applet */ private class ShadObj { float[] vertices; float[] normal; int n; } /* Local GLCanvas extension class */ private class softshadowCanvas extends GLCanvas implements MouseListener { private float leftwallshadow[] = new float[16]; private float floorshadow[] = new float[16]; private float lightpos[] = { 50.0f, 50.0f, -320.0f, 1.0f }; private int rendermode = SHADOW; public softshadowCanvas(int w, int h) { super(w, h); GLContext.gljNativeDebug = false; GLContext.gljClassDebug = false; } public void preInit() { doubleBuffer = true; stereoView = false; stencilBits = 3; accumSize = 8; } public void init() { System.out.println("init(): " + this); reshape(getSize().width, getSize().height); /* turn on features */ gl.glEnable(GL_DEPTH_TEST); gl.glEnable(GL_LIGHTING); gl.glEnable(GL_LIGHT0); /* make shadow matricies */ float v0[] = new float[3]; float v1[] = new float[3]; float v2[] = new float[3]; /* 3 points on floor */ v0[X] = -100.0f; v0[Y] = -100.0f; v0[Z] = -320.0f; v1[X] = 100.0f; v1[Y] = -100.0f; v1[Z] = -320.0f; v2[X] = 100.0f; v2[Y] = -100.0f; v2[Z] = -520.0f; float plane[] = new float[4]; findplane(plane, v0, v1, v2); shadowmatrix(floorshadow, plane, lightpos); /* 3 points on left wall */ v0[X] = -100.0f; v0[Y] = -100.0f; v0[Z] = -320.0f; v1[X] = -100.0f; v1[Y] = -100.0f; v1[Z] = -520.0f; v2[X] = -100.0f; v2[Y] = 100.0f; v2[Z] = -520.0f; findplane(plane, v0, v1, v2); shadowmatrix(leftwallshadow, plane, lightpos); /* place light 0 in the right place */ gl.glLightfv(GL_LIGHT0, GL_POSITION, lightpos); /* remove back faces to speed things up */ gl.glCullFace(GL_BACK); gl.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); /* make display lists for sphere and cone; for efficiency */ long sphere, cone, base; gl.glNewList(SPHERE, GL_COMPILE); sphere = glu.gluNewQuadric(); glu.gluSphere(sphere, 20.f, 20, 20); glu.gluDeleteQuadric(sphere); gl.glEndList(); gl.glNewList(LIGHT, GL_COMPILE); sphere = glu.gluNewQuadric(); glu.gluSphere(sphere, 5.f, 20, 20); glu.gluDeleteQuadric(sphere); gl.glEndList(); gl.glNewList(CONE, GL_COMPILE); cone = glu.gluNewQuadric(); base = glu.gluNewQuadric(); gl.glRotatef(-90.f, 1.f, 0.f, 0.f); glu.gluDisk(base, 0., 20., 20, 1); glu.gluCylinder(cone, 20., 0., 60., 20, 20); glu.gluDeleteQuadric(cone); glu.gluDeleteQuadric(base); gl.glEndList(); gl.glNewList(FLOOR, GL_COMPILE); gl.glEndList(); gl.glNewList(LEFTWALL, GL_COMPILE); gl.glEndList(); byte tex[] = make_texture(TEXDIM, TEXDIM); gl.glTexImage2D (GL_TEXTURE_2D, 0, 1, TEXDIM, TEXDIM, 0, GL_RED, GL_UNSIGNED_BYTE, tex); tex = null; glj.gljCheckGL(); addMouseListener(this); } public void cvsDispose() { System.out.println("destroy(): " + this); removeMouseListener(this); super.cvsDispose(); } public void reshape(int width, int height) { gl.glViewport(0,0,width,height); /* draw a perspective scene */ gl.glMatrixMode(GL_PROJECTION); gl.glFrustum(-100.0f, 100.0f, -100.0f, 100.0f, 320.0f, 640.0f); gl.glMatrixMode(GL_MODELVIEW); } public void display() { if (glj.gljMakeCurrent() == false) return; int dx, dy, dz; dy = 0; /* jitter the light around */ if (rendermode == SHADOW) { gl.glClear(GL_ACCUM_BUFFER_BIT); for (dz = -4; dz < 5; dz += 2) { for (dx = -4; dx < 5; dx += 2) { render((float)dx, (float)dy, (float)dz); gl.glAccum(GL_ACCUM, 1.0f / 25.0f); } } gl.glAccum(GL_RETURN, 1.0f); } else render(0.0f, 0.0f, 0.0f); glj.gljSwap(); glj.gljCheckGL(); glj.gljFree(); } // Methods required for the implementation of MouseListener public void mouseEntered( MouseEvent evt ) { } public void mouseExited( MouseEvent evt ) { } public void mousePressed( MouseEvent evt ) { rendermode++; if (rendermode > SHADOW) rendermode = NONE; repaint(); } public void mouseReleased( MouseEvent evt ) { } public void mouseClicked( MouseEvent evt ) { } private byte[] make_texture(int maxs, int maxt) { byte texture[] = new byte[maxs*maxt]; for (int t = 0; t < maxt; t++) { for (int s = 0; s < maxs; s++) { texture[s + maxs * t] = ( (((s >> 4) & 0x1) != 0) ^ (((t >> 4) & 0x1) != 0) ) ? (byte)0xff : (byte)0; } } return texture; } /* create a matrix that will project the desired shadow */ void shadowmatrix(float shadowMat[], float groundplane[], float lightpos[]) { float dot; /* find dot product between light position vector and ground plane normal */ dot = groundplane[0] * lightpos[0] + groundplane[1] * lightpos[1] + groundplane[2] * lightpos[2] + groundplane[3] * lightpos[3]; shadowMat[0] = dot - lightpos[0] * groundplane[0]; shadowMat[4] = -lightpos[0] * groundplane[1]; shadowMat[8] = -lightpos[0] * groundplane[2]; shadowMat[12] = -lightpos[0] * groundplane[3]; shadowMat[1] = -lightpos[1] * groundplane[0]; shadowMat[5] = dot - lightpos[1] * groundplane[1]; shadowMat[9] = -lightpos[1] * groundplane[2]; shadowMat[13] = -lightpos[1] * groundplane[3]; shadowMat[2] = -lightpos[2] * groundplane[0]; shadowMat[6] = -lightpos[2] * groundplane[1]; shadowMat[10] = dot - lightpos[2] * groundplane[2]; shadowMat[14] = -lightpos[2] * groundplane[3]; shadowMat[3] = -lightpos[3] * groundplane[0]; shadowMat[7] = -lightpos[3] * groundplane[1]; shadowMat[11] = -lightpos[3] * groundplane[2]; shadowMat[15] = dot - lightpos[3] * groundplane[3]; } /* find the plane equation given 3 points */ void findplane(float plane[], float v0[], float v1[], float v2[]) { float vec0[] = new float[3], vec1[] = new float[3]; /* need 2 vectors to find cross product */ vec0[X] = v1[X] - v0[X]; vec0[Y] = v1[Y] - v0[Y]; vec0[Z] = v1[Z] - v0[Z]; vec1[X] = v2[X] - v0[X]; vec1[Y] = v2[Y] - v0[Y]; vec1[Z] = v2[Z] - v0[Z]; /* find cross product to get A, B, and C of plane equation */ plane[A] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y]; plane[B] = -(vec0[X] * vec1[Z] - vec0[Z] * vec1[X]); plane[C] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X]; plane[D] = -(plane[A]*v0[X] + plane[B]*v0[Y] + plane[C]*v0[Z]); } private void sphere() { gl.glPushMatrix(); gl.glTranslatef(60.0f, -50.0f, -360.0f); gl.glCallList(SPHERE); gl.glPopMatrix(); } private void cone() { gl.glPushMatrix(); gl.glTranslatef(-40.0f, -40.0f, -400.0f); gl.glCallList(CONE); gl.glPopMatrix(); } /* render while jittering the shadows */ private void render(float dx, float dy, float dz) { float v[] = new float[3]; /* material properties for objects in scene */ float wall_mat[] = {1.0f, 1.0f, 1.0f, 1.0f}; float sphere_mat[] = {1.0f, 0.5f, 0.0f, 1.0f}; float cone_mat[] = {0.0f, 0.5f, 1.0f, 1.0f}; gl.glClear (GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); /* Note: wall vertices are ordered so they are all front facing this lets me do back face culling to speed things up. */ gl.glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, wall_mat); /* floor */ /* make the floor textured */ gl.glEnable(GL_TEXTURE_2D); /* Since we want to turn texturing on for floor only, we have to make floor a separate glBegin()/glEnd() sequence. You can't turn texturing on and off between begin and end calls */ gl.glBegin(GL_QUADS); gl.glNormal3f(0.0f, 1.0f, 0.0f); gl.glTexCoord2i(0, 0); gl.glVertex3f(-100.0f, -100.0f, -320.0f); gl.glTexCoord2i(1, 0); gl.glVertex3f(100.0f, -100.0f, -320.0f); gl.glTexCoord2i(1, 1); gl.glVertex3f(100.0f, -100.0f, -520.0f); gl.glTexCoord2i(0, 1); gl.glVertex3f(-100.0f, -100.0f, -520.0f); gl.glEnd(); gl.glDisable(GL_TEXTURE_2D); if (rendermode == SHADOW) { gl.glDisable(GL_DEPTH_TEST); gl.glDisable(GL_LIGHTING); gl.glColor3f(0.f, 0.f, 0.f); /* shadow color */ gl.glPushMatrix(); gl.glMultMatrixf(floorshadow); gl.glTranslatef(dx, dy, dz); cone(); gl.glPopMatrix(); gl.glPushMatrix(); gl.glMultMatrixf(floorshadow); gl.glTranslatef(dx, dy, dz); sphere(); gl.glPopMatrix(); gl.glEnable(GL_DEPTH_TEST); gl.glEnable(GL_LIGHTING); } /* walls */ if (rendermode == SHADOW) { gl.glEnable(GL_STENCIL_TEST); gl.glStencilFunc(GL_ALWAYS, 1, 0); gl.glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); } /* left wall */ gl.glBegin(GL_QUADS); gl.glNormal3f(1.0f, 0.0f, 0.0f); gl.glVertex3f(-100.0f, -100.0f, -320.0f); gl.glVertex3f(-100.0f, -100.0f, -520.0f); gl.glVertex3f(-100.0f, 100.0f, -520.0f); gl.glVertex3f(-100.0f, 100.0f, -320.0f); gl.glEnd(); if (rendermode == SHADOW) { gl.glStencilFunc(GL_EQUAL, 1, 1); gl.glDisable(GL_DEPTH_TEST); gl.glDisable(GL_LIGHTING); gl.glColor3f(0.0f, 0.0f, 0.0f); /* shadow color */ gl.glDisable(GL_DEPTH_TEST); gl.glPushMatrix(); gl.glMultMatrixf(leftwallshadow); gl.glTranslatef(dx, dy, dz); cone(); gl.glPopMatrix(); gl.glEnable(GL_DEPTH_TEST); gl.glDisable(GL_STENCIL_TEST); gl.glEnable(GL_DEPTH_TEST); gl.glEnable(GL_LIGHTING); } gl.glBegin(GL_QUADS); /* right wall */ gl.glNormal3f(-1.0f, 0.0f, 0.0f); gl.glVertex3f(100.0f, -100.0f, -320.0f); gl.glVertex3f(100.0f, 100.0f, -320.0f); gl.glVertex3f(100.0f, 100.0f, -520.0f); gl.glVertex3f(100.0f, -100.0f, -520.0f); /* ceiling */ gl.glNormal3f(0.0f, -1.0f, 0.0f); gl.glVertex3f(-100.0f, 100.0f, -320.0f); gl.glVertex3f(-100.0f, 100.0f, -520.0f); gl.glVertex3f(100.0f, 100.0f, -520.0f); gl.glVertex3f(100.0f, 100.0f, -320.0f); /* back wall */ gl.glNormal3f(0.0f, 0.0f, 1.0f); gl.glVertex3f(-100.0f, -100.0f, -520.0f); gl.glVertex3f(100.0f, -100.0f, -520.0f); gl.glVertex3f(100.0f, 100.0f, -520.0f); gl.glVertex3f(-100.0f, 100.0f, -520.0f); gl.glEnd(); gl.glPushMatrix(); gl.glTranslatef(lightpos[X], lightpos[Y], lightpos[Z]); gl.glDisable(GL_LIGHTING); gl.glColor3f(1.0f, 1.0f, 0.7f); gl.glCallList(LIGHT); gl.glEnable(GL_LIGHTING); gl.glPopMatrix(); gl.glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cone_mat); cone(); gl.glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, sphere_mat); sphere(); } /* simple way to extend a point to build shadow volume */ private void extend(float newvertex[], float light[], float vertex[], float t) { float delta[] = new float[3]; delta[X] = vertex[X] - light[X]; delta[Y] = vertex[Y] - light[Y]; delta[Z] = vertex[Z] - light[Z]; newvertex[X] = light[X] + delta[X] * t; newvertex[Y] = light[Y] + delta[Y] * t; newvertex[Z] = light[Z] + delta[Z] * t; } } }