import java.io.File; import java.io.IOException; import java.nio.FloatBuffer; import javax.media.opengl.GL; import javax.media.opengl.GL2; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLProfile; import javax.media.opengl.awt.GLCanvas; import javax.media.opengl.glu.GLU; import javax.swing.JFrame; import com.sun.opengl.util.Animator; import com.sun.opengl.util.BufferUtil; import com.sun.opengl.util.texture.Texture; import com.sun.opengl.util.texture.TextureIO; public class perspectiveshadowmapping extends JFrame implements GLEventListener { private GLCanvas canvas; private Animator animator; GLU glu; Texture texture; Texture bgtexture; static FloatBuffer verticesBuf; static FloatBuffer normalsBuf; static FloatBuffer texcoordBuf; static float[] floor_planar = new float[4]; static float t = 0.0f; public perspectiveshadowmapping() { glu = new GLU(); canvas = new GLCanvas(new GLCapabilities(GLProfile.getDefault())); canvas.addGLEventListener(this); getContentPane().add(canvas); } public void run() { setSize(320+10, 460+30); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); canvas.requestFocusInWindow(); animator = new Animator(canvas); animator.setRunAsFastAsPossible(false); animator.start(); } public static void main(String[] args) { new perspectiveshadowmapping().run(); } public void init(GLAutoDrawable drawable) { try { texture = TextureIO.newTexture(new File("wood.png"), true); bgtexture = TextureIO.newTexture(new File("grass.png"), true); } catch (IOException ex) { ex.printStackTrace(); } findPlane(floor_planar, new float[] { 10.0f, -2.0f, 10.0f}, new float[] { 10.0f, -2.0f,-10.0f}, new float[] {-10.0f, -2.0f,-10.0f}); GL2 gl = drawable.getGL().getGL2(); setupPointers(gl); gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); gl.glEnableClientState(GL2.GL_NORMAL_ARRAY); gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY); gl.glVertexPointer(3, GL2.GL_FLOAT, 0, verticesBuf); gl.glNormalPointer(GL2.GL_FLOAT, 0, normalsBuf); gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, texcoordBuf); } public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { GL2 gl = drawable.getGL().getGL2(); gl.glMatrixMode(GL2.GL_PROJECTION); gl.glLoadIdentity(); // gluPerspective( 視野角度[deg], アスペクト比(ウィンドウの幅/高さ), // 描画する距離範囲(視点から最も近い点までの距離), 視点から最も遠い点までの距離); glu.gluPerspective(60, (float)width/(float)height, 0.5, 50.0) ; // glViewport( 左下のピクセル座標X, 左下のピクセル座標Y, ウィンドウ幅,高さ ); gl.glViewport( 0, 0, width , height ); gl.glMatrixMode(GL2.GL_MODELVIEW); gl.glLoadIdentity(); } public void display(GLAutoDrawable drawable) { GL2 gl = drawable.getGL().getGL2(); float[] pM = new float[16]; float[] lightpos = new float[]{ 10, 8, 0, 1 }; float[] fogcolor = new float[]{ 1, 1, 1, 1 }; gl.glEnable(GL2.GL_FOG); gl.glFogi(GL2.GL_FOG_MODE, GL2.GL_EXP); gl.glFogf(GL2.GL_FOG_DENSITY, 0.025f); gl.glFogfv(GL2.GL_FOG_COLOR, fogcolor, 0); //クライアント領域の塗りつぶし色の設定 gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); //デプスバッファクリア値の設定 gl.glClearDepth(1.0f); //ウィンドウを塗りつぶす gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT); //オブジェクト描画モードにする。 gl.glMatrixMode(GL2.GL_MODELVIEW); gl.glLoadIdentity(); // デプスバッファ gl.glEnable(GL.GL_DEPTH_TEST); gl.glDepthFunc(GL.GL_LEQUAL); //ステンシルバッファクリア値の設定 gl.glClearStencil(0); gl.glCullFace(GL2.GL_BACK); gl.glEnable(GL.GL_CULL_FACE); gl.glEnable(GL2.GL_AUTO_NORMAL); //gl.glEnable(GL2.GL_NORMALIZE); // 光源と材質の設定 gl.glEnable(GL2.GL_COLOR_MATERIAL); gl.glColorMaterial(GL2.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE); // カメラ glu.gluLookAt( (float)(5.0*Math.sin(t)), 5, (float)(10.0*Math.cos(t)), // 視点の位置x,y,z, 0, 0, 0, // 視界の中心位置の参照点座標x,y,z 0, 1, 0); // 視界の上方向のベクトルx,y,z // 今の座標系を保存しておく gl.glPushMatrix(); // 照明を点灯する。 if ((t += 0.0005) > 100.0 ) t -= 100.0; lightpos[0] = (float)(4.5*Math.sin(t/2)); lightpos[2] = (float)(9.0*Math.cos(t/2)); gl.glEnable(GL2.GL_LIGHTING); gl.glEnable(GL2.GL_LIGHT0); gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, lightpos, 0); //オブジェクトの描画 drawBox(gl); //床の描画 drawFloor(gl, true); // 座標系を戻す gl.glPopMatrix(); for (int n = 0; n <= 1; n++) { // 今の座標系を保存しておく gl.glPushMatrix(); /* * Plannar Projected Shadow Matrix */ // 平面射影行列の算出 lightpos[0] = (float)(4.5*Math.sin(t/2 + (float)n*0.4f)); lightpos[2] = (float)(9.0*Math.cos(t/2 + (float)n*0.4f)); shadowMatrix(pM, floor_planar, lightpos); gl.glEnable(GL2.GL_LIGHTING); gl.glEnable(GL2.GL_LIGHT0); gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, lightpos, 0); //床のステンシルを付ける gl.glEnable(GL.GL_STENCIL_TEST); gl.glStencilFunc(GL.GL_ALWAYS, 1, ~0); //これから描画するもののステンシル値にすべて1タグをつける gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP ,GL.GL_REPLACE); //カラー・デプスバッファマスクをセットする //これで以下の内容のピクセルの色の値は、書き込まれない。 gl.glColorMask(false, false, false, false); gl.glDepthMask(false); //床の描画 drawFloor(gl, true); //床にオブジェクトの影のステンシルを付ける gl.glEnable(GL2.GL_STENCIL_TEST); gl.glStencilFunc(GL.GL_EQUAL, 1, ~0); //これから描画するもののステンシル値にすべて1タグをつける gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP ,GL.GL_INCR); gl.glDisable(GL.GL_DEPTH_TEST); gl.glDisable(GL.GL_CULL_FACE); gl.glPushMatrix(); gl.glMultMatrixf(pM, 0); drawBox(gl); gl.glPopMatrix(); gl.glEnable(GL.GL_DEPTH_TEST); gl.glEnable(GL.GL_CULL_FACE); //ビットマスクを解除 gl.glColorMask(true, true, true, true); gl.glDepthMask(true); //影をつける gl.glStencilFunc(GL.GL_EQUAL, 2, ~0 ); gl.glEnable(GL.GL_BLEND); gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); gl.glColor4f(0.0f, 0.0f, 0.0f, 0.4f - (float)n*0.1f); gl.glDisable(GL.GL_DEPTH_TEST); drawFloor(gl, false); gl.glEnable(GL.GL_DEPTH_TEST); gl.glDisable(GL.GL_BLEND); gl.glDisable(GL.GL_STENCIL_TEST); // 座標系を戻す gl.glPopMatrix(); } gl.glDisable(GL2.GL_AUTO_NORMAL); gl.glDisable(GL2.GL_NORMALIZE); // コマンドのフラッシュ gl.glFlush(); } private void drawBox(GL2 gl) { // Draw Box gl.glPushMatrix(); gl.glEnable(GL2.GL_TEXTURE_2D); texture.enable(); texture.bind(); for (int i = 0; i < 6; i++) { gl.glDrawArrays(GL2.GL_TRIANGLE_STRIP, i*4, 4); } texture.disable(); gl.glPopMatrix(); } private void drawFloor(GL2 gl, boolean tex) { // Draw Floor gl.glDisable(GL2.GL_LIGHTING); gl.glPushMatrix(); if (tex) { gl.glEnable(GL2.GL_TEXTURE_2D); bgtexture.enable(); bgtexture.bind(); gl.glDrawArrays(GL2.GL_TRIANGLE_STRIP, 6*4, 4); bgtexture.disable(); } else { gl.glDisable(GL2.GL_TEXTURE_2D); gl.glDrawArrays(GL2.GL_TRIANGLE_STRIP, 6*4, 4); } gl.glPopMatrix(); gl.glEnable(GL2.GL_LIGHTING); } private void setupPointers(GL2 gl) { float vertices[] = new float[] { 1,0,0, 0,0,0, 1,1,0, 0,1,0, 0,0,0, 1,0,0, 0,0,1, 1,0,1, 1,0,0, 1,1,0, 1,0,1, 1,1,1, 1,1,0, 0,1,0, 1,1,1, 0,1,1, 0,1,0, 0,0,0, 0,1,1, 0,0,1, 0,0,1, 1,0,1, 0,1,1, 1,1,1, 15.0f, -3.0f, 15.0f, 15.0f, -3.0f,-15.0f, -15.0f, -3.0f, 15.0f, -15.0f, -3.0f,-15.0f }; this.verticesBuf = mkFloatBuf(vertices, 2.0f); float normals[] = new float[] { 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1, 0,-1,0, 0,-1,0, 0,-1,0, 0,-1,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, -1,0,0, -1,0,0, -1,0,0, -1,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1 }; this.normalsBuf = mkFloatBuf(normals, 1.0f); float texcoord[] = new float[] { 0,0, 1,0, 0,1, 1,1, 0,0, 1,0, 0,1, 1,1, 0,0, 1,0, 0,1, 1,1, 0,0, 1,0, 0,1, 1,1, 0,0, 1,0, 0,1, 1,1, 0,0, 1,0, 0,1, 1,1, 0,0, 1,0, 0,1, 1,1 }; this.texcoordBuf = mkFloatBuf(texcoord, 1.0f); } private FloatBuffer mkFloatBuf(float[] vertices, float ratio) { FloatBuffer tmpVerticesBuf = BufferUtil.newFloatBuffer(vertices.length); for (int i = 0; i < vertices.length; i++) tmpVerticesBuf.put(vertices[i] * ratio); tmpVerticesBuf.rewind(); return tmpVerticesBuf; } /* * Create a matrix that will project the desired shadow. */ private void shadowMatrix( float m[], // 作成する行列のポインタ float plane[], // 射影する表面の平面方程式の係数 float light[]) // 光源の同時座標値 { float dot; // Find dot product between light position vector and ground plane normal. dot = plane[0] * light[0] + plane[1] * light[1] + plane[2] * light[2] + plane[3] * light[3]; m[0] = dot - light[0] * plane[0]; m[4] = 0.f - light[0] * plane[1]; m[8] = 0.f - light[0] * plane[2]; m[12] = 0.f - light[0] * plane[3]; m[1] = 0.f - light[1] * plane[0]; m[5] = dot - light[1] * plane[1]; m[9] = 0.f - light[1] * plane[2]; m[13] = 0.f - light[1] * plane[3]; m[2] = 0.f - light[2] * plane[0]; m[6] = 0.f - light[2] * plane[1]; m[10] = dot - light[2] * plane[2]; m[14] = 0.f - light[2] * plane[3]; m[3] = 0.f - light[3] * plane[0]; m[7] = 0.f - light[3] * plane[1]; m[11] = 0.f - light[3] * plane[2]; m[15] = dot - light[3] * plane[3]; } /* * Find the plane equation given 3 points. */ private void findPlane( float plane[], // 作成する平面方程式の係数 float v0[], // 頂点1 float v1[], // 頂点2 float v2[]) // 頂点3 { float[] vec0 = new float[3], vec1 = new float[3]; // Need 2 vectors to find cross product. vec0[0] = v1[0] - v0[0]; vec0[1] = v1[1] - v0[1]; vec0[2] = v1[2] - v0[2]; vec1[0] = v2[0] - v0[0]; vec1[1] = v2[1] - v0[1]; vec1[2] = v2[2] - v0[2]; // find cross product to get A, B, and C of plane equation plane[0] = vec0[1] * vec1[2] - vec0[2] * vec1[1]; plane[1] = -(vec0[0] * vec1[2] - vec0[2] * vec1[0]); plane[2] = vec0[0] * vec1[1] - vec0[1] * vec1[0]; plane[3] = -(plane[0] * v0[0] + plane[1] * v0[1] + plane[2] * v0[2]); } public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} public void dispose(GLAutoDrawable drawable){} }