// // コンピュータグラフィックス特論II // 影の描画のサンプルプログラム // // Windows ヘッダファイルのインクルード #include // GLUTヘッダファイルのインクルード #include // ヘッダファイルのインクルード #include "Obj.h" #include "bitmap.h" // 視点操作のためのグローバル変数 // ウィンドウのサイズ int win_width, win_height; // カメラの回転のための変数 float camera_yaw = 15.0f; // 30.0; // Y軸を中心とする回転角度 float camera_pitch = -20.0f; // -30.0; // X軸を中心とする回転角度 float camera_distance = 15.0; // 中心からカメラの距離 // マウスのドラッグのための変数 int drag_mouse_r = 0; // 右ボタンがドラッグ中かどうかのフラグ(1:ドラッグ中, 0:非ドラッグ中) int drag_mouse_l = 0; // 左ボタンがドラッグ中かどうかのフラグ(1:ドラッグ中, 0:非ドラッグ中) int drag_mouse_m = 0; // 中ボタンがドラッグ中かどうかのフラグ(1:ドラッグ中, 0:非ドラッグ中) int last_mouse_x, last_mouse_y; // 最後に記録されたマウスカーソルの座標 // 物体の回転アニメーションのための変数 bool on_animation = true; // 影の描画に関するグローバル変数 // 影の描画方法 enum ShadowModeEnum { SHADOW_NONE, SHADOW_TEXTURE, SHADOW_PROJECTION, SHADOW_VOLUME, NUM_SHADOW_MODE }; // 現在の影の描画方法 ShadowModeEnum shadow_mode = SHADOW_TEXTURE; // SHADOW_PROJECTION; // 影の描画方法の名前 const char * shadow_mode_name[ NUM_SHADOW_MODE ] = { "No Shadow", "Texture Shadow", "Polygon Projection Shadow", "Shadow Volume" }; // 点光源の位置(影の投影方向) Vector light_pos; // 影のテクスチャ画像の番号 unsigned int shadow_texture = 0; // ポリゴン投影による影の描画色 float shadow_color_rgb = 0.2f; float shadow_color_alpha = 0.5f; // 影の描画の設定(ブレンディングやステンシルバッファの使用の有無の切り替え) bool shadow_blend_off = false; bool shadow_stencil_off = false; // 幾何形状オブジェクトに関するグローバル変数 // 幾何形状オブジェクトの数 #define NUM_OBJECTS 2 // 幾何形状オブジェクト Obj * object[ NUM_OBJECTS ] = { NULL, NULL }; // 位置 Vector object_pos[ NUM_OBJECTS]; // 水平向き float object_ori[ NUM_OBJECTS ]; // 大きさ(テクスチャマッピングによる影の描画用) Vector object_size[ NUM_OBJECTS ]; // 描画フラグ bool object_display[ NUM_OBJECTS ] = { true, true }; // アニメーションフラグ bool object_animation[ NUM_OBJECTS ] = { false, true }; /////////////////////////////////////////////////////////////////////////////// // // テクスチャマッピングによる影の描画 // // // 影のテクスチャ画像の読み込み・設定 // bool LoadShadowTexture( const char * filename = NULL ) { // デフォルトのテクスチャ画像のファイル名 static const char * default_filename = "shadow.bmp"; // 読み込みに失敗したかどうかのフラグ static bool try_default_file = false; // ファイル名が省略されたらデフォルトの画像ファイルを使用 if ( filename == NULL ) { // 既にデフォルトの画像ファイルの読み込みに失敗していれば終了 if ( try_default_file ) return false; // デフォルトの画像ファイル名を設定 filename = default_filename; } // テクスチャ画像の読み込み int result; unsigned char * shadow_image = NULL; int shadow_width, shadow_height; result = loadBitmap( filename, &shadow_image, &shadow_width, &shadow_height ); // 読み込みに失敗したら終了 if ( result != 0 ) { if ( filename == default_filename ) try_default_file = true; return false; } // テクスチャマッピングの設定 glGenTextures( 1, &shadow_texture ); glBindTexture( GL_TEXTURE_2D, shadow_texture ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, shadow_width, shadow_height, 0, GL_RGB, GL_UNSIGNED_BYTE, shadow_image ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); return true; } // // テクスチャマッピングによる影の描画 // void RenderTextureShadow( float obj_matrix[ 16 ], float size_x, float size_z, float shadow_y ) { // テクスチャ画像の読み込みと設定 // テクスチャ画像が読み込まれていなければ、最初にデフォルトの画像を読み込み if ( shadow_texture == 0 ) { // 読み込みに失敗したら終了 if ( ! LoadShadowTexture() ) return; } // 影のテクスチャ画像を描画する四隅の水平位置+高さ float x0, z0, x1, z1, x2, z2, x3, z3, y; // ※レポート課題 // 現在の描画設定を取得(描画終了後に元の設定に戻すため) GLboolean b_texture, b_blend, b_lighting; glGetBooleanv( GL_TEXTURE_2D, &b_texture ); glGetBooleanv( GL_BLEND, &b_blend ); glGetBooleanv( GL_LIGHTING, &b_lighting ); // 描画設定の変更 glDisable( GL_LIGHTING ); glEnable( GL_BLEND ); glEnable( GL_TEXTURE_2D ); // 動作確認のための描画設定の変更 if ( shadow_blend_off ) glDisable( GL_BLEND ); // テクスチャマッピングの設定 glBindTexture( GL_TEXTURE_2D, shadow_texture ); glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ); // ※レポート課題 // 描画設定を復元 if ( b_lighting ) glEnable( GL_LIGHTING ); if ( !b_blend ) glDisable( GL_BLEND ); if ( !b_texture ) glDisable( GL_TEXTURE_2D ); } /////////////////////////////////////////////////////////////////////////////// // // ポリゴン投影による影の描画 // // // 幾何形状モデル(Obj形状)の描画(固定色で描画) // void RenderObjUnicolor( const Obj * obj, float color_r, float color_g, float color_b, float color_a ) { // ※レポート課題 } // // ポリゴン投影による影の描画 // void RenderProjectionShadow( const Obj * obj, const float obj_matrix[ 16 ], const Vector & light_dir, float color_r, float color_g, float color_b, float color_a ) { // 現在の描画設定を取得(描画終了後に元の設定に戻すため) GLboolean b_cull_face, b_blend, b_lighting, b_stencil; glGetBooleanv( GL_CULL_FACE, &b_cull_face ); glGetBooleanv( GL_BLEND, &b_blend ); glGetBooleanv( GL_LIGHTING, &b_lighting ); glGetBooleanv( GL_STENCIL_TEST, &b_stencil ); // 描画設定の変更 if ( b_lighting ) glDisable( GL_LIGHTING ); if ( !b_cull_face ) glEnable( GL_CULL_FACE ); if ( !b_blend ) glEnable( GL_BLEND ); // ブレンディングの設定 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); // ステンシルバッファの設定 // ※レポート課題 // 動作確認のための描画設定の変更 if ( shadow_blend_off ) glDisable( GL_BLEND ); if ( shadow_stencil_off ) glDisable( GL_STENCIL_TEST ); // 現在の変換行列を一時保存 glPushMatrix(); // ポリゴンモデルを地面に投影して描画するための変換行列を設定 //(この時点で、ワールド座標系からカメラ座標系への変換行列が設定されているものとする) // ※レポート課題 // 影の描画、幾何形状モデルを指定色で描画 RenderObjUnicolor( obj, color_r, color_g, color_b, color_a ); // 一時保存しておいた変換行列を復元 glPopMatrix(); // 描画設定を復元 if ( b_lighting ) glEnable( GL_LIGHTING ); if ( b_cull_face ) glEnable( GL_CULL_FACE ); if ( !b_blend ) glDisable( GL_BLEND ); if ( !b_stencil ) glDisable( GL_STENCIL_TEST ); } /////////////////////////////////////////////////////////////////////////////// // // シャドウ・ヴォリュームによる影の描画 // // // シャドウ・ヴォリュームを描画 // void DrawShadowVolume( const Obj * obj, const float model_world[ 16 ], const Vector & light_vec ) { // 省略 } // // シャドウ・ヴォリュームを塗りつぶす // void FillShadowVolume( float color_r, float color_g, float color_b, float color_a ) { // 省略 } /////////////////////////////////////////////////////////////////////////////// // // 以下、プログラムのメイン処理 // // // 幾何形状オブジェクトの読み込み・初期化 // void InitObjects() { // 幾何形状オブジェクトの読み込み if ( !object[ 0 ] ) { object[ 0 ] = LoadObj( "Car.obj" ); if ( object[ 0 ] ) { ScaleObj( object[ 0 ], 5.0f, &object_size[ 0 ].x, &object_size[ 0 ].y, &object_size[ 0 ].z ); object_size[ 0 ].x *= 1.5; object_size[ 0 ].z *= 1.5; } } if ( !object[ 1 ] ) { object[ 1 ] = LoadObj( "Pyramid.obj" ); if ( object[ 1 ] ) { ScaleObj( object[ 1 ], 2.0f, &object_size[ 1 ].x, &object_size[ 1 ].y, &object_size[ 1 ].z ); object_size[ 1 ].x *= 1.5; object_size[ 1 ].z *= 1.5; } } // 幾何形状オブジェクトの初期位置・向きの設定 object_pos[ 0 ].x = 0.0f; object_pos[ 0 ].y = 2.0f; object_pos[ 0 ].z = 0.0f; object_ori[ 0 ] = 180.0f; object_pos[ 1 ].x = 2.5f; object_pos[ 1 ].y = 4.0f; object_pos[ 1 ].z = 1.5f; object_ori[ 1 ] = 0.0f; } // // 格子模様の床を描画 // void DrawFloor( float tile_size, int num_x, int num_z, float r0, float g0, float b0, float r1, float g1, float b1 ) { int x, z; float ox, oz; glBegin( GL_QUADS ); glNormal3d( 0.0, 1.0, 0.0 ); ox = - ( num_x * tile_size ) / 2; for ( x=0; x 360.0 ) camera_yaw -= 360.0; // マウスの縦移動に応じてX軸を中心に回転 camera_pitch -= ( my - last_mouse_y ) * 1.0; if ( camera_pitch < -90.0 ) camera_pitch = -90.0; else if ( camera_pitch > 90.0 ) camera_pitch = 90.0; } // 中ボタン(もしくは ctrlキーを押しながら右ボタン)のドラッグ中は視点とカメラの距離を変更する if ( drag_mouse_m || ( drag_mouse_r && ( glutGetModifiers() & GLUT_ACTIVE_CTRL ) ) ) { // 前回のマウス座標と今回のマウス座標の差に応じて視点を回転 // マウスの縦移動に応じて距離を移動 camera_distance += ( my - last_mouse_y ) * 0.2; if ( camera_distance < 5.0 ) camera_distance = 5.0; } // 左ボタンのドラッグ中は影の方向を変更する if ( drag_mouse_l ) { // 前回のマウス座標と今回のマウス座標の差に応じて影の方向を変更 // マウスの縦移動に応じて距離を移動 float delta_x = ( mx - last_mouse_x ) * 0.05f; float delta_z = ( my - last_mouse_y ) * 0.05f; light_pos.x += delta_x; if ( light_pos.x < -5.0f ) light_pos.x = -5.0f; else if ( light_pos.x > 5.0f ) light_pos.x = 5.0f; light_pos.z += delta_z; if ( light_pos.z < -5.0f ) light_pos.z = -5.0f; else if ( light_pos.z > 5.0f ) light_pos.z = 5.0f; } // 今回のマウス座標を記録 last_mouse_x = mx; last_mouse_y = my; // 再描画の指示を出す(この後で再描画のコールバック関数が呼ばれる) glutPostRedisplay(); } // // キーボードのキーが押されたときに呼ばれるコールバック関数 // void KeyboardCallback( unsigned char key, int mx, int my ) { // mキーで影の描画モードを変更 if ( key == 'm' ) { shadow_mode = (ShadowModeEnum)( ( shadow_mode + 1 ) % NUM_SHADOW_MODE ); glutPostRedisplay(); } // oキーでオブジェクトの描画を変更 if ( key == 'o' ) { if ( object_display[ 0 ] && object_display[ 1 ] ) { object_display[ 0 ] = false; } else if ( !object_display[ 0 ] && object_display[ 1 ] ) { object_display[ 0 ] = true; object_display[ 1 ] = false; } else { object_display[ 1 ] = true; } } // sキーでオブジェクトのアニメーションを変更 if ( key == 's' ) { if ( object_animation[ 0 ] && object_animation[ 1 ] ) { object_animation[ 0 ] = false; object_animation[ 1 ] = false; } else if ( !object_animation[ 0 ] && object_animation[ 1 ] ) { object_animation[ 0 ] = true; object_animation[ 1 ] = true; } else { object_animation[ 0 ] = false; object_animation[ 1 ] = true; } on_animation = ( object_animation[ 0 ] && object_animation[ 1 ] ); } // dキーでデバッグモードを変更 if ( key == 'd' ) { if ( !shadow_blend_off && !shadow_stencil_off ) { shadow_blend_off = true; } else if ( shadow_blend_off && !shadow_stencil_off ) { shadow_blend_off = false; shadow_stencil_off = true; } else { shadow_blend_off = false; shadow_stencil_off = false; } } // 再描画の指示を出す(この後で再描画のコールバック関数が呼ばれる) glutPostRedisplay(); } // // アイドル時に呼ばれるコールバック関数 // void IdleCallback( void ) { if ( on_animation ) { #ifdef WIN32 // システム時間を取得し、前回からの経過時間に応じてΔtを決定 static DWORD last_time = 0; DWORD curr_time = timeGetTime(); float delta = ( curr_time - last_time ) * 0.001f; if ( delta > 0.01f ) delta = 0.01f; last_time = curr_time; #else // 固定のΔtを使用 delta = 0.01f; #endif // オブジェクトを回転 for ( int i=0; i 360.0f ) object_ori[ i ] -= 360.0f; if ( object_ori[ i ] < 0.0f ) object_ori[ i ] += 360.0f; } // 再描画の指示を出す(この後で再描画のコールバック関数が呼ばれる) glutPostRedisplay(); } } // // 環境初期化関数 // void InitEnvironment() { // 光源を作成する float light0_position[] = { 10.0, 10.0, 10.0, 1.0 }; float light0_diffuse[] = { 0.8, 0.8, 0.8, 1.0 }; float light0_specular[] = { 1.0, 1.0, 1.0, 1.0 }; float light0_ambient[] = { 0.1, 0.1, 0.1, 1.0 }; glLightfv( GL_LIGHT0, GL_POSITION, light0_position ); glLightfv( GL_LIGHT0, GL_DIFFUSE, light0_diffuse ); glLightfv( GL_LIGHT0, GL_SPECULAR, light0_specular ); glLightfv( GL_LIGHT0, GL_AMBIENT, light0_ambient ); glEnable( GL_LIGHT0 ); // 光源計算を有効にする glEnable( GL_LIGHTING ); // 物体の色情報を有効にする glEnable( GL_COLOR_MATERIAL ); // Zテストを有効にする glEnable( GL_DEPTH_TEST ); // 背面除去を有効にする glCullFace( GL_BACK ); glEnable( GL_CULL_FACE ); // 背景色を設定 glClearColor( 0.5, 0.5, 0.8, 0.0 ); // 光源位置を初期化 light_pos.x = 4.0f; light_pos.y = 5.0f; light_pos.z = 1.0f; } // // メイン関数(プログラムはここから開始) // int main( int argc, char ** argv ) { // GLUTの初期化 glutInit( &argc, argv ); glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GLUT_STENCIL ); glutInitWindowSize( 640, 640 ); glutInitWindowPosition( 0, 0 ); glutCreateWindow("Shadow Sample"); // コールバック関数の登録 glutDisplayFunc( DisplayCallback ); glutReshapeFunc( ReshapeCallback ); glutMouseFunc( MouseClickCallback ); glutMotionFunc( MouseDragCallback ); glutKeyboardFunc( KeyboardCallback ); glutIdleFunc( IdleCallback ); // 環境初期化 InitEnvironment(); // オブジェクトの読み込み・初期化 InitObjects(); // GLUTのメインループに処理を移す glutMainLoop(); return 0; }