スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

DXライブラリでランバートシェーダ

こんにちは。
前回の更新から4ヶ月も経っていることに驚きを隠せないペチコです

dolまだ続いているのでしょうか


久しぶりにシェーダで遊んでいました。
間が空く度に基本からやり直さないと思い出せない鳥頭ペチコなので
ライティング基本のLambert反射をやります

DXライブラリさんでいうところの「剛体メッシュのディレクショナルライトあり描画」ですね

ライブラリさんのサンプルをそのままなぞっても意味がないので、ライティング計算をピクセルシェーダに移しました

まずは頂点シェーダ
Lambert_VS.fx
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Pos : POSITION ; // ローカル
float2 Tex : TEXCOORD0 ; // テクスチャUV
float3 Normal : NORMAL0 ; // ローカル
} ;

// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Pos : POSITION ; // 射影
float2 Tex : TEXCOORD0 ; // テクスチャUV
float3 Norm : TEXCOORD1 ; // ビュー
} ;

// C++ 側で設定する定数の定義
float4x4 g_World : register( c94 ) ; // ローカル→ワールド
float4x4 g_View : register( c6 ) ; // ワールド→ビュー
float4x4 g_Proj : register( c2 ) ; // ビュー→射影

// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput;

//ローカル→ワールド→ビュー→射影変換
VSOutput.Pos = mul(mul(mul(VSInput.Pos, g_World), g_View), g_Proj);

// 法線をビューへ
float3 N = mul(VSInput.Normal,(float3x3)g_World);
VSOutput.Norm = mul(N,(float3x3)g_View);

// テクスチャUV
VSOutput.Tex = VSInput.Tex;

return VSOutput;
}

インデントずれを気にしてるとこの先生きのこれません

必要な情報はローカルから射影(スクリーン)空間へ変換した座標、法線をビュー空間に変換した座標、テクスチャのUV座標です
法線をビューに変換しているのはDXライブラリさんが用意してくださっているライトの方向がビュー空間にあるため
自前でライトを管理する場合はワールドで構いませんです

で、頂点シェーダで計算した座標をピクセルシェーダに持って行きます
Lambert_PS.fx
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 Tex : TEXCOORD0 ; // テクスチャUV
float3 Norm : TEXCOORD1 ; // 法線(ビュー)
} ;

// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;

// マテリアルパラメータ
struct MATERIAL
{
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Power ; // スペキュラの強さ
} ;

// ライトパラメータ
struct LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;

// C++ 側で設定するテクスチャや定数の定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
LIGHT cfLight : register( c32 ) ; // ライトパラメータ

// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput;

//法線とライト方向の正規化
float3 N = normalize( PSInput.Norm );
float3 L = normalize( cfLight.Direction );

//ライトの強さ
float P = dot(N,-L);
P = clamp( P,0,1 );
//色
float4 C = P * cfLight.Diffuse * cfMaterial.Diffuse + cfLight.Ambient + cfAmbient_Emissive;
//テクスチャ
float4 D = tex2D( DiffuseMapTexture, PSInput.Tex );

PSOutput.Color0.rgb = D * C.rgb;
PSOutput.Color0.a = cfMaterial.Diffuse.a * D.a * cfFactorColor.a ;

return PSOutput;
}

ピクセルシェーダは大体3.0でコンパイルしてます。2.0は制限キツイです

説明はほぼライブラリさんの通りなので省きます
多少はしょってるの部分があるので微妙に違う結果になるかもしれませんが、「らしく見えればいい」が簡略化の秘訣なのです
有名な話で、カプコンのバイオハザード4で主人公の車の窓への映り込みをプレステ2で表現するのは困難だったため、窓の外にあたかも反射しているかのように半透明の主人公モデルを立たせて「らしく見せてる」というものがありましたね

頂点シェーダで色計算を行う場合とピクセルシェーダで色計算を行う場合の違いですが、
例えば↓のようなモデルの場合
多面体
頂点シェーダは面単位で明暗が変化するため、頂点数の少ないポリゴンの場合面が粗く見えます
ピクセルシェーダで色計算を行う場合、その点は滑らかになりますが、負荷は頂点シェーダ版に比べて大体のシーンで大きくなります



頂点シェーダ版とピクセルシェーダ版どっちを使うかはシーンによりけりですね


追記:
テキトー過ぎて行列の順序とか考えてなかったせいでモデルによっては描画が変になっちゃいますね

cppでモデル描画前に
MATRIX MatWorld = MV1GetLocalWorldMatrix(描画するモデルのハンドル);
MATRIX Camera_ViewMatrix = GetCameraViewMatrix() ;
MATRIX Camera_ProjectionMatrix = GetCameraProjectionMatrix() ;
SetVSConstFMtx(94,MatWorld);
SetVSConstFMtx(6,Camera_ViewMatrix);
SetVSConstFMtx(2,Camera_ProjectionMatrix);


頂点シェーダの行列変換部分を
VSOutput.Pos = mul(mul(mul(g_Proj, g_View), g_World), VSInput.Pos);
//法線
VSOutput.Norm = mul(mul(g_View, g_World), VSInput.Normal);

に追記および変更で正常になる、はずです。。行列とか全然分かってないです!(きっぱり)
大人しくライブラリ様の通りにしろってカンジですね

コメントの投稿

非公開コメント

プロフィール

ペチコートさん

Author:ペチコートさん
大航海時代オンライン
Zephyros、Eurosサーバーに潜伏中

偽ペチコ1号・2号
ペチコの手下達
Zephyrosサーバーに潜伏中

最新記事
月別アーカイブ
カテゴリ
リンク
最新コメント
検索フォーム
QRコード
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。