スポンサーサイト

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

DXライブラリでV.A.T.S.シェーダ

こんにちは、どんどんDOLから離れていくペチコです。
でもDOLがだいすきです。

矛盾してますね。


今回はV.A.T.S.シェーダ。
なにそれ?ってカンジですが他に呼び名がわからなかったもので。


V.A.T.S.とは3DゲームFallOutシリーズの用語で

V.A.T.S.
こういうレーダー的ななにかがビビッ!となってシュパー!って人が選択されるすごいのです。
うん、表現力の稚拙さに我ながら情けなくなってきます。

しかしてシェーダを作ろうと参考になりそうな情報を検索してもどう探していいかもわからない、
エッジや輪郭抽出で調べてもトゥーンシェーダのことばかり。
あちらは内側も縁取られますが、やりたいのは外側だけの輪郭検出なのです。

そこで3Dで考えないで2Dで考えたらと思い、ペチコ流でやってみました。
なので変なことしてても生暖かい目で見守ってください。


さて免罪符的な自己防衛がすんだところで解説。

まずライブラリのMakeScreenで描画用画面を用意して、
STEP1
こういうモデルを

STEP2
単色で描画します。

次にもう一つ描画用画面を用意して、
STEP3
先ほどの画像(2D)からラプラシアンフィルタでエッジを抽出します。
ラプラシアンフィルタについてはこちらのサイト様を参考にさせていただきました。

そこに最初の画像と走査線テクスチャを合成した画面をテクスチャとして
STEP4
モデル描画時に反映させます。


こんな感じになりました。



モデルが動いてもその都度外側を縁取るようになりました。
本家には色々劣ってますがなんとか「らしく」なったかな。
ゲームのキャラ選択などに使えそうです。
でもこんなめんどくさいことしなくて、輪郭なしでキャラに色を付けてもさほど変わりがないような気もしてきました。

ともあれせっかくなのでHLSLコード。
簡単な割にとても長いのでまた要点をば。

まずシェーダーを3つ使います。
・モデルを単色で塗るシェーダ(頂点シェーダ、ピクセルシェーダ)
・画像のエッジを抽出するシェーダ(ピクセルシェーダ)
・その画像とモデルを合成するシェーダ(頂点シェーダ、ピクセルシェーダ)

ああ、めんどくさい。


最初のシェーダはライティングも何もいらないので特に説明はいりませんね。
DXライブラリ様のサンプル「剛体メッシュのライティング無し描画」のピクセルシェーダ部分を

PS_OUTPUT PSOutput;
float4 Color = {0.6f,1.0f,0.7f,1.0f};
PSOutput.Color0 = Color;

こんな感じにしとけばよいと思います。

描画については

//過程用に二画面用意(画面サイズは各自調整)
int ScrennHandle1 = MakeScreen( 1024, 1024, FALSE ) ;
int ScreenHandle2 = MakeScreen( 1024, 1024, FALSE ) ;


//描画先を描画用画面1にセット
SetDrawScreen( ScreenHandle ) ;
ClearDrawScreen() ; //画面クリア
//描画先はべき乗の1:1サイズなのでアスペクト比を合わせる
SetCameraDotAspect( 768.0f / 1024.0f ) ;


と描画先を2画面作っておいてください。
画像サイズはべき乗が吉です。詳しくはライブラリ様のサイトをご参照ください。
描画先をセットした時は、カメラポジション等色々初期化されるのでその辺も見ておくとよいと思います。


これを単色塗りつぶしシェーダをセットしてモデルを描画、次が今回の要です。
DrawPrimitive2DToShaderで2D描画を行うのでプリミティブを先に用意しておきます。

	// 頂点データ
VERTEX2DSHADER Vert[6] ;
{
Vert[ 0 ].pos = VGet( 0.0f, 0.0f, 0.0f ) ;
Vert[ 1 ].pos = VGet( 1024.0f, 0.0f, 0.0f ) ;
Vert[ 2 ].pos = VGet( 0.0f, 1024.0f, 0.0f ) ;
Vert[ 3 ].pos = VGet( 1024.0f, 1024.0f, 0.0f ) ;
Vert[ 0 ].dif = GetColorU8( 255,255,255,255 ) ;
Vert[ 0 ].spc = GetColorU8( 0,0,0,0 ) ;
Vert[ 0 ].u = 0.0f ; Vert[ 0 ].v = 0.0f ;
Vert[ 1 ].u = 1.0f ; Vert[ 1 ].v = 0.0f ;
Vert[ 2 ].u = 0.0f ; Vert[ 2 ].v = 1.0f;//*(4.0f/3.0f) ;//確認用
Vert[ 3 ].u = 1.0f ; Vert[ 3 ].v = 1.0f;//*(4.0f/3.0f) ;
Vert[ 0 ].su = 0.0f ; Vert[ 0 ].sv = 0.0f ;
Vert[ 1 ].su = 1.0f ; Vert[ 1 ].sv = 0.0f ;
Vert[ 2 ].su = 0.0f ; Vert[ 2 ].sv = 1.0f;//*(4.0f/3.0f) ;
Vert[ 3 ].su = 1.0f ; Vert[ 3 ].sv = 1.0f;//*(4.0f/3.0f) ;
Vert[ 0 ].rhw = Vert[ 1 ].rhw = Vert[ 2 ].rhw = Vert[ 3 ].rhw = 1.0f ;

Vert[ 4 ] = Vert[ 2 ] ;
Vert[ 5 ] = Vert[ 1 ] ;
}


