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

■Cook Torrance Prev Top Next
関連ページ:Direct3D11 自作ライブラリ


物理学的に正確な鏡面反射。

Aシェーダー系で使用するソースです。
CookTorrance.hlsl Blinn-Phongでレンダリングするためのシェーダーソース
CookTorrance.h CookTorranceクラスのヘッダーファイル
CookTorrance.cpp CookTorranceクラスのソースファイル

B個別に使用するソースです。
main.cpp main関数のソースファイル


---CookTorrance.hlsl---  ↑
// ************************************************************
// Cook Torrance
// ************************************************************

// 定数バッファ
cbuffer CBuffer : register( b0 ){
   column_major float4x4 g_matCameraWorldViewProj    : packoffset(c0);    // カメラビューのワールド×ビュー×射影行列
   float4 g_vecLightDir                              : packoffset(c4);    // 平行光源の方向ベクトル(ワールド行列の逆行列)
   float4 g_vecEyePos                                : packoffset(c5);    // カメラの位置ベクトル(ワールド×ビュー行列の逆行列)
   float g_m                                         : packoffset(c6.x);  // 表面材質の荒さ(反射光の分散率)
   float g_n2                                        : packoffset(c6.y);  // フレネル項で使用する反射係数
};

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

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

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

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

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

   Out.posWVP = mul( float4( In.pos, 1.0f ), g_matCameraWorldViewProj);
   Out.normal = In.normal;
   Out.texel = In.texel;
   Out.posLocal = In.pos.xyz;
   
   return Out;
}

// ピクセルシェーダー
float4 CookTorrance_PS_Main(VS_OUT_PS_IN In) : SV_TARGET{
   float4 Out = (float4)0;

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

   // 視線ベクトル
   float3 V = normalize(g_vecEyePos.xyz - In.posLocal);

   // 平行光源ベクトルと視線ベクトルとのハーフベクトルを計算
   float3 H = normalize(g_vecLightDir.xyz + V);

   float NH = dot(In.normal, H);

   float NV = dot(In.normal, V);
   
   float NL = dot(In.normal, g_vecLightDir.xyz);
   
   float VH = dot(V, H);
   
   // ベックマン分布(反射光の分散率)
   float NH2 = NH * NH;
   float D = exp( -( 1.0f - NH2 ) / ( NH2 * g_m * g_m ) ) / ( 4.0f * g_m * g_m * NH2 * NH2 );

   // 幾何減衰項(微小面による自己陰影)
   float G = min( 2.0f * NH * NV / VH, min( 2.0f * NH * NL / VH, 1.0f ) );
   
   // フレネル項(Schlickによる近似)
   // https://en.wikipedia.org/wiki/Schlick%27s_approximation
   // 法線に平行に入射する光の反射係数
   // なお n1 に対応する材質は大気でありその屈折率は 1.0f で近似できるらしい
   float r1 = 1.0f - g_n2;
   float r2 = 1.0f + g_n2;
   float r3 = 1.0f - NV;
   float R0 = r1 * r1 / r2 / r2;
   float Rt = R0 + ( 1.0f - R0 ) * ( r3 * r3 * r3 * r3 * r3 );
   
   // Cook Torrance
   // https://ja.wikipedia.org/wiki/%E9%8F%A1%E9%9D%A2%E3%83%8F%E3%82%A4%E3%83%A9%E3%82%A4%E3%83%88
   float CT = Rt * D * G / NV;
   
   // ランバート拡散
   float lambert = max(0.0f, NL);
   
   return ( decalmap * lambert + CT ).bgra;
}

---CookTorrance.h---  ↑

#pragma once

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

