package classwork; import javax.media.opengl.*; import javax.media.opengl.glu.*; import java.awt.event.*; import jocode.*; import jomodel.*; /** * GLART_5_transparent_material_problem * * Draw a row of transparent textured spheres. Use left,right arrow keys to rotate scene. * * Set the alpha value in the diffuse material color to make a material transparent. * * This demo shows the front-to-back drawing problem that transparent * shapes have. The depth test throws out any objects that are overlapped * by objects closer to the eye. This is a problem when drawing transparent * surfaces because we want to see the background objects through the * foreground objects, but they aren't rendered. * * If you disable the depth test, then all objects are drawn, but as you * rotate the sccene the objects are clearly not drawn in the right order. * They are drawn in the order they appear in the code, not in the spatial * order they appear in the scene. * * We need to draw background objects first, then foreground objects, so * that we can see the background objects through the transparent foreground * objects. See GLART_5_transparent_material_solution.java for a method * that solves the problem of rendering transparent objects. * * @see GLART_5_transparent_material_solution */ public class GLART_5_transparent_material_problem extends JOApp { float rotation = 0; // Material objects will hold surface color values JOMaterial clearMtl = new JOMaterial(); JOMaterial opaqueMtl = new JOMaterial(); // texture handle (a number that refers to an allocated texture) int marbleTextureHandle = 0; /** * Main function just creates and runs the application. */ public static void main(String args[]) { GLART_5_transparent_material_problem app = new GLART_5_transparent_material_problem(); displayWidth = 800; displayHeight = 600; 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 // specular is the hightlight color // ambient is the color of scattered light from this source // position is where the light is, or it's direction float lightDiffuse[] = { .9f, .9f, .8f, 1f }; // direct light float lightSpecular[] = { .9f, .9f, .8f, 1f }; // highlight float lightAmbient[] = { .1f, .1f, .0f, 1f }; // scattered light float lightPosition[] = { -4f, 4f, 6, 1f }; // Last value is 1: this is light position. setLight( GL.GL_LIGHT1, lightDiffuse, lightAmbient, lightSpecular, lightPosition ); // overall scene lighting setAmbientLight( new float[] { .7f, .7f, .7f, 1f } ); // make the transparent material clearMtl.setDiffuse( new float[] { .9f, .95f, .9f, .5f }); // white/green .5f in the fourth position is ALPHA value clearMtl.setAmbient( new float[] { .7f, .75f, .7f, 1f }); // light gray greenish clearMtl.setSpecular( new float[] { .9f, .9f, .9f, 1f }); // almost white: very reflective clearMtl.setShininess(127f); // 0=no shine, 127=max shine // make the opaque material opaqueMtl.setDiffuse(new float[] { 1f, .3f, .3f, 1f }); // reddish opaqueMtl.setAmbient(new float[] { .8f, .2f, .2f, 1f }); // reddish // set the background color gl.glClearColor(.1f, .1f, .23f, 1); // blending gl.glEnable(GL.GL_BLEND); gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); // Force normals to length 1 gl.glEnable(GL.GL_NORMALIZE); // Draw specular highlghts on top of textures (GL12.GL_SINGLE_COLOR to reset) gl.glLightModeli(GL.GL_LIGHT_MODEL_COLOR_CONTROL, GL.GL_SEPARATE_SPECULAR_COLOR ); // Create a texture from the image marbleTextureHandle = makeTexture("images/marble.jpg"); } /** * Render the scene. */ public void draw() { // 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.glLoadIdentity(); // Where is the 'eye' glu.gluLookAt( -2f, 0f, 6f, // eye position 0f, 0f, 0f, // target to look at 0f, 1f, 0f); // which way is up // Can try to disable the depth test so that background objects still render, but this causes other problems //gl.glDisable(GL.GL_DEPTH_TEST); // rotate scene gl.glRotatef(rotation, 0,1,0); // Clear screen and depth buffer again gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // activate opaque material and draw sphere opaqueMtl.apply(); drawSphereAt(0f,0f,-4f); // activate transparent material, draw 3 transparent spheres clearMtl.apply(); drawSphereAt(0f, 0f, -2f); drawSphereAt(0f, 0f, 0f); drawSphereAt(0f, 0f, 2f); // draw opaque sphere at other end of line opaqueMtl.apply(); drawSphereAt(0f,0f,4f); } /** * Translate to the given world position and draw a sphere. * Return the Z depth of that position. */ public void drawSphereAt(float x, float y, float z) { gl.glPushMatrix(); { // shift to sphere position gl.glTranslatef(x,y,z); // draw double-sided sphere draw2SidedSphere(); } gl.glPopMatrix(); } /** * Draw a sphere with another slightly smaller sphere inside. The inner sphere faces * inside, creating the effect that the sphere is a glass shell and can reflect * light both on the outer surface and the inner surface. */ public void draw2SidedSphere() { gl.glPushMatrix(); { // draw inside-facing surface a little smaller gl.glScalef(.99f,.99f,.99f); renderSphereInside(); } gl.glPopMatrix(); gl.glPushMatrix(); { // draw outside-facing sphere a little bigger gl.glScalef(1.01f, 1.01f, 1.01f); renderSphere(); } gl.glPopMatrix(); } /** * call glu functions to create geometry for a 1 unit sphere, with normals. * NOTE: this is not optimized at all. */ public static void renderSphereInside() { GLUquadric s = glu.gluNewQuadric(); glu.gluQuadricOrientation(s, GLU.GLU_INSIDE); // normals point inwards glu.gluQuadricTexture(s, true); glu.gluSphere(s, 1, 48, 48); // run GL commands to draw sphere } public static void renderSphere() { GLUquadric s = glu.gluNewQuadric(); glu.gluQuadricOrientation(s, GLU.GLU_OUTSIDE); // normals point outwards glu.gluQuadricTexture(s, true); glu.gluSphere(s, 1, 48, 48); // run GL commands to draw sphere } /** * rotate scene if right or left arrow keys are pressed */ public void keyDown(int keycode) { if(keycode == KeyEvent.VK_RIGHT) { rotation -= 2f; } else if(keycode == KeyEvent.VK_LEFT) { rotation += 2f; } } }