スポンサーサイト

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

DXライブラリでオーシャンシェーダ

こんにちは、どんどんDOLから離れていくペチコです。
だって別のアカウント取るのめんどくさいんですもの。


前々回のペチコグをそろそろ浮かべてみようと思っています。

しかし、浮かべるということは海が必要です。
ということで、今回はシェーダで海(水面)の表現に挑戦してみたいと思います。

そんな課程をいちいちここに書くのはひとえにモチベーション維持のためです。
すぐにサボるんです。


とりあえず枡目上に配置したメッシュに

海テクスチャ

こういうテクスチャを乗っけて、どんなカンジになるか見てみます。
左からデフューズ、ノーマル、ハイトマップです。
効果がわかりやすいようにデフューズテクスチャは単色にしてます。


これを前回の視差マッピングで描画してみると

視差マッピング


うん微妙。


ノーマルマップの作りが悪いとか船のUVが破綻してるとか問題は多々ありますが、なにより動きがないのが致命的です。
これは静止画ですが、動画にしたとしてもこのまま動きません。波が立ってるのに落ちません。
なので技術を盗用するために色々と調べた結果、2枚のテクスチャを別々にスライドさせるとそれっぽいとの情報を得ました。

とはいえ今回のテクスチャは単色なので、ノーマルマップの山を動かしてみようと思います。

いめぇじ

イメージとしてはこのようなずらした塗り50%のノーマルマップを加算合成したものです。
テクスチャはラップしているので実際は図のような薄い部分はでないようになっています。

Tex.x += 0.5;
Tex.y += (sin(g_time*3+8)/512)+(g_time/32);

こんなカンジの適当計算をピクセルシェーダでやります。
Texは頂点シェーダから受け取ったものでxyよりuvと書いた方がしっくりきますね。
g_timeはアプリを起動してからの時間(ミリ秒)を定数レジスタで受け取るようにします。
テクスチャ座標が等速移動だとそっけないので、sin波で尺取り虫のような速度にしてます。
これを1枚目としてもう一つマイナス方向に動かしてやります。
物理的にどうなるとか全く考えてない(理解できない)のでそれっぽく。



こんなカンジに仕上がりました。
一瞬らしく見えるけど微妙ですね。
速度を変えたり横揺れも入れたりするともう少し良くなるのかもしれません。


これだけは物足りないのでさらに調べます。
ゲルストナー波とかいう用語が出てきました。なんか強そうな名称ですね。=理解できない
どこで見たか後で履歴から探しても見つかりませんでしたが、とりあえず揺れる水面のようなエフェクトがあったので実装してみました。



この動画だとよくわかりませんが水面がゆらゆらしています。
最初の効果切った方がわかりやすかったですね。

ついでに頂点シェーダにて頂点も上下させてみました。
ただのサイン波です。



海メッシュが細かければもっと見映えがよさそうです。
これは頂点ごと動かしてるのでモデルの方も動かしてやらないとダメですね。

残念ながらこのままでは使えないクオリティに仕上がってしまいましたが、せっかくなのでHLSL載せておきます。

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

// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Pos : POSITION ; // 射影
float2 Tex : TEXCOORD0 ; // テクスチャ座標
float3 L : TEXCOORD1; // ライト方向ベクトル(接空間)
float3 E : TEXCOORD2; // 視線ベクトル(接空間)
} ;

// C++ 側で設定する定数の定義
float4x4 g_World : register( c94 ) ; // ローカル→ワールド
float4x4 g_View : register( c6 ) ; // ワールド→ビュー
float4x4 g_Proj : register( c2 ) ; // ビュー→射影
float4 LightDirection : register( c15 ) ; //ライトの方向(ローカル)
float4 EyePos : register( c43 ) ; //視線ベクトル(ローカル)
float4 g_fTime : register( c0 ) ; //時間

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

float g_time = g_fTime.x;

//頂点は座標変換前に移動
VSInput.Pos.y = sin(VSInput.Pos.z + g_time*2)+4;
VSInput.Pos.z += sin((g_time*8)+(VSInput.Pos.y/4))/16;

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

