その7
第7章:Simple3D.hlslで深度比較して影を出す
この章でやること
Simple3D.hlsl のピクセルシェーダーで、シャドウマップを読んで影を判定します。
この章で初めて画面に影が出ます。 ただし影のエッジはジャギー(ギザギザ)になります。 なめらかにするのは第8章で行います。
この章でのコード変更点
| ファイル | 変更内容 |
|---|---|
| Stage.cpp | メインパスの前後にシャドウマップSRVをセット/解除 |
| Simple3D.hlsl | g_shadowMap・g_shadowSampler の宣言を追加 |
| Simple3D.hlsl | cbuffer gStage に matLightVP を追加 |
| Simple3D.hlsl | PS() に影判定コードを追加 |
1 Stage.cpp:シャドウマップSRVをセットする
ここでは何をするか
メインパスの描画前に、第3章で作ったシャドウマップのテクスチャをピクセルシェーダーの t1 に渡します。 描画後は必ず解除します。
なぜ解除が必要か
解除しないと次のフレームで同じテクスチャを書き込み用(DSV)と読み込み用(SRV)の両方にバインドしようとして DirectX が警告を出し、正常に動作しなくなります。
Before(Stage.cpp の Draw() メインパス部分)
Model::Draw(hball_);
Model::Draw(hRoom_);
Model::Draw(hDonut_);
After
// シャドウマップを t1 にセット
ID3D11ShaderResourceView* pShadowSRV = Direct3D::GetShadowMapSRV();
Direct3D::pContext->PSSetShaderResources(1, 1, &pShadowSRV);
Model::Draw(hball_);
Model::Draw(hRoom_);
Model::Draw(hDonut_);
// 描画後は必ず解除する
ID3D11ShaderResourceView* nullSRV = nullptr;
Direct3D::pContext->PSSetShaderResources(1, 1, &nullSRV);
2 Simple3D.hlsl:宣言を追加する
ここでは何をするか
シャドウマップを読むためのテクスチャ変数とサンプラーを宣言します。 あわせて cbuffer gStage に matLightVP を追加します。 C++ 側は第6章ですでに送っています。シェーダー側の受け口を追加するだけです。
Before(宣言部)
Texture2D g_texture : register(t0);
SamplerState g_sampler : register(s0);
After
Texture2D g_texture : register(t0);
SamplerState g_sampler : register(s0);
Texture2D g_shadowMap : register(t1);
SamplerState g_shadowSampler : register(s1);
Before(cbuffer gStage)
cbuffer gStage : register(b1)
{
float4 lightPosition;
float4 eyePosition;
int lightType;
float3 _pad;
};
After
cbuffer gStage : register(b1)
{
float4 lightPosition;
float4 eyePosition;
int lightType;
float3 _pad;
row_major float4x4 matLightVP;
};
3 Simple3D.hlsl:PS() に影判定を追加する
ここでは何をするか
今描いているピクセルが「ライトから見えているか(明るい)」 「見えていないか(影)」を判定して、色に掛けます。
判定の手順
- ワールド座標をライト視点のクリップ座標に変換する
- クリップ座標をシャドウマップのUV座標に変換する
- シャドウマップから「ライトに一番近いZ値」を読む
- 今のピクセルのZ値と比べる 今のピクセルの方が奥 → 手前に何かある → 影
After(PS() の return color の直前に追加)
float shadow = 1.0;
float4 lightClipPos = mul(inData.wpos, matLightVP);
float2 shadowUV;
shadowUV.x = lightClipPos.x / lightClipPos.w * 0.5 + 0.5;
shadowUV.y = -lightClipPos.y / lightClipPos.w * 0.5 + 0.5;
if (shadowUV.x >= 0.0 && shadowUV.x <= 1.0 &&
shadowUV.y >= 0.0 && shadowUV.y <= 1.0)
{
float currentDepth = lightClipPos.z / lightClipPos.w;
float bias = 0.015;
float shadowDepth = g_shadowMap.Sample(g_shadowSampler, shadowUV).r;
shadow = (currentDepth - bias <= shadowDepth) ? 1.0 : 0.0;
}
color *= (0.3 + 0.7 * shadow);
bias(バイアス)とは
シャドウマップのZ値には浮動小数点の誤差があります。 バイアスなしだと、自分自身の表面が「自分より奥にある」と誤判定されて ノイズのような影が全面に出ます(セルフシャドウ)。 bias = 0.015 は「少しだけ手前のものは影にしない」という調整値です。
ビルド確認
- ビルドが通る
- ドーナツが床や部屋に影を落とす
- WASD / 上下キーでライト方向を変えると影も動く
- 影のエッジがジャギー(ギザギザ)になる → 第8章で改善する
No Comments