DXライブラリでライティングなしのシェーダーのコードを書きます。DirectX11版。光源などの影響を考えずマテリアル・テクスチャの色をそのまま出すシェーダーです。
※※以降のシェーダーの土台となるので重要です※※
シェーダーそのものの作り方や読み込み方はこちらの記事にて。
頂点シェーダーとピクセルシェーダー
シェーダーを作っていく際に頂点シェーダーとピクセルシェーダーの2つを作っていくことになる。
頂点シェーダーは3Dモデルの全頂点に対して実行されるプログラムで、3Dモデルやライトのデータを受け取ってビュー座標(プロジェクション座標、スクリーン空間とも)に変換してピクセルシェーダーに渡すのが主な役割。
一方のピクセルシェーダーは各ピクセルに対して実行されるプログラムで、頂点シェーダーからのデータをもとに各ピクセルの色を出力するのが主な役割。
色の算出について、初代Play StationやNintendo64の時代だと頂点シェーダー内でライティング処理を行い色を算出していた。当時のハードだとスペックが低かったため、処理負荷を減らすために頂点シェーダーで色を算出していた。ハードのスペックが上がっている現在だとピクセルシェーダーでライティング処理をして色を算出するのが標準となっている。
ライティングなしのシェーダーサンプルコード
それではライティングなしシェーダーのサンプルコードをペタリ。記事冒頭に書いてある通りDirectX11版で使用言語はHLSLです。
また3Dモデルにボーン(アーマチュア)が埋め込まれていてオブジェクトとペアレントされている(ウエイトは不要)前提です。ボーンがないと表示がおかしくなります。
頂点シェーダー
まずはシェーダで必要な情報(3Dモデル、ライトなど)を受け取る。
//頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION; // 座標( ローカル空間 )
float3 Normal : NORMAL0; // 法線( ローカル空間 )
float4 Diffuse : COLOR0; // ディフューズカラー
float4 Specular : COLOR1; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0; // テクスチャ座標
float4 TexCoords1 : TEXCOORD1; // サブテクスチャ座標
int4 BlendIndices0 : BLENDINDICES0; // スキニング処理用 Float型定数配列インデックス
float4 BlendWeight0 : BLENDWEIGHT0; // スキニング処理用ウエイト値
int4 BlendIndices1 : BLENDINDICES1; // スキニング処理用 Float型定数配列インデックス
float4 BlendWeight1 : BLENDWEIGHT1; // スキニング処理用ウエイト値
};
//頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Diffuse : COLOR0; // ディフューズカラー
float4 Specular : COLOR1; // スペキュラカラー
float2 TexCoords0 : TEXCOORD0; // テクスチャ座標
float4 Position : SV_POSITION; // 座標( プロジェクション空間 )
};
// 基本パラメータ
struct DX_D3D11_VS_CONST_BUFFER_BASE
{
float4 AntiViewportMatrix[4]; // アンチビューポート行列
float4 ProjectionMatrix[4]; // ビュー → プロジェクション行列
float4 ViewMatrix[3]; // ワールド → ビュー行列
float4 LocalWorldMatrix[3]; // ローカル → ワールド行列
float4 ToonOutLineSize; // トゥーンの輪郭線の大きさ
float DiffuseSource; // ディフューズカラー( 0.0f:マテリアル 1.0f:頂点 )
float SpecularSource; // スペキュラカラー( 0.0f:マテリアル 1.0f:頂点 )
float MulSpecularColor; // スペキュラカラー値に乗算する値( スペキュラ無効処理で使用 )
float Padding;
};
// その他の行列
struct DX_D3D11_VS_CONST_BUFFER_OTHERMATRIX
{
float4 ShadowMapLightViewProjectionMatrix[3][4]; // シャドウマップ用のライトビュー行列とライト射影行列を乗算したもの
float4 TextureMatrix[3][2]; // テクスチャ座標操作用行列
};
// スキニングメッシュ用の ローカル → ワールド行列
struct DX_D3D11_VS_CONST_BUFFER_LOCALWORLDMATRIX
{
float4 Matrices[54 * 3]; // ローカル → ワールド行列
};
// 基本パラメータ
cbuffer cbD3D11_CONST_BUFFER_VS_BASE : register(b1)
{
DX_D3D11_VS_CONST_BUFFER_BASE g_Base;
};
// その他の行列
cbuffer cbD3D11_CONST_BUFFER_VS_OTHERMATRIX : register(b2)
{
DX_D3D11_VS_CONST_BUFFER_OTHERMATRIX g_OtherMatrix;
};
// スキニングメッシュ用の ローカル → ワールド行列
cbuffer cbD3D11_CONST_BUFFER_VS_LOCALWORLDMATRIX : register(b3)
{
DX_D3D11_VS_CONST_BUFFER_LOCALWORLDMATRIX g_LocalWorldMatrix;
};
DXライブラリ本家によるとこれらの変数の並び順は重要らしく、勝手に入れ替えたりすると3Dモデルの描画が正確に行われなくなるのでいじらないこと。
続けて頂点シェーダーのメイン関数部分。
//main
VS_OUTPUT main(VS_INPUT VSInput)
{
VS_OUTPUT VSOutput;
float4 lLocalWorldMatrix[3];
float4 lWorldPosition;
float4 lViewPosition;
// 複数のフレームのブレンド行列の作成
lLocalWorldMatrix[0] = g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.x + 0] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[1] = g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.x + 1] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[2] = g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.x + 2] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[0] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.y + 0] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[1] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.y + 1] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[2] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.y + 2] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[0] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.z + 0] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[1] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.z + 1] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[2] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.z + 2] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[0] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.w + 0] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[1] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.w + 1] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[2] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices0.w + 2] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[0] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.x + 0] * VSInput.BlendWeight1.x;
lLocalWorldMatrix[1] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.x + 1] * VSInput.BlendWeight1.x;
lLocalWorldMatrix[2] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.x + 2] * VSInput.BlendWeight1.x;
lLocalWorldMatrix[0] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.y + 0] * VSInput.BlendWeight1.y;
lLocalWorldMatrix[1] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.y + 1] * VSInput.BlendWeight1.y;
lLocalWorldMatrix[2] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.y + 2] * VSInput.BlendWeight1.y;
lLocalWorldMatrix[0] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.z + 0] * VSInput.BlendWeight1.z;
lLocalWorldMatrix[1] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.z + 1] * VSInput.BlendWeight1.z;
lLocalWorldMatrix[2] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.z + 2] * VSInput.BlendWeight1.z;
lLocalWorldMatrix[0] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.w + 0] * VSInput.BlendWeight1.w;
lLocalWorldMatrix[1] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.w + 1] * VSInput.BlendWeight1.w;
lLocalWorldMatrix[2] += g_LocalWorldMatrix.Matrices[ VSInput.BlendIndices1.w + 2] * VSInput.BlendWeight1.w;
//頂点座標変換(開始)------------
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot(VSInput.Position, lLocalWorldMatrix[0]);
lWorldPosition.y = dot(VSInput.Position, lLocalWorldMatrix[1]);
lWorldPosition.z = dot(VSInput.Position, lLocalWorldMatrix[2]);
lWorldPosition.w = 1.0f;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot(lWorldPosition, g_Base.ViewMatrix[0]);
lViewPosition.y = dot(lWorldPosition, g_Base.ViewMatrix[1]);
lViewPosition.z = dot(lWorldPosition, g_Base.ViewMatrix[2]);
lViewPosition.w = 1.0f;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot(lViewPosition, g_Base.ProjectionMatrix[0]);
VSOutput.Position.y = dot(lViewPosition, g_Base.ProjectionMatrix[1]);
VSOutput.Position.z = dot(lViewPosition, g_Base.ProjectionMatrix[2]);
VSOutput.Position.w = dot(lViewPosition, g_Base.ProjectionMatrix[3]);
//頂点座標変換(終了)------------
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot(VSInput.TexCoords0, g_OtherMatrix.TextureMatrix[0][0]);
VSOutput.TexCoords0.y = dot(VSInput.TexCoords0, g_OtherMatrix.TextureMatrix[0][1]);
//出力パラメータセット(終了)------------
//出力パラメーターを返す
return VSOutput;
}
複数のフレームのブレンド行列~の部分はボーン(アーマチュア)が動いた時のオブジェクトへの影響を計算するもの。これがあって3Dモデルが正しく表示される。
次に3Dモデルの各頂点をローカル座標からビュー座標に変換する。これをすることでどこのピクセルに3Dモデルを表示すればいいかがわかるようになる。
最後にテクスチャ座標の変換を行い、ピクセルシェーダーに情報を渡す。
ピクセルシェーダー
頂点シェーダーのデータをもとに各ピクセルの色を算出して出力する。
// ピクセルシェーダーの入力
struct PS_INPUT
{
float4 Diffuse : COLOR0; // ディフューズカラー
float4 Specular : COLOR1; // スペキュラカラー
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : SV_TARGET0 ; // 色
} ;
// マテリアルパラメータ
struct DX_D3D11_CONST_MATERIAL
{
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient_Emissive ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float Power ; // スペキュラの強さ
float TypeParam0 ; // マテリアルタイプパラメータ0
float TypeParam1 ; // マテリアルタイプパラメータ1
float TypeParam2 ; // マテリアルタイプパラメータ2
} ;
// フォグパラメータ
struct DX_D3D11_VS_CONST_FOG
{
float LinearAdd ; // フォグ用パラメータ end / ( end - start )
float LinearDiv ; // フォグ用パラメータ -1 / ( end - start )
float Density ; // フォグ用パラメータ density
float E ; // フォグ用パラメータ 自然対数の低
float4 Color ; // カラー
} ;
// ライトパラメータ
struct DX_D3D11_CONST_LIGHT
{
int Type ; // ライトタイプ( DX_LIGHTTYPE_POINT など )
int3 Padding1 ; // パディング1
float3 Position ; // 座標( ビュー空間 )
float RangePow2 ; // 有効距離の2乗
float3 Direction ; // 方向( ビュー空間 )
float FallOff ; // スポットライト用FallOff
float3 Diffuse ; // ディフューズカラー
float SpotParam0 ; // スポットライト用パラメータ0( cos( Phi / 2.0f ) )
float3 Specular ; // スペキュラカラー
float SpotParam1 ; // スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float Attenuation0 ; // 距離による減衰処理用パラメータ0
float Attenuation1 ; // 距離による減衰処理用パラメータ1
float Attenuation2 ; // 距離による減衰処理用パラメータ2
float Padding2 ; // パディング2
} ;
// ピクセルシェーダー・頂点シェーダー共通パラメータ
struct DX_D3D11_CONST_BUFFER_COMMON
{
DX_D3D11_CONST_LIGHT Light[ 6 ] ; // ライトパラメータ
DX_D3D11_CONST_MATERIAL Material ; // マテリアルパラメータ
DX_D3D11_VS_CONST_FOG Fog ; // フォグパラメータ
} ;
// 定数バッファピクセルシェーダー基本パラメータ
struct DX_D3D11_PS_CONST_BUFFER_BASE
{
float4 FactorColor ; // アルファ値等
float MulAlphaColor ; // カラーにアルファ値を乗算するかどうか( 0.0f:乗算しない 1.0f:乗算する )
float AlphaTestRef ; // アルファテストで使用する比較値
float2 Padding1 ;
int AlphaTestCmpMode ; // アルファテスト比較モード( DX_CMP_NEVER など )
int3 Padding2 ;
float4 IgnoreTextureColor ; // テクスチャカラー無視処理用カラー
} ;
// 頂点シェーダー・ピクセルシェーダー共通パラメータ
cbuffer cbD3D11_CONST_BUFFER_COMMON : register( b0 )
{
DX_D3D11_CONST_BUFFER_COMMON g_Common ;
} ;
// 基本パラメータ
cbuffer cbD3D11_CONST_BUFFER_PS_BASE : register( b1 )
{
DX_D3D11_PS_CONST_BUFFER_BASE g_Base ;
} ;
// C++ 側で設定するテクスチャや定数の定義
SamplerState g_DiffuseMapSampler : register( s0 ) ; // ディフューズマップテクスチャ
Texture2D g_DiffuseMapTexture : register( t0 ) ; // ディフューズマップテクスチャ
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput;
float4 TextureDiffuseColor;
// テクスチャカラーの読み込み
TextureDiffuseColor = g_DiffuseMapTexture.Sample(g_DiffuseMapSampler, PSInput.TexCoords0);
// 出力カラー = テクスチャカラー
PSOutput.Color0.rgb = saturate(TextureDiffuseColor.rgb * g_Common.Material.Diffuse.xyz);
// 出力アルファ = テクスチャアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * g_Base.FactorColor.a;
// 出力パラメータを返す
return PSOutput;
}
ライティング処理なしということでマテリアルまたはテクスチャカラーをそのままべったり出力している。当然ながらスペキュラーカラーとかアンビエントカラーとかも計算していない。
出力カラーを算出する際に使っている saturate() は0.0f~1.0fの間に値をクリップする関数。これがないと出力カラーが変になる場合があるので使っている。
この2つのシェーダーを出力して読み込んだ場合、3Dモデルの見た目は SetUseLighting() でFALSEを指定したときと同じ見た目になる(はず)。
HLSLシェーダーについてきちんと学びたい人はこの本を買うといいです。

コメント