Microsoft Visual 2017 Community C++
 Direct3D 11.0( SM 5.0 )
 DirectXTex texture processing library
 XMLLite
 DirectXMath

■Direct3D11 Variance Shadow Maps Prev Top Next
関連ページ:Direct3D11 自作ライブラリ


今回はVariance Shadow Mapsをやります。細かい説明はここでは省略しますので、3Dグラフィックス・マニアックスを参照してください。


@Direct3D 自作の共通ライブラリ一式をここを参照して作成してください。

Aシェーダー系で使用するソースです。
GaussianFilter.hlsl ガウスフィルターのhlslファイル
GaussianFilter.h ガウスフィルタークラスのヘッダーファイル
GaussianFilter.cpp ガウスフィルタークラスのソースファイル
VarianceShadowMaps_Pass0.hlsl VarianceShadowMapsのPass0のhlslファイル
VarianceShadowMaps_Pass1.hlsl VarianceShadowMapsのPass1のhlslファイル
VarianceShadowMaps.h VarianceShadowMapsクラスのヘッダーファイル
VarianceShadowMaps.cpp VarianceShadowMapsクラスのソースファイル

B個別に使用するソースです。
F14Mesh.h F14メッシュクラスのヘッダーファイル
F14Mesh.cpp F14メッシュクラスのソースファイル
PlaneMesh.h 地面メッシュクラスのヘッダーファイル
PlaneMesh.cpp 地面メッシュクラスのソースファイル
main.cpp main関数のソースファイル


---GaussianFilter.hlsl---  ↑

// ************************************************************
// GaussianFilter Pass0
// ************************************************************

// 定数バッファ0
cbuffer cbBuffer0 : register( b0 )
{
   float  g_Weight0 : packoffset( c0.x );    // 重み
   float  g_Weight1 : packoffset( c0.y );    // 重み
   float  g_Weight2 : packoffset( c0.z );    // 重み
   float  g_Weight3 : packoffset( c0.w );    // 重み
   float  g_Weight4 : packoffset( c1.x );    // 重み
   float  g_Weight5 : packoffset( c1.y );    // 重み
   float  g_Weight6 : packoffset( c1.z );    // 重み
   float  g_Weight7 : packoffset( c1.w );    // 重み
   float  g_Width   : packoffset( c2.x );    // サーフェスの横サイズ
   float  g_Height  : packoffset( c2.y );    // サーフェスの縦サイズ
   float  g_OffsetX : packoffset( c2.z );
   float  g_OffsetY : packoffset( c2.w );
};

// テクスチャー
Texture2D g_Tex : register( t0 );

// サンプラーステート
SamplerState  g_Sampler;

// 頂点シェーダーの入力パラメータ
struct VS_IN
{
   float3 pos    : POSITION;   // 頂点座標
   float2 texel  : TEXCOORD;
};

// 頂点シェーダーの出力パラメータ
struct VS_OUT_PS_IN
{
   float4 pos    : SV_POSITION;
   float2 texel0 : TEXCOORD0;   // テクセル
   float2 texel1 : TEXCOORD1;   // テクセル
   float2 texel2 : TEXCOORD2;   // テクセル
   float2 texel3 : TEXCOORD3;   // テクセル
   float2 texel4 : TEXCOORD4;   // テクセル
   float2 texel5 : TEXCOORD5;   // テクセル
   float2 texel6 : TEXCOORD6;   // テクセル
   float2 texel7 : TEXCOORD7;   // テクセル
};

// X方向にブラーを適用する頂点シェーダー
VS_OUT_PS_IN GaussianFilter_Pass0_VS_Main( VS_IN In )
{
   VS_OUT_PS_IN Out;

   Out.pos = float4( In.pos, 1.0f );
   Out.texel0 = In.texel + float2( -1.0f  / g_Width, 0.0f );
   Out.texel1 = In.texel + float2( -3.0f  / g_Width, 0.0f );
   Out.texel2 = In.texel + float2( -5.0f  / g_Width, 0.0f );
   Out.texel3 = In.texel + float2( -7.0f  / g_Width, 0.0f );
   Out.texel4 = In.texel + float2( -9.0f  / g_Width, 0.0f );
   Out.texel5 = In.texel + float2( -11.0f / g_Width, 0.0f );
   Out.texel6 = In.texel + float2( -13.0f / g_Width, 0.0f );
   Out.texel7 = In.texel + float2( -15.0f / g_Width, 0.0f );
   return Out;
}

// X方向にブラーを適用するピクセルシェーダ
float4 GaussianFilter_Pass0_PS_Main( VS_OUT_PS_IN In ) : SV_TARGET
{
   float4 Out = 0.0f;
   
   Out += ( g_Tex.Sample( g_Sampler, In.texel0 ) + g_Tex.Sample( g_Sampler, float2( In.texel7.x + g_OffsetX, In.texel7.y ) ) ) * g_Weight0;
   Out += ( g_Tex.Sample( g_Sampler, In.texel1 ) + g_Tex.Sample( g_Sampler, float2( In.texel6.x + g_OffsetX, In.texel6.y ) ) ) * g_Weight1;
   Out += ( g_Tex.Sample( g_Sampler, In.texel2 ) + g_Tex.Sample( g_Sampler, float2( In.texel5.x + g_OffsetX, In.texel5.y ) ) ) * g_Weight2;
   Out += ( g_Tex.Sample( g_Sampler, In.texel3 ) + g_Tex.Sample( g_Sampler, float2( In.texel4.x + g_OffsetX, In.texel4.y ) ) ) * g_Weight3;
   Out += ( g_Tex.Sample( g_Sampler, In.texel4 ) + g_Tex.Sample( g_Sampler, float2( In.texel3.x + g_OffsetX, In.texel3.y ) ) ) * g_Weight4;
   Out += ( g_Tex.Sample( g_Sampler, In.texel5 ) + g_Tex.Sample( g_Sampler, float2( In.texel2.x + g_OffsetX, In.texel2.y ) ) ) * g_Weight5;
   Out += ( g_Tex.Sample( g_Sampler, In.texel6 ) + g_Tex.Sample( g_Sampler, float2( In.texel1.x + g_OffsetX, In.texel1.y ) ) ) * g_Weight6;
   Out += ( g_Tex.Sample( g_Sampler, In.texel7 ) + g_Tex.Sample( g_Sampler, float2( In.texel0.x + g_OffsetX, In.texel0.y ) ) ) * g_Weight7;
   return Out;
}

// ************************************************************
// GaussianFilter Pass1
// ************************************************************

// Y方向にブラーを適用する頂点シェーダー
VS_OUT_PS_IN GaussianFilter_Pass1_VS_Main( VS_IN In )
{
   VS_OUT_PS_IN Out;

   Out.pos = float4( In.pos, 1.0f );
   Out.texel0 = In.texel + float2( 0.0f, -1.0f  / g_Height );
   Out.texel1 = In.texel + float2( 0.0f, -3.0f  / g_Height );
   Out.texel2 = In.texel + float2( 0.0f, -5.0f  / g_Height );
   Out.texel3 = In.texel + float2( 0.0f, -7.0f  / g_Height );
   Out.texel4 = In.texel + float2( 0.0f, -9.0f  / g_Height );
   Out.texel5 = In.texel + float2( 0.0f, -11.0f / g_Height );
   Out.texel6 = In.texel + float2( 0.0f, -13.0f / g_Height );
   Out.texel7 = In.texel + float2( 0.0f, -15.0f / g_Height );
   return Out;
}