class CookTorrance {
private:
   // Direct3D用定数バッファ設定用構造体
   typedef struct _CBUFFER {
      XMMATRIX matCameraWorldViewProj;
      XMFLOAT4 vecLightDir;
      XMFLOAT4 vecEyePos;
      //  表面材質の荒さ(反射光の分散率)
      // 0 < m <= 1 となりほとんど0の場合は分散しなくなる
      float fm;
      // フレネル項で使用する反射係数
      float fn2;
      float dummy3;
      float dummy4;

      _CBUFFER::_CBUFFER() {
         matCameraWorldViewProj = XMMatrixIdentity();
         vecEyePos = XMFLOAT4(0, 0, 0, 0);
         fm = 0.0f;
         fn2 = 0.0f;
      }
   }CBUFFER;

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

   UGraphicsPipeline* m_pGraphicsPipeline[1];

   DWORD m_Width, m_Height;

   int m_Pass;

   XMMATRIX m_MatCameraView;
   XMMATRIX m_MatCameraProj;
   XMFLOAT4 m_VecLightPos;

public:
   CookTorrance();
   virtual ~CookTorrance();
   void Invalidate();
   void Create(ID3D11Device* pD3DDevice, DWORD Width, DWORD Height);
   void BeginPass(ID3D11DeviceContext* pD3DDeviceContext, UINT Pass,
      XMFLOAT4* pVecLightPos, XMMATRIX* pMatCameraView, XMMATRIX* pMatCameraProj);
   void SetConstantBuffers(ID3D11DeviceContext* pD3DDeviceContext,
      XMMATRIX* pMatWorld,
      float m,
      float n2);
   void EndPass(ID3D11DeviceContext* pD3DDeviceContext);
   inline UINT GetMaxPass() { return 1; };
};

---CookTorrance.cpp---  ↑

#include "../../Header/Shader/CookTorrance.h"
#if !(defined(DEBUG) || defined(_DEBUG))
#include "../../HLSL/CookTorrance_VS_Main.h"
#include "../../HLSL/CookTorrance_PS_Main.h"
#endif

CookTorrance::CookTorrance() {
   for (int i = 0; i<_countof(m_pGraphicsPipeline); i++) {
      m_pGraphicsPipeline[i] = nullptr;
      m_pConstantBuffers[i] = nullptr;
   }

   Invalidate();
}

CookTorrance::~CookTorrance() {
   Invalidate();
}

void CookTorrance::Invalidate() {
   for (int i = 0; i<_countof(m_pGraphicsPipeline); i++) {
      SAFE_DELETE(m_pGraphicsPipeline[i]);
      SAFE_RELEASE(m_pConstantBuffers[i]);
   }

   m_Width = 0;
   m_Height = 0;
   m_MatCameraView = XMMatrixIdentity();
   m_MatCameraProj = XMMatrixIdentity();
   m_VecLightPos = XMFLOAT4(0, 0, 0, 0);
   m_Pass = -1;
}

