-簡易的なシャドウマッピング- |
-簡易的なシャドウマッピング- ここでいう簡易的なシャドウマッピングとは、 「ある平面(地面)に対して平行な影を生成する」 というシャドウマッピングを指しています。 現実的には物に影が映り込んだりしますが、ここでは考えません。 結果は以下のようになります。 以下のようにして作成しています。 ------------------------------------- //ステンシルバッファのクリアと0埋め glClear(GL_STENCIL_BUFFER_BIT); //ステンシルバッファをすべて0で初期化 glClearStencil(0); //ステンシルを有効に glEnable(GL_STENCIL_TEST); //描画する物体のステンシル値にすべて1をつける glStencilFunc(GL_ALWAYS, 1, ~0); //ステンシル評価方法設定(ステンシルテスト失敗時の動作, Zバッファテストで失敗時の動作, 成功の時の動作) glStencilOp(GL_REPLACE,GL_REPLACE,GL_REPLACE); //カラーマスク、デプスマスクをセットし、ピクセルの色が変更されないようにする glColorMask(0,0,0,0); glDepthMask(0); //影行列を作成。shadowMat(長さ16個の配列)へ保存される(この関数は自作) MyCalcShadowMat(lightPosShadow, (mJointsIndex.begin())->second->position, shadowMat); //マトリクスの乗算 glMultMatrixf(shadowMat); glPushMatrix(); …(ここで物体を描画)… glPopMatrix(); //マスクを元通りにする glColorMask(1, 1, 1, 1); glDepthMask(GL_TRUE); //ステンシル値が上書きされないように設定 glStencilOp(GL_KEEP, GL_KEEP ,GL_KEEP); //ステンシル値が1のピクセルにのみレンダリング glStencilFunc( GL_EQUAL, 1, ~0); //ブレンド処理を有効化 glEnable(GL_BLEND); //半透明な影を作るために次のように指定する glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //影の色を初期化 glColor3f(0.0, 0.0, 0.0); //ZX平面に大きな四角形(地面と同じくらい)を書く。ブレンド処理によって半透明になる glBegin(GL_QUADS); glVertex3f( 5.0, 0.0, 5.0); glVertex3f(-5.0, 0.0, 5.0); glVertex3f(-5.0, 0.0, -5.0); glVertex3f( 5.0, 0.0, -5.0); glEnd(); //ブレンド無効化 glDisable(GL_BLEND); //ステンシル無効化 glDisable(GL_STENCIL_TEST); ------------------------------------- MyCalcShadowMatのソースは以下のようになっています。 引数のVector, Vector4は自作した構造体ですので、お気になさらず。 ------------------------------------- void MyCalcShadowMat(GLfloat lightPos[], Vector basicJointPos, Vector4 floorVec, float mat[]) { float ex, ey, ez;//光源の方向 float a, b, c, d;//床の面のパラメータ float s; //object中心から光源までの距離 float x, y, z; //光源の方向ベクトル x = lightPos[0] - basicJointPos.x; y = lightPos[1] - basicJointPos.y; z = lightPos[2] - basicJointPos.z; //光源の方向ベクトルを単位ベクトル化 s = sqrt(x * x + y * y + z * z); ex = x / s; ey = y / s; ez = z / s; //フロアの方向ベクトル(y軸方向)を作成 a = floorVec.x; b = floorVec.y; c = floorVec.z; d = floorVec.w; //フロアと影の干渉を防ぐための値(-0.01とする) //shadow matrixの公式に当てはめる mat[0] = b * ey + c * ez; mat[1] = -a * ey; mat[2] = -a * ez; mat[3] = 0.0; mat[4] = -b * ex; mat[5] = a * ex + c * ez; mat[6] = -b * ez; mat[7] = 0.0; mat[8] = -c * ex; mat[9] = -c * ey; mat[10] = a * ex + b * ey; mat[11] = 0.0; mat[12] = -d * ex; mat[13] = -d * ey; mat[14] = -d * ez; mat[15] = a * ex + b * ey + c * ez; } ------------------------------------- 描画するオブジェクトのステンシルを"1"になるように設定し、 そこへ大きな四角形を描画すると、ステンシルが"1"のところのみ描画されます。 このようにすれば、キレイな1枚影を得ることができます。 ステンシルを使わず、影行列だけを使った場合は以下のような結果となります。 凹凸のあるオブジェクトの場合、重なった部分の影が濃くなってしまいます。 これを解決するためにステンシルを使用しています。 ステンシルをwgl環境で使う場合の注意として、 PIXELFORMATDESCRIPTORの初期化の時に、 「ステンシルバッファのビット数」に「1」を指定するのを忘れずに。 参考サイト: sonson@Picture&Software [OpenGL] ステンシルバッファ |