// Y方向にブラーを適用するピクセルシェーダ
float4 GaussianFilter_Pass1_PS_Main( VS_OUT_PS_IN In ) : SV_TARGET
{
   float4 Out = 0.0f;

   Out += ( g_Tex.Sample( g_Sampler, In.texel0 ) + g_Tex.Sample( g_Sampler, float2( In.texel7.x, In.texel7.y + g_OffsetY ) ) ) * g_Weight0;
   Out += ( g_Tex.Sample( g_Sampler, In.texel1 ) + g_Tex.Sample( g_Sampler, float2( In.texel6.x, In.texel6.y + g_OffsetY ) ) ) * g_Weight1;
   Out += ( g_Tex.Sample( g_Sampler, In.texel2 ) + g_Tex.Sample( g_Sampler, float2( In.texel5.x, In.texel5.y + g_OffsetY ) ) ) * g_Weight2;
   Out += ( g_Tex.Sample( g_Sampler, In.texel3 ) + g_Tex.Sample( g_Sampler, float2( In.texel4.x, In.texel4.y + g_OffsetY ) ) ) * g_Weight3;
   Out += ( g_Tex.Sample( g_Sampler, In.texel4 ) + g_Tex.Sample( g_Sampler, float2( In.texel3.x, In.texel3.y + g_OffsetY ) ) ) * g_Weight4;
   Out += ( g_Tex.Sample( g_Sampler, In.texel5 ) + g_Tex.Sample( g_Sampler, float2( In.texel2.x, In.texel2.y + g_OffsetY ) ) ) * g_Weight5;
   Out += ( g_Tex.Sample( g_Sampler, In.texel6 ) + g_Tex.Sample( g_Sampler, float2( In.texel1.x, In.texel1.y + g_OffsetY ) ) ) * g_Weight6;
   Out += ( g_Tex.Sample( g_Sampler, In.texel7 ) + g_Tex.Sample( g_Sampler, float2( In.texel0.x, In.texel0.y + g_OffsetY ) ) ) * g_Weight7;
   return Out;
}

チェビチェフの不等式では深度マップの深度値の平均値を計算します。平均値の計算はブラー処理をかけて行います。
ブラー処理は何でもいいのですがここではガウスフィルターを使用しました。

---GaussianFilter.h---  ↑

#ifndef GAUSSIANFILTER_H
#define GAUSSIANFILTER_H

#include "../Common/UCommon.h"
#include "../Common/UException.h"
#include "../Common/UGraphicsPipeline.h"
#include "../Common/UDirect3D11.h"

class GaussianFilter
{
private:
   typedef struct _VERTEX
   {
      XMFLOAT3 Position;
      XMFLOAT2 Texcoord;
   }VERTEX;

   // ブラーフィルターの定数バッファ定義
   typedef struct _CBUFFER
   {
      float Weight[8];
      float Width;
      float Height;
      XMFLOAT2 Offset;
   }CBUFFER;

   // 頂点バッファ
   ID3D11Buffer* m_pVertexBuffer;

   ID3D11RenderTargetView* m_pRTView[2];
   ID3D11ShaderResourceView* m_pSRView[2];