最初の4行が画面サイズになります。
ここは中途の画面なので1024x1024のべき乗でやります。
2つめのシェーダの時点で画面出力したい場合は『//確認』部分を有効にすればアスペクト比があって1024x768出力になります。

//描画先2つめ
SetDrawScreen( ScreenHandle2 ) ;
ClearDrawScreen() ; //画面クリア

// 使用するテクスチャをセット
SetUseTextureToShader( 0, ScreenHandle ) ;
SetUseTextureToShader( 1, TextureHandle ) ;

// 描画
DrawPrimitive2DToShader( Vert, 6, DX_PRIMTYPE_TRIANGLELIST ) ;


次の描画先(ScreenHandle2)をセットします。
ここには書いてませんが、2つめのシェーダもセットしてます。
テクスチャは先の単色で塗りつぶした画面と走査線テクスチャをサンプラに渡してます。
※DrawPrimitive2DToShaderで小さいテクスチャをラップさせて繰り返す方法がわからなかったので画面サイズと同じ走査線テクスチャを使いました

次が2つめのHLSLです。

VATS_2PS.fx


// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 Tex : TEXCOORD0 ; // テクスチャ
} ;

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

sampler DiffuseMapTexture : register( s0 ) ;// ディフューズマップテクスチャ
sampler DecalTexture : register( s2 ) ;// 走査線テクスチャ

//ラプラシアンフィルタ
float2 nextXY(float2 uv, float dx, float dy)
{
uv.x += dx/256;
uv.y += dy/256;
return uv;
}

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

float4 sum;
float2 Tex = PSInput.Tex;

//ラプラシアンフィルタ
sum = tex2D(DiffuseMapTexture, nextXY(Tex,-1,-1))
+ tex2D(DiffuseMapTexture, nextXY(Tex,0,-1))
+ tex2D(DiffuseMapTexture, nextXY(Tex,1,-1))
+ tex2D(DiffuseMapTexture, nextXY(Tex,-1,0))
+ tex2D(DiffuseMapTexture, nextXY(Tex,1,0))
+ tex2D(DiffuseMapTexture, nextXY(Tex,-1,1))
+ tex2D(DiffuseMapTexture, nextXY(Tex,0,1))
+ tex2D(DiffuseMapTexture, nextXY(Tex,1,1))
+ (tex2D(DiffuseMapTexture, Tex) * (-8.0f));

//輪郭に色を付ける用カラー
float4 Col = float4( 0.4f,0.7f,0.3f,1.0f );
//単色で塗りつぶした画面をデフューズテクスチャに
float4 TextureDiffuseColor = tex2D(DiffuseMapTexture, Tex);
//輪郭部分と元画像部分に走査線テクスチャを合成
PSOutput.Color0.rgb = (TextureDiffuseColor-sum.rgb)* Col * tex2D(DecalTexture, Tex);
PSOutput.Color0.a=1;//アルファは1

return PSOutput;
}

ピクセルシェーダだけなので頂点シェーダはありません。
ラプラシアンフィルタ部分は先に紹介しましたサイト様を丸パクリ参考にしました。

これで輪郭をもった単色の画像ができましたので、次でモデルのテクスチャとして使います。

//描画先を裏画面に
SetDrawScreen( DX_SCREEN_BACK ) ;

// テクスチャ0番をリセット
SetUseTextureToShader( 0, -1 ) ;

// 使用するテクスチャをセット
SetUseTextureToShader( 1, ScreenHandle2 ) ;


描画先を裏画面にして先の画面(ScreenHandle2)をテクスチャとしてサンプラ1番セットします。
0番はモデルのデフューズテクスチャが使用しますのでリセットしときます。
省いていますが、また初期化されたカメラの設定や3番目のシェーダのセットも行ってます。

VATS_3VS.fx


// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Pos : POSITION ; // ローカル
float3 Normal : NORMAL0 ; // ローカル
float2 Tex : TEXCOORD0 ; // テクスチャ座標
} ;

// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Pos : POSITION ; // 射影
float2 Tex : TEXCOORD0 ; // テクスチャ座標
float3 E : TEXCOORD1; // 視線ベクトル(ビュー)
float3 N : TEXCOORD2; // 法線(ビュー)
float4 PPos : TEXCOORD3 ; // 座標( 射影 )

} ;

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

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

// 視線ベクトル
float3 E = viewPosition;
VSOutput.E = E ;

// 法線
VSOutput.N = mul(mul(VSInput.Normal, (float3x3)g_World), (float3x3)g_View);

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

// 射影座標をテクスチャ座標0としても出力する
VSOutput.PPos = VSOutput.Pos ;

return VSOutput;
}


VATS_3PS.fx
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 Tex : TEXCOORD0 ; // テクスチャ
float3 E : TEXCOORD1; //視線(ビュー)
float3 N : TEXCOORD2; //法線(ビュー)
float4 PPos : TEXCOORD3 ; // 座標( 射影空間 )
} ;

// ピクセルシェーダーの出力
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 ;
float4 AT2_SpotP0_SpotP1 ;
} ;

// C++ 側で設定するテクスチャや定数の定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
sampler DecalTexture : register( s1 ) ; // デカール用テクスチャ(先に描画した画面2)

float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
LIGHT cfLight : register( c32 ) ; // ライトパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等

// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput;
float4 PPosN;
float2 TexCoord;
// PPos の xyz を w で割って非同次座標にする
PPosN = PSInput.PPos / PSInput.PPos.w ;
TexCoord.x = ( PPosN.x + 1.0f ) / 2.0f;
// yは更に上下反転
TexCoord.y = 1.0f - ( PPosN.y + 1.0f ) / 2.0f;

//視線ベクトルを正規化
float3 E = normalize( PSInput.E ) ;

//ライト方向ベクトル
float3 L = cfLight.Direction ;

// テクスチャ座標
float2 Tex = PSInput.Tex;

//法線
float3 N = PSInput.N ;

// ディフューズカラーとスペキュラカラーの蓄積値を初期化
float4 TotalDiffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
float4 TotalSpecular = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;

// DiffuseAngleGen = ディフューズ角度減衰率計算
float DiffuseAngleGen = saturate( dot( N, -L ) ) ;

// ディフューズカラー蓄積値 += ライトのディフューズカラー * マテリアルのディフューズカラー * ディフューズカラー角度減衰率 + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
TotalDiffuse += cfLight.Diffuse * cfMaterial.Diffuse * DiffuseAngleGen + cfLight.Ambient ;

// ハーフベクトルの計算
float3 H = normalize( E - L );
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
float Temp = pow( max( 0.0f, dot( N, H ) ), cfMaterial.Power.x ) ;

// スペキュラカラー蓄積値 += Temp * ライトのスペキュラカラー
TotalSpecular += Temp * cfLight.Specular ;

// TotalDiffuse = ライトディフューズカラー蓄積値 + ( マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの )
TotalDiffuse += cfAmbient_Emissive ;
// SpecularColor = ライトのスペキュラカラー蓄積値 * マテリアルのスペキュラカラー
float4 SpecularColor = TotalSpecular * cfMaterial.Specular ;
// 出力カラー = TotalDiffuse * テクスチャカラー + SpecularColor

//デフューズテクスチャとデカールテクスチャの合成
float4 TextureDiffuseColor = tex2D( DiffuseMapTexture, Tex ) ;
float4 TextureDecalColor = tex2D( DecalTexture, TexCoord ) ;
TextureDiffuseColor.rgb=TextureDiffuseColor.rgb*0.5+TextureDecalColor.rgb*0.8;

PSOutput.Color0.rgb = TextureDiffuseColor.rgb * TotalDiffuse.rgb + SpecularColor.rgb ;
// アルファ値 = テクスチャアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * cfMaterial.Diffuse.a * cfFactorColor.a ;

return PSOutput;
}


長くていやになりますが、フォンシェーダベースです。
違いは頂点シェーダで座標PPosをピクセルシェーダに渡しているところ、
ピクセルシェーダでそれを使ってごにょごにょした後、テクスチャ合成してる所だけです。
その辺はライブラリ様サンプルの10.3Dアクション基本+光学迷彩風モデル描画辺りに詳しいのでそちらをご参照のこと。

ちなみにペチコの元コードはちょっとごちゃごちゃしてたので、これを書きながら整理して記載しました。
つまり当然ノーチェックということで、色々とおかしな所あるかもしれません。

そういうのを見つけるのも楽しいですよね。そのりくつはおかしい


コメントの投稿

非公開コメント

プロフィール

ペチコートさん

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

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

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