影付きレンダリングの仕方

このページでは影の表示の仕方を説明します。
「陰」というと光の当たらない面が暗くなっている所をさしますが
「影」とは光源と物体の間に遮蔽物があり、物体が遮蔽物で隠されている部分が暗くなるところなどをさします。

影を描画するための原理はここでは詳しくは説明しません。
ここでは影を表示するための命令の使い方をe3dhsp3_shadow2.hspを例にとって説明します。

まずe3dhsp3_shadow2.hspを実行してみてください。


図1 サンプルの実行画面

図1のように地面にモロ星人とビルボードの木の影が落ちています。

ver5.0.2.0から影の表示にLiSPSMという技術を導入しました
ver5.0.2.0以前の影の表示はユーザーさんがパラメータを適切に設定しないとちゃんとした影は表示されませんでしたが
ver5.0.2.0からは難しいパラメータは自動で設定されます。
それでもいくつかやらないといけないことがあるのでそれを順番に説明します。


まず影を描画するには大きなレンダリング可能なテクスチャが必要です。
テクスチャにライトの位置から見たシーンのZ成分(カメラからの距離)を描画します。
このテクスチャのZ成分と通常のカメラから見たZ座標を比べて影なのか影じゃないのかを判定します。

このテクスチャにZ成分を書きだす際にテクスチャに格納可能な精度が問題になります。
精度が悪いと影の形が悪くなったり縞模様が出たりします。
ですので出来ればアルファ付きの浮動小数点フォーマット(D3DFMT_A16B16G16R16F)を使います。

e3dhsp3_shadow2.hspの中で言いますと
  E3DCheckRTFormat D3DFMT_A16B16G16R16F, fmtok
  if( fmtok != 0 ){
    rtsize = 1024
    E3DCreateRenderTargetTexture 1024, 1024, rtscid, rttexid, rtok1, D3DFMT_A16B16G16R16F
    if( rtok1 == 0 ){
      rtsize = 512
      E3DCreateRenderTargetTexture 512, 512, rtscid, rttexid, rtok2, D3DFMT_A16B16G16R16F
      if( rtok2 == 0 ){
        rtsize = 256
        E3DCreateRenderTargetTexture 256, 256, rtscid, rttexid, rtok3, D3DFMT_A16B16G16R16F
        if( rtok3 == 0 ){
          dialog "メモリ不足で実行できません"
          goto *bye
        }
      }
    }
  }else{
    rtsize = 1024
    E3DCreateRenderTargetTexture 1024, 1024, rtscid, rttexid, rtok4, D3DFMT_A8R8G8B8
    if( rtok4 == 0 ){
      rtsize = 512
      E3DCreateRenderTargetTexture 512, 512, rtscid, rttexid, rtok5, D3DFMT_A8R8G8B8
      if( rtok5 == 0 ){
        rtsize = 256
        E3DCreateRenderTargetTexture 256, 256, rtscid, rttexid, rtok6, D3DFMT_A8R8G8B8
        if( rtok6 == 0 ){
          dialog "メモリ不足で実行できません"
          goto *bye
        }
      }
    }
  }

の部分がそうです。
E3DCheckRTFormatで浮動小数点フォーマットが可能かどうかを調べ可能な場合はD3DFMT_A16B16G16R16Fで
可能でない場合はD3DFMT_A8R8G8B8で作成します。

浮動小数点フォーマットの方が影の精度がよくなりますが、ビデオカードによっては問題が生じる場合があります。
ビルボードの影が四角くなってしまうことがあるのです。
この現象はGeForce FX5200で確認されたという報告がありました。
ですので精度は悪くなりますが、どのビデオカードでもビルボードの影をちゃんと出したい場合には
D3DFMT_A8R8G8B8でレンダーターゲットテクスチャを作るのが無難です。

レンダーターゲットテクスチャの大きさは大きい方が影の形が良くなりますが
環境によってはメモリ不足になることがあるので、必ずE3DCreateRenderTargetTextureのokflagの値を見て
成功しているかチェックし、失敗した場合は小さいサイズのテクスチャを作成し直します。

実行画面の左上に描画しているのがこのレンダーターゲットテクスチャの中身です。


E3DSetShadowBias ではZバッファの誤差の軽減のためのバイアス値の設定をします。
浮動小数点のレンダーターゲットテクスチャが作れない場合などは特にですが
Zバッファの誤差の影響で画面に縞模様が出たり、ごみのような黒いものが表示されたりします。
バイアスの値を大きく設定するとこの誤差による現象が軽減されます。
ただしあまり大きい値を設定すると、今度は影の形が変わってしまいます。

E3DSetShadowDarknessでは影の暗さを設定します。
指定した値がアンビエント色に乗算されて影の色になります。
通常1.0でいいと思います。1.0のときは影の色と陰の色が同じになります。

E3DSetShadowMapLightDirで影用の平行光源の向きを設定します。
地面に影を落とすためには向きのY成分はマイナスにする必要があります。


ver5.0.2.0以前の影の表示に必要だったE3DSetShadowMapCameraとE3DSetShadowMapProjOrthoは必要なくなりました。


ここまでがテクスチャに描画する準備です。

次はいよいよシーンの描画をします。
e3dhsp3_shadow2.hspでは

arhsid.0 = hsid0
arhsid.1 = hsid4
arhsid.2 = -1
E3DRenderWithShadow scid1, rtscid, rttexid, arhsid, 3

の行でレンダリングしています。
arhsidの配列に描画したいhsidを格納します。-1はビルボードです。
E3DRenderWithShadowでは内部でE3DChkInView、E3DBeginScene、E3DEndSceneも呼ばれます。
ピクセルシェーダー2.0がない環境では影は表示できないので、そのときは通常の影なしシーンを描画するようになっています。

E3DRenderWithShadowではスプライトやテキストは描画できません。
スプライトやテキストを描画したいときは
E3DRenderWithShadow に続けてE3DBeginSceneをskipflag 1で呼び出して描画を追加します。

例えば
E3DBeginScene scid1, 1
  E3DBeginSprite
    spsc = 120.0 / double( rtsize )
    E3DRenderSprite spid1, spsc, spsc, 0, 0, 0.0
  E3DEndSprite
  gosub *DrawText
E3DEndScene

のようになります。


LiSPSMの導入により、影付きシーンの視野内にあるオブジェクトがシャドウマップに含まれるように自動的に調整が行われます。
この際、地面のオブジェクトはシャドウマップに含まれるようには設定されません。地面以外のオブジェクト(ビルボードを含む)の
合計最大1000個までのパーツがシャドウマップに含まれるように設定されます。



簡単ですが以上で説明は終わりです。



トップページに戻る