void CookTorrance::Create(ID3D11Device* pD3DDevice, DWORD Width, DWORD Height) {
   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, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
      { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
   };

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

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

void CookTorrance::BeginPass(ID3D11DeviceContext* pD3DDeviceContext, UINT Pass,
   XMFLOAT4* pVecLightPos, XMMATRIX* pMatCameraView, XMMATRIX* pMatCameraProj) {

   if (m_Pass != -1) {
      throw(UException(-1, _T("CookTorrance::BeginPass()はCreate()またはEndPass()実行後に使用してください")));
   }

   m_Pass = (int)Pass;

   switch (m_Pass) {
   case 0:
      m_VecLightPos = *pVecLightPos;
      m_MatCameraView = (*pMatCameraView);
      m_MatCameraProj = (*pMatCameraProj);
      break;
   default:
      throw(UException(-1, _T("CookTorrance::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 CookTorrance::SetConstantBuffers(ID3D11DeviceContext* pD3DDeviceContext,
   XMMATRIX* pMatWorld,
   float m,
   float n2) {
   HRESULT hr = E_FAIL;

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

   switch (m_Pass) {
   case 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("CookTorrance::SetConstantBuffers()でエラーが発生しました。Map()が失敗しました。")));
      }

      CookTorrance::CBUFFER* cbuffer = (CookTorrance::CBUFFER*)mappedResource.pData;

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

      // ワールド行列の逆行列で平行光源の位置ベクトルを行列変換した値をセットする
      ::CopyMemory(&cbuffer->vecLightDir, &localLightPos, sizeof(XMFLOAT4));

      // カメラービューでのワールド×ビュー×射影行列をセットする
      XMMATRIX matCameraWorldViewProj;
      matCameraWorldViewProj = (*pMatWorld) * m_MatCameraView * m_MatCameraProj;
      matCameraWorldViewProj = XMMatrixTranspose(matCameraWorldViewProj);
      ::CopyMemory(&cbuffer->matCameraWorldViewProj, &matCameraWorldViewProj, sizeof(XMMATRIX));

      // カメラの位置ベクトルをセットする
      matInv = XMMatrixInverse(nullptr, (*pMatWorld) * m_MatCameraView);
      v = XMVector4Transform(XMLoadFloat4(&XMFLOAT4(0, 0, 0, 1)), matInv);
      ::CopyMemory(&cbuffer->vecEyePos, &v, sizeof(XMFLOAT4));

      // 表面材質の荒さ(反射光の分散率)
      cbuffer->fm = m;
      // フレネル項で使用する反射係数
      cbuffer->fn2 = n2;

      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 CookTorrance::EndPass(ID3D11DeviceContext*) {
   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"
#include "../Header/Shader/CookTorrance.h"
#include "../Header/Common/UDebugFont.h"
#include "../Header/Mesh/MonkeyMesh.h"

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

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

CookTorrance* p_CookTorrance = nullptr;

MonkeyMesh* g_pMonkeyMesh = nullptr;

UDebugFont* g_pDebugFont = nullptr;
UFPS* g_pFPS = nullptr;

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);

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

// メモリ解放
void Invalidate() {
   SAFE_DELETE(g_pFPS);
   SAFE_DELETE(g_pDebugFont);
   SAFE_DELETE(p_CookTorrance);
   SAFE_DELETE(g_pMonkeyMesh);
   SAFE_DELETE(g_pDirect3D11);
}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, UINT wParam, LONG lParam) {
   switch (msg) {
   case WM_KEYUP:
      switch (wParam) {
      case VK_ESCAPE:      // アプリ終了
         ::DestroyWindow(hWnd);
         break;
      }
      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;

   DXGI_MODE_DESC  ModeDesc;
   UINT num;
   bool find = false;

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

   pModeDescArray = NEW DXGI_MODE_DESC[num];

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

   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;
      }
   }

   SAFE_DELETE_ARRAY(pModeDescArray);

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

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

   g_pDebugFont->CreateMesh(g_pDirect3D11->m_pD3DDevice, 0.03f, 0.08f);

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

void CreateGraphicePipeline() {
   p_CookTorrance->Create(g_pDirect3D11->m_pD3DDevice, 1024, 1024);
}

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

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

   p_CookTorrance = NEW CookTorrance();

   g_pDebugFont = NEW UDebugFont();

   g_pFPS = NEW UFPS();

   // 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_pMonkeyMesh->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 < p_CookTorrance->GetMaxPass(); i++) {
      p_CookTorrance->BeginPass(g_pDirect3D11->m_pD3DDeviceContext, i, &g_VecLightPos, &g_MatCameraView, &g_MatCameraProj);

      // 定数バッファを設定
      p_CookTorrance->SetConstantBuffers(g_pDirect3D11->m_pD3DDeviceContext,
         g_pMonkeyMesh->GetMatWorld(),
         0.35f,
         0.1f);
      // レンダリング
      g_pMonkeyMesh->Render(g_pDirect3D11->m_pD3DDeviceContext);
      p_CookTorrance->EndPass(g_pDirect3D11->m_pD3DDeviceContext);
   }

   g_pDebugFont->Render(g_pDirect3D11->m_pD3DDeviceContext);
   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;
}

Prev Top Next
inserted by FC2 system