裏カリング影


~影を綺麗に見せる玄人的な手法~




■はじめに

nightmare さんに、影をより綺麗にする方法を教えていただきました。

まぁ、いつものように適当にファイルが入っています。

shadow.vsh頂点シェーダー。
shadow.pshピクセルシェーダー。
vs.vsh頂点シェーダー。深度テクスチャ作成用。
ps.pshピクセルシェーダー。深度テクスチャ作成用。
draw.cppメインの描画部分。
draw.h描画の各関数の定義。ひそかにカメラのソースが入ってる。
bg.cpp背景表示用。
main.h基本的な定数など。今回も出番無し。
main.cpp描画に関係しないシステム的な部分。変更が無いので、出番無し。
load.cppロード。
load.hロードのインターフェイス。
tile.bmp (床デカール)
sky.bmp (空デカール)

あと、モデルと、実行ファイル及び、プロジェクトファイルが入っています。

■改良点。

シャドウバッファを行う時は、何も手段を講じないと、本来影があたらない部分に、縞模様ができてしまいます。

これは、深度検査をする時に、ピクセル補間や深度テクスチャーの精度、テクセルサンプリングのずれによって、 ピクセル単位で深度にむらが生じてしまう現象のためで、 一番前面に出ているポリゴンの場合は、ポリゴンの深度と評価する深度の値が等しいため、顕著にその現象が現れます。
日常的に用いられる対処法は、最終的な深度検査する時にチェックするポリゴンの深度にオフセットを加え、 ポリゴンを少し前面に持ってきて、少しの深度のずれでも結果に影響しないように調整します。
この方法の欠点は、ポリゴンの深度をずらしてしまうため、本来影が出るべきところで影が出なかったりします。
特に、下手にやるとセルフシャドウが消えてしまい、シャドウバッファを用いた価値が全くなくなってしまいます。

今回の方法は、この前面に出る影が非常に出にくくなる手法です。
深度バッファを作成するときに、通常表面だけしか書かないところを、裏面だけ書くようにします。

この深度をバッファを用いて深度を比較すれば、(表面と裏面が重なっていない場合は)深度バッファの値と ポリゴンの深度が離れているので、オフセットなしに綺麗な結果が得られます。

ただ、影となる部分は深度バッファとポリゴンの深度が近くなりますので、その部分で余分なゴミが出ないように調節することが必要です。

■プログラム

今回の変更点は2箇所だけです。
それは、影バッファを作るときに裏面描画に設定し、画面描画の直前に元に戻します。
また、前回掛けていたポリゴン描画時の比較のオフセットを小さくしました(当社比100分の1)。

0372: //-----------------------------------------------------------------------------
0373: // Name: Render()
0374: // Desc: Draws the scene
0375: //-----------------------------------------------------------------------------
0376: VOID Render(LPDIRECT3DDEVICE8 lpD3DDev)
0377: {
0378:     D3DXMATRIX mWorld, mView, mLightView, m;
0379:     
0380:     LPDIRECT3DSURFACE8 lpZbuffer = NULL;
0381:     lpD3DDev->GetDepthStencilSurface( &lpZbuffer );
0382:     lpD3DDev->SetRenderTarget(pTextureSurface, lpZbuffer);
0383:     lpD3DDev->Clear(0,NULL,D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0),1.0f,0);
0384: 
0385:     // ビュー行列
0386:     D3DXVECTOR3 l_eye    = 30.0f*lightDir;
0387:     D3DXVECTOR3 l_lookAt = D3DXVECTOR3(0.0f,  0.0f,  0.0f);
0388:     D3DXVECTOR3 l_up     = D3DXVECTOR3(0.0f,  1.0f,  0.0f);
0389:     // 通常表示
0390:     D3DXMatrixLookAtLH(&mLightView, &l_eye, &l_lookAt, &l_up);
0391: 
0392:     // z値を0.0fから1.0fに補正する定数
0393:     lpD3DDev->SetVertexShaderConstant(15, &D3DXVECTOR4(3.0f/(z_max-z_min), -0.45f*z_max/(z_max-z_min), 0.0f, 0.0f), 1);
0394:     
0395:     lpD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE);
0396:     lpD3DDev->SetVertexShader(hVertexShader);
0397:     lpD3DDev->SetPixelShader(hPixelShader);
0398: 
0399:     lpD3DDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
0400:     DrawModel(lpD3DDev, mLightView, mLightView, 0);
0401: 
0402:     lpD3DDev->SetRenderTarget(pBackbuffer, lpZbuffer );                 // 描画をバックバッファに戻す
0403:     lpD3DDev->Clear(0,NULL,D3DCLEAR_ZBUFFER, 0,1.0f,0);
0404:     lpD3DDev->SetVertexShader(hShadowVertexShader); 
0405:     lpD3DDev->SetPixelShader(hShadowPixelShader);   
0406: 
0407:     lpD3DDev->SetTexture( 1, pTexture );                    // 元テクスチャー
0408:     
0409:     // ビュー行列
0410:     D3DXVECTOR3 eye    = D3DXVECTOR3(0.0f,1.4f*MeshRadius,2.5f*MeshRadius);
0411:     D3DXVECTOR3 lookAt = D3DXVECTOR3(0.0f,  0.0f,  0.0f);
0412:     D3DXVECTOR3 up     = D3DXVECTOR3(0.0f,  1.0f,  0.0f);
0413: 
0414:     lpD3DDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
0415:     // 通常表示
0416:     D3DXMatrixLookAtLH(&mView, &eye, &lookAt, &up);
0417:     DrawModel(lpD3DDev, mView, mLightView, 1);

あと、邪魔なので背景の描画も止めました。

■結果

セルフシャドウがきちんと出るなど、前回よりも良好な結果が得られました。
ただ、部分的におかしな部分が見られました。

カリングを変えないときと同様、斜めの部分での縞は起きてしまうので、この部分に関しては何らかの対処が必要だと思います。

■最後に

nightmare さん、ありがとうございました。
もう、最初のときは全然見当違いのことを言ってしまい、お恥ずかしい限りです。
今回の理解で正しいですか?
また、何かありましたら、よろしくお願いいたします。

他の皆さんも、何かありましたら、よろしくお願いいたします。
できることなら挑戦していきたいと思います。

影をやるなら Hardware Shadow Mapping をしなくちゃ駄目ですか?




もどる

imagire@gmail.com