-簡易的なシャドウマッピング-
-簡易的なシャドウマッピング-

ここでいう簡易的なシャドウマッピングとは、
「ある平面(地面)に対して平行な影を生成する」
というシャドウマッピングを指しています。

現実的には物に影が映り込んだりしますが、ここでは考えません。
結果は以下のようになります。




以下のようにして作成しています。


-------------------------------------
//ステンシルバッファのクリアと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] ステンシルバッファ




inserted by FC2 system