   ID3D11RenderTargetView* m_pOldRTView;
   ID3D11DepthStencilView* m_pOldDSView;
   D3D11_VIEWPORT m_pOldViewport[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
   UINT m_ViewportCount;

   ID3D11ShaderResourceView* m_pSrcSRView;

   // シェーダー用定数バッファ
   ID3D11Buffer* m_pConstantBuffers;

   // サンプラーステート
   ID3D11SamplerState* m_pSamplerState;

   UGraphicsPipeline* m_pGraphicsPipeline[2];

   // 縮小バッファの解像度
   UINT m_Width;
   UINT m_Height;

public:
   GaussianFilter();
   virtual ~GaussianFilter();
   void Create( ID3D11Device* pD3DDevice, DWORD Width, DWORD Height, DXGI_FORMAT Format, float Dispersion );
   void BeginPass( ID3D11DeviceContext* pD3DDeviceContext, ID3D11ShaderResourceView* pSRView );
   void Render( ID3D11DeviceContext* pD3DDeviceContext );
   void EndPass( ID3D11DeviceContext* pD3DDeviceContext );
   inline const ID3D11ShaderResourceView* GetBlurMap(){ return m_pSRView[1]; };
};

#endif

---GaussianFilter.cpp---  ↑

#include "../../Header/Shader/GaussianFilter.h"
#include "../../HLSL/GaussianFilter_Pass0_VS_Main.h"
#include "../../HLSL/GaussianFilter_Pass0_PS_Main.h"
#include "../../HLSL/GaussianFilter_Pass1_VS_Main.h"
#include "../../HLSL/GaussianFilter_Pass1_PS_Main.h"

GaussianFilter::GaussianFilter()
{
   m_pVertexBuffer = nullptr;
   for( int i=0; i<_countof( m_pRTView ); i++ )
   {
      m_pRTView[i] = nullptr;
      m_pSRView[i] = nullptr;
      m_pGraphicsPipeline[i] = nullptr;
   }
   m_pConstantBuffers = nullptr;
   m_pSamplerState = nullptr;
}

GaussianFilter::~GaussianFilter()
{
   SAFE_RELEASE( m_pSamplerState );
   SAFE_RELEASE( m_pConstantBuffers );
   for( int i=0; i<_countof( m_pRTView ); i++ )
   {
      SAFE_DELETE( m_pGraphicsPipeline[i] );
      SAFE_RELEASE( m_pSRView[i] );
      SAFE_RELEASE( m_pRTView[i] );
   }
   SAFE_RELEASE( m_pVertexBuffer );
}

void GaussianFilter::Create( ID3D11Device* pD3DDevice, DWORD Width, DWORD Height, DXGI_FORMAT Format, float Dispersion )
{
   m_Width = Width;
   m_Height = Height;

   // *******************************************************************************************************
   // パス0用のGaussianFilterの作成
   // *******************************************************************************************************

   m_pGraphicsPipeline[0] = NEW UGraphicsPipeline();

   // ラスタライザーステートを作成する
   m_pGraphicsPipeline[0]->CreateRasterizerState( pD3DDevice, D3D11_CULL_MODE::D3D11_CULL_BACK );

   // 深度ステンシルステートを無効にする
   m_pGraphicsPipeline[0]->CreateDepthStencilState( pD3DDevice, FALSE, D3D11_DEPTH_WRITE_MASK::D3D11_DEPTH_WRITE_MASK_ZERO );

   // ブレンドステートを作成する
   UGraphicsPipeline::UEBLEND_STATE BlendStateType[1] = { UGraphicsPipeline::UEBLEND_STATE::NONE };
   m_pGraphicsPipeline[0]->CreateBlendState( pD3DDevice, BlendStateType, 1 );

   D3D11_INPUT_ELEMENT_DESC layout[] = {
         { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
         { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
   };

   // シェーダーを作成する
#if defined(DEBUG) || defined(_DEBUG)
   m_pGraphicsPipeline[0]->CreateVertexShaderFromFile( pD3DDevice, _T("../HLSL/GaussianFilter.hlsl"),
      "GaussianFilter_Pass0_VS_Main", layout, _countof( layout ) );

   m_pGraphicsPipeline[0]->CreatePixelShaderFromFile(  pD3DDevice, _T("../HLSL/GaussianFilter.hlsl"),
      "GaussianFilter_Pass0_PS_Main" );
#else
   m_pGraphicsPipeline[0]->CreateVertexShaderFromMemory( pD3DDevice, (LPBYTE)g_GaussianFilter_Pass0_VS_Main,
      sizeof( g_GaussianFilter_Pass0_VS_Main ), layout, _countof( layout ) );

   m_pGraphicsPipeline[0]->CreatePixelShaderFromMemory(  pD3DDevice, (LPBYTE)g_GaussianFilter_Pass0_PS_Main,
      sizeof( g_GaussianFilter_Pass0_PS_Main ) );
#endif

   // レンダーターゲットビューを作成する
   UMaps::CreateRenderTargetView( pD3DDevice, Format, m_Width, m_Height, &m_pRTView[0], &m_pSRView[0] );

   // *******************************************************************************************************
   // パス1用のGaussianFilterの作成
   // シェーダー以外はパス0の使いまわし
   // *******************************************************************************************************

   m_pGraphicsPipeline[1] = NEW UGraphicsPipeline();

   // シェーダーを作成する
#if defined(DEBUG) || defined(_DEBUG)
   m_pGraphicsPipeline[1]->CreateVertexShaderFromFile( pD3DDevice, _T("../HLSL/GaussianFilter.hlsl"),
      "GaussianFilter_Pass1_VS_Main", layout, _countof( layout ) );

   m_pGraphicsPipeline[1]->CreatePixelShaderFromFile(  pD3DDevice, _T("../HLSL/GaussianFilter.hlsl"),
      "GaussianFilter_Pass1_PS_Main" );
#else
   m_pGraphicsPipeline[1]->CreateVertexShaderFromMemory( pD3DDevice, (LPBYTE)g_GaussianFilter_Pass1_VS_Main,
      sizeof( g_GaussianFilter_Pass1_VS_Main ), layout, _countof( layout ) );

   m_pGraphicsPipeline[1]->CreatePixelShaderFromMemory(  pD3DDevice, (LPBYTE)g_GaussianFilter_Pass1_PS_Main,
      sizeof( g_GaussianFilter_Pass1_PS_Main ) );
#endif

   // レンダーターゲットビューを作成する
   UMaps::CreateRenderTargetView( pD3DDevice, Format, m_Width, m_Height, &m_pRTView[1], &m_pSRView[1] );

   // *******************************************************************************************************
   // メッシュ作成
   // *******************************************************************************************************

   // 頂点のデータ
   VERTEX v[] = {
      XMFLOAT3(   1,  1, 0 ), XMFLOAT2( 1, 0 ),
      XMFLOAT3(  -1,  1, 0 ), XMFLOAT2( 0, 0 ),
      XMFLOAT3(   1, -1, 0 ), XMFLOAT2( 1, 1 ),
      XMFLOAT3(  -1, -1, 0 ), XMFLOAT2( 0, 1 )
   };
   // 頂点バッファを作成する
   m_pVertexBuffer = UBuffers::CreateVertexBuffer( pD3DDevice, v, sizeof( v ), 0 );

   // サンプラーステートを作成する
   m_pSamplerState = USamplers::CreateSamplerState( pD3DDevice, D3D11_TEXTURE_ADDRESS_CLAMP );

   GaussianFilter::CBUFFER cbuffer;
   const UINT Division = 8;

   float total = 0.0f;
   float weight[Division];
   
   // ガウス関数による重みの計算
   for( int i=0; i<Division; i++ )
   {
      float pos = (float)i * 2.0f;
      weight[i] = expf( - pos * pos * Dispersion );
      total += weight[i];
   }

   // 重みの規格化
   for( int i=0; i<Division; i++ )
      weight[i] = weight[i] / total * 0.5f;

   ::CopyMemory( &cbuffer.Weight, weight, sizeof( float ) * Division );

   cbuffer.Width    = (float)m_Width;
   cbuffer.Height   = (float)m_Height;
   cbuffer.Offset.x = 16.0f / (float)m_Width;
   cbuffer.Offset.y = 16.0f / (float)m_Height;

   // 定数バッファを作成する
   m_pConstantBuffers = m_pGraphicsPipeline[0]->CreateConstantBuffer( pD3DDevice, (void*)&cbuffer, sizeof( GaussianFilter::CBUFFER ), 0 );
}

void GaussianFilter::BeginPass( ID3D11DeviceContext* pD3DDeviceContext, ID3D11ShaderResourceView* pSRView )
{
   m_pSrcSRView = pSRView;

   // レンダーターゲットを退避
   pD3DDeviceContext->OMGetRenderTargets( 1, &m_pOldRTView, &m_pOldDSView );

   // ビューポートの退避
   pD3DDeviceContext->RSGetViewports( &m_ViewportCount, nullptr );
   pD3DDeviceContext->RSGetViewports( &m_ViewportCount, &m_pOldViewport[0] );   

   D3D11_VIEWPORT Viewport[1];   
   Viewport[0].TopLeftX = 0;
   Viewport[0].TopLeftY = 0;
   Viewport[0].Width    = (FLOAT)m_Width;
   Viewport[0].Height   = (FLOAT)m_Height;
   Viewport[0].MinDepth = 0.0f;
   Viewport[0].MaxDepth = 1.0f;
   // ビューポートの切り替え
   pD3DDeviceContext->RSSetViewports( 1, Viewport );
}

void GaussianFilter::Render( ID3D11DeviceContext* pD3DDeviceContext )
{
   ID3D11ShaderResourceView* pSRView[2] = { m_pSrcSRView, m_pSRView[0] };

   // 各種グラフィックパイプラインを設定
   m_pGraphicsPipeline[0]->SetRasterizerState( pD3DDeviceContext );
   m_pGraphicsPipeline[0]->SetDepthStencilState( pD3DDeviceContext );
   m_pGraphicsPipeline[0]->SetBlendState( pD3DDeviceContext );

   // 頂点バッファ設定
   UINT stride = sizeof( GaussianFilter::VERTEX );
   UINT offset = 0;
   pD3DDeviceContext->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &stride, &offset );

   // プリミティブ タイプおよびデータの順序に関する情報を設定
   pD3DDeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

   // ピクセルシェーダーのサンプラーステートを設定する
   pD3DDeviceContext->PSSetSamplers( 0, 1, &m_pSamplerState );

   // 定数バッファを設定
   pD3DDeviceContext->VSSetConstantBuffers( 0, 1, &m_pConstantBuffers );
   pD3DDeviceContext->PSSetConstantBuffers( 0, 1, &m_pConstantBuffers );

   for( int i=0; i<_countof( m_pRTView ); i++ )
   {
      pD3DDeviceContext->OMSetRenderTargets( 1, &m_pRTView[i], nullptr );

      // バックバッファをクリアする。( 上書きのため必要なし )

      // シェーダーを設定する
      m_pGraphicsPipeline[i]->SetVertexShader( pD3DDeviceContext );
      m_pGraphicsPipeline[i]->SetHullShader( pD3DDeviceContext );
      m_pGraphicsPipeline[i]->SetDomainShader( pD3DDeviceContext );
      m_pGraphicsPipeline[i]->SetGeometryShader( pD3DDeviceContext );
      m_pGraphicsPipeline[i]->SetPixelShader( pD3DDeviceContext );

      // デカールマップを設定する
      pD3DDeviceContext->PSSetShaderResources( 0, 1, &pSRView[i] );

      // 描画
      pD3DDeviceContext->Draw( 4, 0 );
   }
}

void GaussianFilter::EndPass( ID3D11DeviceContext* pD3DDeviceContext )
{
   pD3DDeviceContext->OMSetRenderTargets( 1, &m_pOldRTView, m_pOldDSView );
   SAFE_RELEASE( m_pOldRTView );
   SAFE_RELEASE( m_pOldDSView );

   pD3DDeviceContext->RSSetViewports( m_ViewportCount, m_pOldViewport );
}

---VarianceShadowMaps_Pass0.hlsl---  ↑

// ************************************************************
// Variance Shadow Maps Pass0
// ************************************************************

// 定数バッファ
cbuffer CBuffer : register( b0 )
{
   column_major float4x4 g_matLightWorldViewProj    : packoffset( c0 );  // ライトビューの正射影行列
};

// 頂点シェーダーの入力パラメータ
struct VS_IN
{
   float3 pos    : POSITION;    // 頂点座標
   float3 normal : NORMAL;      // 法線ベクトル
   float2 texel  : TEXCOORD;    // テクセル
};

// 頂点シェーダーの出力パラメータ
struct VS_OUT_PS_IN
{
   float4 pos      : SV_POSITION;
   float4 posLWVP : TEXCOORD0;
};

// 頂点シェーダー
VS_OUT_PS_IN VarianceShadowMaps_Pass0_VS_Main( VS_IN In )
{
   VS_OUT_PS_IN Out;

   Out.pos = mul( float4( In.pos, 1.0f ), g_matLightWorldViewProj );
   Out.posLWVP = Out.pos;
   
   return Out;
}

// ピクセルシェーダ
float2 VarianceShadowMaps_Pass0_PS_Main( VS_OUT_PS_IN In ) : SV_TARGET
{
   float z = In.posLWVP.z / In.posLWVP.w;
   return float2( z, z * z );
}

ライトビューの正射影空間上での深度値を出力するシェーダーソースです。
チェビチェフの不等式の E(x²) の計算で必要となるため、深度値を二乗した値も出力します。
それ以外はシャドウマップと同じです。

---VarianceShadowMaps_Pass1.hlsl---  ↑


// ************************************************************
// Variance Shadow Maps Pass1
// ************************************************************

// 定数バッファ
cbuffer CBuffer : register( b0 )
{
   column_major float4x4 g_matCameraWorldViewProj  : packoffset( c0 );   // カメラビューの射影行列
   column_major float4x4 g_matLightWorldViewProj   : packoffset( c4 );   // ライトビューの正射影行列
   float4 g_vecLightPos              : packoffset( c8 );   // ローカル座標系での平行光源の位置ベクトル
   float4 g_DepthMapSize             : packoffset( c9 );   // ブラー深度マップのサイズ
};

// 頂点シェーダーの入力パラメータ
struct VS_IN
{
   float3 pos    : POSITION;    // 頂点座標
   float3 normal : NORMAL;      // 法線ベクトル
   float2 texel  : TEXCOORD;    // テクセル
};

// テクスチャー
Texture2D g_DecalMap : register( t0 );

// ブラーを適用した深度マップ
Texture2D g_BlurDepthMap : register( t1 );

// サンプラーステート
SamplerState  g_Sampler : register( s0 );

// 頂点シェーダーの出力パラメータ
struct VS_OUT_PS_IN
{
   float4 pos       : SV_POSITION;
   float3 normal    : NORMAL;
   float2 texel     : TEXCOORD0;
   float4 posLWVP   : TEXCOORD1;
};

// 頂点シェーダー
VS_OUT_PS_IN VarianceShadowMaps_Pass1_VS_Main( VS_IN In )
{
   VS_OUT_PS_IN Out;

   Out.pos = mul( float4( In.pos, 1.0f ), g_matCameraWorldViewProj );
   Out.posLWVP = mul( float4( In.pos, 1.0f ), g_matLightWorldViewProj );
   Out.normal = In.normal;
   Out.texel = In.texel;

   return Out;
}

// ピクセルシェーダ
float4 VarianceShadowMaps_Pass1_PS_Main( VS_OUT_PS_IN In ) : SV_TARGET
{
   float shadowColor;
   
   // テクセルを計算
   float2 texel = float2( In.posLWVP.x / In.posLWVP.w * 0.5f + 0.5f, In.posLWVP.y / In.posLWVP.w * -0.5f + 0.5f );
   
   // 深度マップの範囲外はとりあえず影なしにする
   if( texel.x < 0 || texel.x > 1 || texel.y < 0 || texel.y > 1 )
      shadowColor = 1.0f;
   else
   {
      // ブラー深度マップから深度値を取得
      float2 blursm = g_BlurDepthMap.Load( float3( texel * g_DepthMapSize.xy, 0 ) ).rg;

      // t
      float t = In.posLWVP.z / In.posLWVP.w;

      // 現在の深度値とブラー深度マップ上の深度値を比較
      if( blursm.r < t - 0.01f )
      {
         // σ^2
         float variance = blursm.g - blursm.r * blursm.r;

         // σ^2 / ( σ^2 + ( t- E(x) )^2 )
         float P = variance / ( variance + ( t - blursm.r ) * ( t - blursm.r ) );
      
         shadowColor = max( P, 0.3f );
      }
      else
         shadowColor = 1.0f;
   }
   
   // ハーフランバート
   float lambert = dot( g_vecLightPos.xyz, In.normal );
   lambert = lambert * 0.5f + 0.5f;

   // デカールマップ
   float4 decalmap = g_DecalMap.Sample( g_Sampler, In.texel );

   return ( decalmap * min( lambert, shadowColor ) ).bgra;
}

影になる確率を計算します。

---VarianceShadowMaps.h---  ↑


#ifndef VARIANCE_SHADOW_MAPS_H
#define VARIANCE_SHADOW_MAPS_H

#include "../Common/UCommon.h"
#include "../Common/UException.h"
#include "../Common/UGraphicsPipeline.h"
#include "../Common/UDirect3D11.h"
#include "GaussianFilter.h"

class VarianceShadowMaps
{
private:
   // Direct3D用定数バッファ設定用構造体
   typedef struct _CBUFFER_PASS0
   {
      XMMATRIX matLightWorldViewProj;
   }CBUFFER_PASS0;

   typedef struct _CBUFFER_PASS1
   {
      XMMATRIX matCameraWorldViewProj;
      XMMATRIX matLightWorldViewProj;
      XMFLOAT4 vecLightDir;
      XMFLOAT4 vecShadowMapSize;
   }CBUFFER_PASS1;

   ID3D11RenderTargetView* m_pRTView;
   ID3D11DepthStencilView* m_pDSView;
   ID3D11ShaderResourceView* m_pSRView;

   ID3D11RenderTargetView* m_pOldRTView;
   ID3D11DepthStencilView* m_pOldDSView;
   D3D11_VIEWPORT m_pOldViewport[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
   UINT m_ViewportCount;

   // シェーダー用定数バッファ
   ID3D11Buffer* m_pConstantBuffers[2];

   UGraphicsPipeline* m_pGraphicsPipeline[2];

   GaussianFilter* m_pGaussianFilter;

   DWORD m_Width, m_Height;
   
   int m_Pass;
   
   XMMATRIX m_MatLightViewProj;
   XMMATRIX m_MatCameraViewProj;
   XMFLOAT4 m_VecLightPos;

public:
   VarianceShadowMaps();
   virtual ~VarianceShadowMaps();
   void Invalidate();
   void Create( ID3D11Device* pD3DDevice, DWORD Width, DWORD Height, float Dispersion );
   void BeginPass( ID3D11DeviceContext* pD3DDeviceContext, UINT Pass,
                   XMFLOAT4* pVecLightPos, XMMATRIX* pMatLightProj, XMMATRIX* pMatCameraView, XMMATRIX* pMatCameraProj );
   void SetConstantBuffers( ID3D11DeviceContext* pD3DDeviceContext, XMMATRIX* pMatWorld );
   void EndPass( ID3D11DeviceContext* pD3DDeviceContext );
   inline UINT GetMaxPass(){ return 2; };
   inline const ID3D11ShaderResourceView* GetDepthMap(){ return m_pSRView; };
   inline const ID3D11ShaderResourceView* GetBlurDepthMap(){ return m_pGaussianFilter->GetBlurMap(); };
};

#endif

---VarianceShadowMaps.cpp---  ↑


#include "../../Header/Shader/VarianceShadowMaps.h"
#include "../../HLSL/VarianceShadowMaps_Pass0_VS_Main.h"
#include "../../HLSL/VarianceShadowMaps_Pass0_PS_Main.h"
#include "../../HLSL/VarianceShadowMaps_Pass1_VS_Main.h"
#include "../../HLSL/VarianceShadowMaps_Pass1_PS_Main.h"

VarianceShadowMaps::VarianceShadowMaps()
{
   m_pRTView = nullptr;
   m_pDSView = nullptr;
   m_pSRView = nullptr;
   for( int i=0; i<_countof( m_pGraphicsPipeline ); i++ )
   {
      m_pGraphicsPipeline[i] = nullptr;
      m_pConstantBuffers[i] = nullptr;
   }
   m_pGaussianFilter = nullptr;

   m_Pass = -1;
}

VarianceShadowMaps::~VarianceShadowMaps()
{
   SAFE_DELETE( m_pGaussianFilter );
   for( int i=0; i<_countof( m_pGraphicsPipeline ); i++ )
   {
      SAFE_DELETE( m_pGraphicsPipeline[i] );
      SAFE_RELEASE( m_pConstantBuffers[i] );
   }
   SAFE_RELEASE( m_pSRView );
   SAFE_RELEASE( m_pDSView );
   SAFE_RELEASE( m_pRTView );
}

void VarianceShadowMaps::Create( ID3D11Device* pD3DDevice, DWORD Width, DWORD Height, float Dispersion )
{
   m_Width = Width;
   m_Height = Height;

   m_Pass = -1;

   // *******************************************************************************************************
   // パス0用のUGraphicsPipelineの作成
   // *******************************************************************************************************

   m_pGraphicsPipeline[0] = NEW UGraphicsPipeline();

   // ラスタライザーステートを作成する
   m_pGraphicsPipeline[0]->CreateRasterizerState( pD3DDevice, D3D11_CULL_MODE::D3D11_CULL_BACK );

   // 深度ステンシルステートを作成する
   m_pGraphicsPipeline[0]->CreateDepthStencilState( pD3DDevice, TRUE, D3D11_DEPTH_WRITE_MASK::D3D11_DEPTH_WRITE_MASK_ALL );

   // ブレンドステートを作成する
   UGraphicsPipeline::UEBLEND_STATE BlendStateType[1] = { UGraphicsPipeline::UEBLEND_STATE::NONE };
   m_pGraphicsPipeline[0]->CreateBlendState( pD3DDevice, BlendStateType, 1 );

   D3D11_INPUT_ELEMENT_DESC layout[] = {
         { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
         { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
         { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
   };

   // シェーダーを作成する
#if defined(DEBUG) || defined(_DEBUG)
   m_pGraphicsPipeline[0]->CreateVertexShaderFromFile( pD3DDevice, _T("../HLSL/VarianceShadowMaps_Pass0.hlsl"), "VarianceShadowMaps_Pass0_VS_Main", layout, _countof( layout ) );
   m_pGraphicsPipeline[0]->CreatePixelShaderFromFile(  pD3DDevice, _T("../HLSL/VarianceShadowMaps_Pass0.hlsl"), "VarianceShadowMaps_Pass0_PS_Main" );
#else
   m_pGraphicsPipeline[0]->CreateVertexShaderFromMemory( pD3DDevice, (LPBYTE)g_VarianceShadowMaps_Pass0_VS_Main, sizeof( g_VarianceShadowMaps_Pass0_VS_Main ), layout, _countof( layout ) );
   m_pGraphicsPipeline[0]->CreatePixelShaderFromMemory(  pD3DDevice, (LPBYTE)g_VarianceShadowMaps_Pass0_PS_Main, sizeof( g_VarianceShadowMaps_Pass0_PS_Main ) );
#endif

   // レンダーターゲットビューを作成する
   UMaps::CreateRenderTargetView( pD3DDevice, DXGI_FORMAT_R16G16_FLOAT, m_Width, m_Height, &m_pRTView, &m_pSRView );
   
   // 深度ステンシルバッファを作成する
   UMaps::CreateDepthStencilView( pD3DDevice, m_Width, m_Height, &m_pDSView, nullptr );

   // 定数バッファを作成する
   m_pConstantBuffers[0] = m_pGraphicsPipeline[0]->CreateConstantBuffer( pD3DDevice, nullptr, sizeof( VarianceShadowMaps::CBUFFER_PASS0 ), D3D11_CPU_ACCESS_WRITE );

   // *******************************************************************************************************
   // パス1用のUGraphicsPipelineの作成
   // シェーダーと定数バッファ以外はパス0の使いまわし
   // *******************************************************************************************************

   m_pGraphicsPipeline[1] = NEW UGraphicsPipeline();

   // シェーダーを作成する
#if defined(DEBUG) || defined(_DEBUG)
   m_pGraphicsPipeline[1]->CreateVertexShaderFromFile( pD3DDevice, _T("../HLSL/VarianceShadowMaps_Pass1.hlsl"), "VarianceShadowMaps_Pass1_VS_Main", layout, _countof( layout ) );
   m_pGraphicsPipeline[1]->CreatePixelShaderFromFile(  pD3DDevice, _T("../HLSL/VarianceShadowMaps_Pass1.hlsl"), "VarianceShadowMaps_Pass1_PS_Main" );
#else
   m_pGraphicsPipeline[1]->CreateVertexShaderFromMemory( pD3DDevice, (LPBYTE)g_VarianceShadowMaps_Pass1_VS_Main, sizeof( g_VarianceShadowMaps_Pass1_VS_Main ), layout, _countof( layout ) );
   m_pGraphicsPipeline[1]->CreatePixelShaderFromMemory(  pD3DDevice, (LPBYTE)g_VarianceShadowMaps_Pass1_PS_Main, sizeof( g_VarianceShadowMaps_Pass1_PS_Main ) );
#endif

   // 定数バッファを作成する
   m_pConstantBuffers[1] = m_pGraphicsPipeline[1]->CreateConstantBuffer( pD3DDevice, nullptr, sizeof( VarianceShadowMaps::CBUFFER_PASS1 ), D3D11_CPU_ACCESS_WRITE );

   m_pGaussianFilter = NEW GaussianFilter();
   m_pGaussianFilter->Create( pD3DDevice, m_Width / 2, m_Height / 2, DXGI_FORMAT_R16G16_FLOAT, Dispersion );
}

void VarianceShadowMaps::BeginPass( ID3D11DeviceContext* pD3DDeviceContext, UINT Pass,
                                    XMFLOAT4* pVecLightPos, XMMATRIX* pMatLightProj, XMMATRIX* pMatCameraView, XMMATRIX* pMatCameraProj)
{
   if (m_Pass != -1)
   {
      throw(UException(-1, _T("VarianceShadowMaps::BeginPass()はCreate()またはEndPass()実行後に使用してください")));
   }

   m_Pass = (int)Pass;

   switch( m_Pass )
   {
   case 0:
      {
         // レンダーターゲットビューと深度ステンシルビューとビューポートを切り替える

         pD3DDeviceContext->OMGetRenderTargets( 1, &m_pOldRTView, &m_pOldDSView );
         pD3DDeviceContext->OMSetRenderTargets( 1, &m_pRTView, m_pDSView );

         pD3DDeviceContext->RSGetViewports( &m_ViewportCount, nullptr );
         pD3DDeviceContext->RSGetViewports( &m_ViewportCount, &m_pOldViewport[0] );   

         D3D11_VIEWPORT Viewport[1];   
         Viewport[0].TopLeftX = 0;
         Viewport[0].TopLeftY = 0;
         Viewport[0].Width    = (FLOAT)m_Width;
         Viewport[0].Height   = (FLOAT)m_Height;
         Viewport[0].MinDepth = 0.0f;
         Viewport[0].MaxDepth = 1.0f;
         pD3DDeviceContext->RSSetViewports( 1, Viewport );

         float ClearColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };

          // バックバッファをクリアする。
         pD3DDeviceContext->ClearRenderTargetView( m_pRTView, ClearColor );

         // 深度バッファをクリアする。
         pD3DDeviceContext->ClearDepthStencilView( m_pDSView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0  );

         m_VecLightPos = *pVecLightPos;

         // ライトビュー × 射影行列を作成
         XMMATRIX matLightView = XMMatrixLookAtLH(XMLoadFloat3((XMFLOAT3*)&m_VecLightPos), XMLoadFloat3(&XMFLOAT3(0, 0, 0)), XMLoadFloat3(&XMFLOAT3(0, 1, 0)));
         m_MatLightViewProj = matLightView * (*pMatLightProj);

         m_MatCameraViewProj = XMMatrixIdentity();
      }
      break;

   case 1:
      {
         ID3D11ShaderResourceView* pSRView = const_cast<ID3D11ShaderResourceView*>( m_pGaussianFilter->GetBlurMap() );

         // 深度マップをシェーダーに設定
         pD3DDeviceContext->PSSetShaderResources( 1, 1, &pSRView );

         // ライトビューはPass0と同じにするためここでは設定しない

         // カメラビュー × 射影行列を作成
         m_MatCameraViewProj = (*pMatCameraView) * (*pMatCameraProj);
      }
      break;

   default:
      throw(UException(-1, _T("VarianceShadowMaps::BeginPass()で無効なPassが指定されました")));
   }

   // 各種ステートを設定する
   m_pGraphicsPipeline[0]->SetRasterizerState( pD3DDeviceContext );
   m_pGraphicsPipeline[0]->SetDepthStencilState( pD3DDeviceContext );
   m_pGraphicsPipeline[0]->SetBlendState( pD3DDeviceContext );

   // 各種グラフィックパイプラインを設定
   m_pGraphicsPipeline[m_Pass]->SetVertexShader( pD3DDeviceContext );
   m_pGraphicsPipeline[m_Pass]->SetHullShader( pD3DDeviceContext );
   m_pGraphicsPipeline[m_Pass]->SetDomainShader( pD3DDeviceContext );
   m_pGraphicsPipeline[m_Pass]->SetGeometryShader( pD3DDeviceContext );
   m_pGraphicsPipeline[m_Pass]->SetPixelShader( pD3DDeviceContext );
}

void VarianceShadowMaps::SetConstantBuffers( ID3D11DeviceContext* pD3DDeviceContext, XMMATRIX* pMatWorld )
{
   HRESULT hr = E_FAIL;

   if (m_Pass == -1)
   {
      throw(UException(-1, _T("VarianceShadowMaps::SetConstantBuffers()はBeginPass()実行後に使用してください")));
   }

   switch( m_Pass )
   {
   case 0:
      {
         XMMATRIX matLightWorldViewProj;
         matLightWorldViewProj = (*pMatWorld) * m_MatLightViewProj;
         matLightWorldViewProj = XMMatrixTranspose( matLightWorldViewProj );

         // 定数バッファを作成する
         D3D11_MAPPED_SUBRESOURCE mappedResource;
         if( FAILED( hr = pD3DDeviceContext->Map( m_pConstantBuffers[m_Pass], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource ) ) )
            throw( UException( -1, _T("VarianceShadowMaps::SetConstantBuffers()でエラーが発生しました。Map()が失敗しました。") ) );

         VarianceShadowMaps::CBUFFER_PASS0* cbuffer = (VarianceShadowMaps::CBUFFER_PASS0*)mappedResource.pData;

         ::CopyMemory( &cbuffer->matLightWorldViewProj, &matLightWorldViewProj, sizeof( XMMATRIX ));

         pD3DDeviceContext->Unmap( m_pConstantBuffers[m_Pass], 0 );

         // 定数バッファを設定
         pD3DDeviceContext->VSSetConstantBuffers( 0, 1, &m_pConstantBuffers[m_Pass] );
         pD3DDeviceContext->PSSetConstantBuffers( 0, 1, &m_pConstantBuffers[m_Pass] );
      }
      break;

   case 1:
      {
         XMMATRIX matCameraWorldViewProj, matLightWorldViewProj;

         matCameraWorldViewProj = (*pMatWorld) * m_MatCameraViewProj;
         matCameraWorldViewProj = XMMatrixTranspose( matCameraWorldViewProj );

         matLightWorldViewProj = (*pMatWorld) * m_MatLightViewProj;
         matLightWorldViewProj = XMMatrixTranspose( matLightWorldViewProj );

         // メッシュ基準のローカル座標系上での平行光源の方向ベクトルを計算する
         XMMATRIX matInv = XMMatrixInverse( nullptr, *pMatWorld );
         XMVECTOR v = XMVector4Transform( XMLoadFloat4( &m_VecLightPos ), matInv );
         XMVECTOR nv = XMVector3Normalize( v );
         XMFLOAT4 localLightPos;
         XMStoreFloat4( &localLightPos, nv );
         
         XMFLOAT4 shadowMapSize = XMFLOAT4( (float)( m_Width / 2 ), (float)( m_Height / 2 ), 0, 0 );

         // 定数バッファを作成する
         D3D11_MAPPED_SUBRESOURCE mappedResource;
         if( FAILED( hr = pD3DDeviceContext->Map( m_pConstantBuffers[m_Pass], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource ) ) )
            throw( UException( -1, _T("VarianceShadowMaps::SetConstantBuffers()でエラーが発生しました。Map()が失敗しました。") ) );

         VarianceShadowMaps::CBUFFER_PASS1* cbuffer = (VarianceShadowMaps::CBUFFER_PASS1*)mappedResource.pData;

         ::CopyMemory( &cbuffer->matCameraWorldViewProj, &matCameraWorldViewProj, sizeof( XMMATRIX ));
         ::CopyMemory( &cbuffer->matLightWorldViewProj, &matLightWorldViewProj, sizeof( XMMATRIX ));
         ::CopyMemory( &cbuffer->vecLightDir, &localLightPos, sizeof( XMFLOAT4 ));
         ::CopyMemory( &cbuffer->vecShadowMapSize, &shadowMapSize, sizeof( XMFLOAT4 ));

         pD3DDeviceContext->Unmap( m_pConstantBuffers[m_Pass], 0 );

         // 定数バッファを設定
         pD3DDeviceContext->VSSetConstantBuffers( 0, 1, &m_pConstantBuffers[m_Pass] );
         pD3DDeviceContext->PSSetConstantBuffers( 0, 1, &m_pConstantBuffers[m_Pass] );
      }
      break;
   }
}

void VarianceShadowMaps::EndPass( ID3D11DeviceContext* pD3DDeviceContext )
{
   switch( m_Pass )
   {
   case 0:
      // レンダーターゲットビューと深度ステンシルビューとビューポートを戻す

      pD3DDeviceContext->OMSetRenderTargets( 1, &m_pOldRTView, m_pOldDSView );
      SAFE_RELEASE( m_pOldRTView );
      SAFE_RELEASE( m_pOldDSView );

      pD3DDeviceContext->RSSetViewports( m_ViewportCount, m_pOldViewport );

      // 作成した深度マップにブラーフィルターを適用する
      m_pGaussianFilter->BeginPass( pD3DDeviceContext, m_pSRView );
      m_pGaussianFilter->Render( pD3DDeviceContext );
      m_pGaussianFilter->EndPass( pD3DDeviceContext );
      break;

   case 1:
      ID3D11ShaderResourceView* pNull = nullptr;
      pD3DDeviceContext->PSSetShaderResources( 1, 1, &pNull );
      break;
   }

   m_Pass = -1;
}

---main.cpp---  ↑


#include "../Header/Common/UCommon.h"
#include "../Header/Common/UException.h"
#include "../Header/Common/UDirect3D11.h"
#include "../Header/Common/UDebugFont.h"
#if defined(DEBUG) || defined(_DEBUG)
#include "../Header/Common/USRViewRenderer.h"
#endif

#include "../Header/Shader/VarianceShadowMaps.h"

#include "../Header/Mesh/F14Mesh.h"
#include "../Header/Mesh/PlaneMesh.h"

// アプリケーション名
TCHAR* AppName = _T("Direct3D11 Variance Shadow Maps");

// Direct3D関連の自作クラス
UDirect3D11* g_pDirect3D11 = nullptr;

VarianceShadowMaps* g_pVarianceShadowMaps = nullptr;

F14Mesh* g_pF14Mesh = nullptr;
PlaneMesh* g_pPlaneMesh = nullptr;

UFPS* g_pFPS = nullptr;

#if defined(DEBUG) || defined(_DEBUG)
USRViewRenderer* g_pSRViewRenderer = nullptr;
#endif

DWORD g_Width = 640, g_Height = 480;

// カメラビュー行列
XMMATRIX g_MatCameraView = XMMatrixIdentity();

// 射影行列
XMMATRIX g_MatCameraProj = XMMatrixPerspectiveFovLH( XM_PI / 5.0f, (float)g_Width / (float)g_Height, 0.01f, 500.0f );

// 正行列
XMMATRIX g_MatLightOrtho = XMMatrixOrthographicLH( (float)300, (float)300, 1.0f, 800.0f );

// 平行光源の位置ベクトル
XMFLOAT4 g_VecLightPos = XMFLOAT4( 300.0f, 200.0f, 300.0f, 0 );

// メモリ解放
void Invalidate()
{
#if defined(DEBUG) || defined(_DEBUG)
   SAFE_DELETE( g_pSRViewRenderer );
#endif
   SAFE_DELETE( g_pFPS );
   SAFE_DELETE( g_pVarianceShadowMaps );
   SAFE_DELETE( g_pPlaneMesh );
   SAFE_DELETE( g_pF14Mesh );
   SAFE_DELETE( g_pDirect3D11 );
}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
   switch( msg )
   {
   case WM_KEYUP:
      // アプリ終了
      if( wParam == VK_ESCAPE )
         ::DestroyWindow( hWnd );
      break;

   case WM_DESTROY:
      ::PostQuitMessage(0);
      break;

   default:
      return ::DefWindowProc( hWnd, msg, wParam, lParam );
   }

   return 0L;
}

// Direct3Dの作成
void CreateDirect3D( HINSTANCE hInstance )
{
   DXGI_MODE_DESC* pModeDescArray = nullptr;

   __try
   {
      DXGI_MODE_DESC  ModeDesc;
      UINT num;

      // ディスプレイモード一覧の数を取得する
      g_pDirect3D11->GetDisplayMode( nullptr, &num );

      pModeDescArray = NEW DXGI_MODE_DESC[num];
      // ディスプレイモード一覧を取得する
      g_pDirect3D11->GetDisplayMode( pModeDescArray, &num );

      bool find = false;
      for( UINT i=0; i<num; i++ )
      {
         // 適切な解像度のディスプレイモードを検索する
         if( pModeDescArray[i].Width == g_Width && pModeDescArray[i].Height == g_Height )
         {
            find = true;
            ::CopyMemory( &ModeDesc, &pModeDescArray[i], sizeof( DXGI_MODE_DESC ) );
            break;
         }
      }

      if( find == false )
         throw( UException( -1, _T("InitDirect3D()でエラーが発生しました。適切なディスプレイモードの解像度を取得できません。") ) );

      // ウィンドウの作成およびDirect3D の作成
      g_pDirect3D11->CreateDirect3D11( AppName, hInstance, WndProc, &ModeDesc, TRUE, TRUE );

      // FPS描画クラス
      g_pFPS->CreateMesh( g_pDirect3D11->m_pD3DDevice );

#if defined(DEBUG) || defined(_DEBUG)
      g_pSRViewRenderer->Create( g_pDirect3D11->m_pD3DDevice, 1.0f - 400.0f / (float)g_Width, 1.0f, 1.0f, 1.0f - 400.0f / (float)g_Height );
#endif
   }
   __finally
   {
      SAFE_DELETE_ARRAY( pModeDescArray );
   }
}

void CreateGraphicePipeline()
{
   DWORD Width = 1024, Height = 1024;

   g_pVarianceShadowMaps->Create( g_pDirect3D11->m_pD3DDevice, Width, Height, 0.2f );
}

// メッシュを作成する
void CreateMesh()
{
   g_pF14Mesh = NEW F14Mesh();
   g_pF14Mesh->CreateMesh( g_pDirect3D11->m_pD3DDevice );

   g_pPlaneMesh = NEW PlaneMesh();
   g_pPlaneMesh->CreateMesh( g_pDirect3D11->m_pD3DDevice );
}

void CreateResource( HINSTANCE hInstance )
{
   // Direct3D 関連自作クラスのインスタンスを作成
   // CreateDirect3D()関数内でインスタンスの作成を行うと、C2712 エラーが発生するのでここで行う。
   g_pDirect3D11 = NEW UDirect3D11();

   g_pVarianceShadowMaps = NEW VarianceShadowMaps();

   g_pFPS = NEW UFPS();

#if defined(DEBUG) || defined(_DEBUG)
   g_pSRViewRenderer = NEW USRViewRenderer();
#endif

   // Direct3Dの作成
   CreateDirect3D( hInstance );

   // グラフィックパイプラインリソースの作成
   CreateGraphicePipeline();

   // リソースの作成
   CreateMesh();
}

void NextFrame()
{
   // ビュー行列
   if( GetKeyState( VK_UP ) & 0x8000 )
      g_MatCameraView *= XMMatrixTranslation( 0, 0, -0.1f );
   if( GetKeyState( VK_DOWN ) & 0x8000 )
      g_MatCameraView *= XMMatrixTranslation( 0, 0, 0.1f );
   if( GetKeyState( VK_RIGHT ) & 0x8000 )
      g_MatCameraView *= XMMatrixRotationY( -0.002f );
   if( GetKeyState( VK_LEFT ) & 0x8000 )
      g_MatCameraView *= XMMatrixRotationY( 0.002f );
   if( GetKeyState( 'Q' ) & 0x8000 )
      g_MatCameraView *= XMMatrixRotationX( 0.002f );
   if( GetKeyState( 'A' ) & 0x8000 )
      g_MatCameraView *= XMMatrixRotationX( -0.002f );

   g_pFPS->NextFrame();

   g_pF14Mesh->NextFrame();
   g_pPlaneMesh->NextFrame();
}

// 描画処理
HRESULT Render()
{
   HRESULT hr = E_FAIL;
   
   static float ClearColor[4] = { 0.3f, 0.3f, 1.0f, 1.0f };

    // バックバッファをクリアする。
   g_pDirect3D11->ClearBackBuffer( ClearColor ); 

   // 深度バッファをクリアする。
   g_pDirect3D11->ClearDepthStencilView( D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0 );

   for( UINT i=0; i<g_pVarianceShadowMaps->GetMaxPass(); i++ )
   {
      g_pVarianceShadowMaps->BeginPass( g_pDirect3D11->m_pD3DDeviceContext, i, &g_VecLightPos, &g_MatLightOrtho, &g_MatCameraView, &g_MatCameraProj );

      for( UINT j=0; j<g_pF14Mesh->GetMaxCount(); j++ )
      {
         // 定数バッファを設定
         g_pVarianceShadowMaps->SetConstantBuffers( g_pDirect3D11->m_pD3DDeviceContext, g_pF14Mesh->GetMatWorld(j) );
         // レンダリング
         g_pF14Mesh->Render( g_pDirect3D11->m_pD3DDeviceContext );
      }

      // 定数バッファを設定
      g_pVarianceShadowMaps->SetConstantBuffers( g_pDirect3D11->m_pD3DDeviceContext, g_pPlaneMesh->GetMatWorld() );
      // レンダリング
      g_pPlaneMesh->Render( g_pDirect3D11->m_pD3DDeviceContext );

      g_pVarianceShadowMaps->EndPass( g_pDirect3D11->m_pD3DDeviceContext );
   }

#if defined(DEBUG) || defined(_DEBUG)
   int SrvRGB[] = { 0, 1, 2, 3 };
   g_pSRViewRenderer->SetSRView( g_pVarianceShadowMaps->GetBlurDepthMap(), SrvRGB );
   g_pSRViewRenderer->Render( g_pDirect3D11->m_pD3DDeviceContext );
#endif

   g_pFPS->Render( g_pDirect3D11->m_pD3DDeviceContext );

   // レンダリングされたイメージをユーザーに表示。
   if( FAILED( hr = g_pDirect3D11->Present( 0, 0 ) ) )
      throw( UException( hr, _T("IDXGISwapChain::Present()が失敗しました") ) );

   return hr;
}
 
// メイン関数
int APIENTRY _tWinMain( HINSTANCE hInstance,
                        HINSTANCE,
                        LPTSTR,
                        INT )
{
   // メモリリーク検出
//   _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | CRTDBG_CHECK_ALWAYS_DF );
   _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_EVERY_1024_DF );

   MSG msg = {0};

   try
   {
      CreateResource( hInstance );

      ::ShowWindow( g_pDirect3D11->m_hWnd, SW_SHOW );
      ::UpdateWindow( g_pDirect3D11->m_hWnd );

      // メッセージループ
      do
      { 
         if( ::PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
         {
            ::TranslateMessage(&msg); 
            ::DispatchMessage(&msg); 
         }
         else
         {
            NextFrame();
            Render();
         }
      }while( msg.message != WM_QUIT );
   }
   catch( UException ex )
   {
      ::MessageBox( nullptr, ex.m_pErrorStr, _T("エラー"), MB_OK );
   }

   Invalidate();

   ::UnregisterClass( AppName, hInstance );

   return msg.wParam;
}

DirectX 9 のときにやったソフトシャドウのサンプルで問題となっていたオブジェクトの輪郭付近で不要なぼかしがかかる問題がクリアされています。
また投影テクスチャーシャドウと異なりセルフシャドウが反映しています。

しかし問題点もあります。
まずセルフシャドウ部分で遮蔽物の深度値と影の発生位置の深度値が近い場合、影が消失します。まあ( t - E(x) )が0に近くなるためだから当然なのですが。
この辺どうにかしようかと思いましたが、結局いい方法が思いつきませんでした。

それと最も単純な深度マップ生成アルゴリズムを使用しているため、いまいちきれいにぼかしがかかっていません。


web拍手 by FC2

Prev Top Next

inserted by FC2 system