// 視線ベクトルをローカル→接空間へ
float3 E = EyePos.xyz - VSInput.Pos.xyz;
VSOutput.E.x = dot( E, VSInput.Tan ) ;
VSOutput.E.y = dot( E, VSInput.Bin ) ;
VSOutput.E.z = dot( E, VSInput.Normal ) ;

// ライト方向ベクトルを接空間へ
float3 L = LightDirection.xyz;
VSOutput.L.x = dot( L, VSInput.Tan ) ;
VSOutput.L.y = dot( L, VSInput.Bin ) ;
VSOutput.L.z = dot( L, VSInput.Normal ) ;

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

return VSOutput;
}

OceanPS.fx
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 Tex : TEXCOORD0 ; // テクスチャ
float3 L : TEXCOORD1; //ライト方向(接空間)
float3 E : TEXCOORD2; //視線(接空間)
} ;

// ピクセルシェーダーの出力
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 NormalMapTexture : register( s1 ) ; // 法線マップテクスチャ
float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
LIGHT cfLight : register( c32 ) ; // ライトパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
float4 g_fTime : register( c0 ) ; // 時間

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

float g_time = g_fTime.x;

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

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

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

//ゆらゆら
float WaveM;
float2 WaveUV ,WaveMove ,DirU, DirV ,WaveDir ={ 1.0, 0.0 };

WaveUV.x = dot( WaveDir, Tex );
WaveUV.y = dot( -WaveDir.yx, Tex );
WaveM = fmod( (g_time + WaveUV.x * 5) * 6.28, 6.28 );
WaveMove.x = sin(WaveM) * 0.01 / 2;
WaveM = fmod( (g_time + WaveUV.y * 5) * 6.28, 6.28 );
WaveMove.y = sin(WaveM) * 0.01 / 2;

Tex += WaveMove;

//ノーマルマップ二重化計画
Tex.x += 0.5;
Tex.y += (sin(g_time*3+8)/512)+(g_time/32);
float h = tex2D( NormalMapTexture, Tex).a;
Tex = Tex + 0.016 * h * E.xy;
float3 N = 2.0f * tex2D( NormalMapTexture, Tex ).xyz - 1.0f;

Tex.x -= 0.5;
Tex.y -= ((sin(g_time*3+8)/512)+(g_time/32))*2.0f;
h = tex2D( NormalMapTexture, Tex ).a;
Tex = Tex + 0.016 * h * E.xy;
float3 N2 = 2.0f * tex2D( NormalMapTexture, Tex ).xyz - 1.0f;

N = ( N + N2 ) /2 ;

// ディフューズカラーとスペキュラカラーの蓄積値を初期化
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 ) ;

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

return PSOutput;
}

cppに追加
//メインループ外に
//時間カウント用
FLOAT4 g_fTime = {0.0f,0.0f,0.0f,0.0f} ;
int GameTime, OldTime;

//メインループ内に
int Time ;
// 現在の時間を得る
Time = GetNowCount() ;
// 今フレームで経過した時間を得る
GameTime = Time - OldTime ;
// 現在の時間を保存
OldTime = Time ;

//モデル描画前に
g_fTime.x += (float)GameTime / 1000.0f ;
SetVSConstF(0, g_fTime);
SetPSConstF(0, g_fTime);

//視線とライトは視差マッピングを参照してください


変数の命名規則が妙なのは色々試しながらやってたら頭痛くなって来たからです
Set○○ConstFで定義した定数は用途に応じてReset○○ConstFしといてください。

疲れたので説明は省きます。
どのみち改良しないと見映え的によろしくないと思います。
ペチコの役立たずとでも罵ってください。よろこびます(え


DXライブラリではシェーダを付属の"ShaderCompiler.exe"でコンパイルして使わなければいけません。
色々なシェーダを試したい時に毎回バッチファイルを書き換えるのが面倒だったので、バッチファイルメーカーを作りました。
需要はないと思いますがせっかく作ったので置いときます。

DX Shader BatchMaker ダウンロード

DXライブラリ付属の"ShaderCompiler.exe"と一緒に置いておけばドラッグアンドドロップでバッチファイルの作成から実行まで行ってくれます。
詳しい説明は中のreadmeを参考のこと。

ちかれた。


コメントの投稿

非公開コメント

プロフィール

ペチコートさん

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

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

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