スポンサーサイト

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

DXライブラリでアフターブルームシェーダ

こんにちは
スカイリム地方からTDU2のイビサ島にバカンスに来ているペチコです


TDU2
あ、ペチタング趣味悪いですか
ショッキングピンクじゃないだけマシだと思いたいですそう?


今回はアフターブルームシェーダ
「残光」で調べたらafterbloomって出てきたので勝手に名付けました
↓こんなカンジでブルームの光が残像となって尾を引くものを目指しました




必要なスクリーンは
通常描画用(1024x768)←出力画面サイズ
ガウスぼかし用2つ(128x128)←出力の1/8サイズ
残像用前フレーム(128x128)
の4つです

取り敢えずコードです。長いです
AfterBloom.cpp
#include "DxLib.h"

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
//ウインドウモード
ChangeWindowMode( TRUE ) ;
//アンチエイリアス
//SetFullSceneAntiAliasingMode( 4, 2 ) ;

// DXライブラリの初期化
if( DxLib_Init() < 0 ) return -1;

// 画面モードの変更
SetGraphMode( 1024 , 768 , 32 ) ;

//カメラポジション
VECTOR CameraPos ;
// カメラの座標を初期化
CameraPos.x = 0.0f ;
CameraPos.y = 0.0f ;
CameraPos.z = -3.0f ;

//モデル構造体
struct MODEL_Main
{
float x ;
float y ;
float z ;
float RX; //Y軸回転
float RY; //Y軸回転
} ;
MODEL_Main mdl ; // モデル情報の実体宣言
mdl.x = 0.0f ;
mdl.y = 0.0f ;
mdl.z = 0.0f ;
mdl.RX = 0.0f ;
mdl.RY = 0.0f ;

//マウス用
int MouseX , MouseY ;
int MouseX_old , MouseY_old ;

// シェーダー
//通常描画用
int Handle_1VS = LoadVertexShader( "Bloom_1VS.vso" ) ;
int Handle_1PS = LoadPixelShader( "Bloom_1PS.pso" ) ;
//レベル補正用
int Handle_2PS = LoadPixelShader( "Bloom_2PS.pso" ) ;
//ガウス用
int Handle_GaussianHPS = LoadPixelShader( "GaussianHPS.pso" ) ;
int Handle_GaussianVPS = LoadPixelShader( "GaussianVPS.pso" ) ;
//残像合成用
int Handle_3PS = LoadPixelShader( "Bloom_3PS.pso" ) ;

//モデル読み込み
int ModelHandle_1 = MV1LoadModel( "amulet.mv1" ) ;

//描画用スクリーン
SetDrawValidMultiSample( 8, 8 ) ; //アンチエイリアス
int Screen1 = MakeScreen( 1024, 1024, FALSE ) ; //通常描画用
SetDrawValidMultiSample( 0, 0 ) ; //アンチエイリアスは通常描画のみ

int Screen2 = MakeScreen( 128, 128, FALSE) ; //縮小描画用
int Screen3 = MakeScreen( 128, 128, FALSE) ; //ブラー用

int Screen_old = MakeScreen( 128, 128, FALSE) ; //前フレーム用

// 2D描画用の頂点データ
VERTEX2DSHADER Vert_Small[6] ;
{
Vert_Small[ 0 ].pos = VGet( 0.0f, 0.0f, 0.0f ) ;
Vert_Small[ 1 ].pos = VGet( 128.0f, 0.0f, 0.0f ) ;
Vert_Small[ 2 ].pos = VGet( 0.0f, 128.0f, 0.0f ) ;
Vert_Small[ 3 ].pos = VGet( 128.0f, 128.0f, 0.0f ) ;
Vert_Small[ 0 ].dif = GetColorU8( 255,255,255,255 ) ;
Vert_Small[ 0 ].spc = GetColorU8( 0,0,0,0 ) ;
Vert_Small[ 0 ].u = 0.0f ; Vert_Small[ 0 ].v = 0.0f ;
Vert_Small[ 1 ].u = 1.0f ; Vert_Small[ 1 ].v = 0.0f ;
Vert_Small[ 2 ].u = 0.0f ; Vert_Small[ 2 ].v = 1.0f;
Vert_Small[ 3 ].u = 1.0f ; Vert_Small[ 3 ].v = 1.0f;
Vert_Small[ 0 ].su = 0.0f ; Vert_Small[ 0 ].sv = 0.0f ;
Vert_Small[ 1 ].su = 1.0f ; Vert_Small[ 1 ].sv = 0.0f ;
Vert_Small[ 2 ].su = 0.0f ; Vert_Small[ 2 ].sv = 1.0f;
Vert_Small[ 3 ].su = 1.0f ; Vert_Small[ 3 ].sv = 1.0f;
Vert_Small[ 0 ].rhw = 1.0f ; Vert_Small[ 1 ].rhw = 1.0f ;
Vert_Small[ 2 ].rhw = 1.0f ; Vert_Small[ 3 ].rhw = 1.0f ;
Vert_Small[ 4 ] = Vert_Small[ 2 ] ; Vert_Small[ 5 ] = Vert_Small[ 1 ] ;
}

VERTEX2DSHADER Vert_Large[6] ;
{
Vert_Large[ 0 ].pos = VGet( 0.0f, 0.0f, 0.0f ) ;
Vert_Large[ 1 ].pos = VGet( 1024.0f, 0.0f, 0.0f ) ;
Vert_Large[ 2 ].pos = VGet( 0.0f, 768.0f, 0.0f ) ;
Vert_Large[ 3 ].pos = VGet( 1024.0f, 768.0f, 0.0f ) ;
Vert_Large[ 0 ].dif = GetColorU8( 255,255,255,255 ) ;
Vert_Large[ 0 ].spc = GetColorU8( 0,0,0,0 ) ;
Vert_Large[ 0 ].u = 0.0f ; Vert_Large[ 0 ].v = 0.0f ;
Vert_Large[ 1 ].u = 1.0f ; Vert_Large[ 1 ].v = 0.0f ;
Vert_Large[ 2 ].u = 0.0f ; Vert_Large[ 2 ].v = 1.0f ;
Vert_Large[ 3 ].u = 1.0f ; Vert_Large[ 3 ].v = 1.0f ;
Vert_Large[ 0 ].su = 0.0f ; Vert_Large[ 0 ].sv = 0.0f ;
Vert_Large[ 1 ].su = 1.0f ; Vert_Large[ 1 ].sv = 0.0f ;
Vert_Large[ 2 ].su = 0.0f ; Vert_Large[ 2 ].sv = 1.0f ;
Vert_Large[ 3 ].su = 1.0f ; Vert_Large[ 3 ].sv = 1.0f ;
Vert_Large[ 0 ].rhw = 1.0f ; Vert_Large[ 1 ].rhw = 1.0f ;
Vert_Large[ 2 ].rhw = 1.0f ; Vert_Large[ 3 ].rhw = 1.0f ;
Vert_Large[ 4 ] = Vert_Large[ 2 ] ; Vert_Large[ 5 ] = Vert_Large[ 1 ] ;
}

// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// マウスの位置を取得
GetMousePoint( &MouseX , &MouseY ) ;

//マウス
if( ( GetMouseInput() & MOUSE_INPUT_LEFT ) != 0 )
{
// 左
mdl.x -= (float)(MouseX_old - MouseX)*0.01f;
mdl.y += (float)(MouseY_old - MouseY)*0.01f;
}else if( ( GetMouseInput() & MOUSE_INPUT_RIGHT ) != 0 )
{
// 右
mdl.RX += (float)(MouseY_old - MouseY)*0.5f;
mdl.RY += (float)(MouseX_old - MouseX)*0.5f;
}
mdl.z -= GetMouseWheelRotVol() * 0.5f;

//マウス位置記憶
MouseX_old = MouseX;
MouseY_old = MouseY;

//シェーダ使用
MV1SetUseOrigShader( TRUE ) ;

//////////////////////////////////////////////////
// 描画先を描画用画面にする
SetDrawScreen( Screen1 ) ;
// 画面を初期化
ClearDrawScreen() ;

//カメラ位置
SetCameraPositionAndTarget_UpVecY( CameraPos, VGet( 0.0f, 0.0f, 0.0f ) ) ;
SetCameraNearFar( 0.1f, 1000.0f ) ;
//カメラのアスペクト比
SetCameraDotAspect( 768.0f / 1024.0f ) ;
//モデル位置
MV1SetRotationXYZ( ModelHandle_1, VGet( mdl.RX * DX_PI_F / 180.0f, mdl.RY * DX_PI_F / 180.0f, 0.0f ) ) ;
MV1SetPosition( ModelHandle_1, VGet( mdl.x, mdl.y, mdl.z ) ) ;

// 使用するシェーダをセット
SetUseVertexShader( Handle_1VS ) ;
SetUsePixelShader( Handle_1PS ) ;

// モデルを描画
MV1DrawModel( ModelHandle_1 ) ;

//////////////////////////////////////////////////
// 描画先をレベル補正用画面にする
SetDrawScreen( Screen2 ) ;
// 画面を初期化
ClearDrawScreen() ;

//カメラ位置
SetCameraPositionAndTarget_UpVecY( CameraPos, VGet( 0.0f, 0.0f, 0.0f ) ) ;
SetCameraNearFar( 0.1f, 1000.0f ) ;
//カメラのアスペクト比
SetCameraDotAspect( 768.0f / 1024.0f ) ;

// 使用するシェーダをセット
SetUseVertexShader( Handle_1VS ) ;
SetUsePixelShader( Handle_2PS ) ;

// モデルを描画
MV1DrawModel( ModelHandle_1 ) ;

//////////////////////////////////////////////////
//ガウスフィルタは複数回掛ける
for(int i=0;i<3;i++){
// 描画先をブラー用にする
SetDrawScreen( Screen3 ) ;

// 使用するホリゾンタルシェーダをセット
SetUsePixelShader( Handle_GaussianHPS ) ;

//レジスタ0にレベル補正画面をセット
SetUseTextureToShader( 0, Screen2 ) ;

//DrawPrimitive2DToShaderでシェーダ描画
DrawPrimitive2DToShader( Vert_Small, 6, DX_PRIMTYPE_TRIANGLELIST ) ;

//レジスタにセットしたテクスチャを解除
SetUseTextureToShader( 0, -1 ) ;

//////////////////////////////////////////////
// 描画先をレベル補正用にする(再利用)
SetDrawScreen( Screen2 ) ;

// 使用するバーティカルシェーダをセット
SetUsePixelShader( Handle_GaussianVPS ) ;

//レジスタ0に今ぼかしたガウス画面をセット
SetUseTextureToShader( 0, Screen3 ) ;

//DrawPrimitive2DToShaderでシェーダ描画
DrawPrimitive2DToShader( Vert_Small, 6, DX_PRIMTYPE_TRIANGLELIST ) ;

//レジスタにセットしたテクスチャを解除
SetUseTextureToShader( 0, -1 ) ;
}

//////////////////////////////////////////////////
//前フレームと合成
// 描画先をブラー用にする(再利用)
SetDrawScreen( Screen3 ) ;
// 画面を初期化
ClearDrawScreen() ;

// 使用するシェーダをセット
SetUsePixelShader( Handle_3PS ) ;

//レジスタ0にぼかした画面をセット
SetUseTextureToShader( 0, Screen2 ) ;
//レジスタ1に前フレームをセット
SetUseTextureToShader( 1, Screen_old ) ;

//DrawPrimitive2DToShaderでシェーダ描画
DrawPrimitive2DToShader( Vert_Small, 6, DX_PRIMTYPE_TRIANGLELIST ) ;

//レジスタにセットしたテクスチャを解除
SetUseTextureToShader( 0, -1 ) ;
SetUseTextureToShader( 1, -1 ) ;

//前フレームに現在の画面をコピー
GetDrawScreenGraph( 0 , 0 , 128 , 128 , Screen_old ) ;

//////////////////////////////////////////////////
// 描画モードをバイリニアに
SetDrawMode( DX_DRAWMODE_BILINEAR ) ;

// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// 画面を初期化
ClearDrawScreen() ;

// 使用するシェーダをセット
//SetUsePixelShader( Handle_3PS ) ;//使い回し

//レジスタ0に通常画面をセット
SetUseTextureToShader( 0, Screen1 ) ;
//レジスタ1に最終合成画面をセット
SetUseTextureToShader( 1, Screen3 ) ;

//DrawPrimitive2DToShaderでシェーダ描画
DrawPrimitive2DToShader( Vert_Large, 6, DX_PRIMTYPE_TRIANGLELIST ) ;

//レジスタにセットしたテクスチャを解除
SetUseTextureToShader( 0, -1 ) ;
SetUseTextureToShader( 1, -1 ) ;

// 描画モードを二アレストに戻す
SetDrawMode( DX_DRAWMODE_NEAREST ) ;

ScreenFlip() ;

}

// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle_1 ) ;

// 読み込んだピクセルシェーダーの削除
InitShader() ;

// DXライブラリの後始末
DxLib_End();

// ソフトの終了
return 0;
}

インデントずれもさることながら改行が意図通りに反映されてないのでさらに見難いです

コードとは関係ありませんが、なぜかタブを切り替えてショートカットをコピーするとこの記事が勝手にリロードされるという不運に見舞われ今回で3回目の書き直しです
もう詳しく解説する気力を絶たれましたので適当簡単な流れをば
コード途中の///////////////////////で区切ってる所が各描画処理です

1.通常描画用画面に通常描画。ここでは手抜きランバートを使ってます
2.ぼかし用画面にレベル補正で高輝度部分を抽出しつつ描画
3.ガウスフィルタの縦横で数回ぼかします
4.その画面を前フレームと合成。前フレームに結果をコピー
5.通常描画用画面と合成
です

3以降は2Dでピクセルシェーダのみ使用なのでDrawPrimitive2DToShader用に2つ頂点データを作成してます
VERTEX2DSHADER Vert_Small[6] ;
VERTEX2DSHADER Vert_Large[6] ;

最近はめんどくさくてべき乗テクスチャを使ってなかったので、今回は真面目にべき乗テクスチャ、縮小ぼかしを使いました
アスペクト比を考えだすと頭こんがらがるのでニガテです
コードでは画面サイズとか比率を決め打ちでやってますが、実際はもっと柔軟にした方がいいですね

以下HLSLと説明

1.の通常描画は手抜きランバートなのではしょります
フォンでもなんでも好きなので描画しましょう


2.は1.の頂点シェーダを使い回して、ランバートベースのレベル補正を掛けて、ぼかし用画面1(128x128)に描画してます
Bloom_2PS.pso
//(元の色*255-黒)/(白-黒)
PSOutput.Color0.rgb = ( (D * C.rgb) *255 - 80.0f ) / (120.0f - 80.0f);
PSOutput.Color0.a = cfMaterial.Diffuse.a * cfFactorColor.a ;

return PSOutput;

なので頂点シェーダはランバートと共通、ピクセルシェーダはランバート用に↑を足しました
ここでは通常描画用にBloom_1VS.vso,Bloom_1PS.pso
レベル補正用にBloom_2PS.psoとしてます(中身はランバートのピクセルシェーダに↑を足したもの)
数値は毎度手抜きの決め打ちなので通常はシーンにあったものをレジスタで渡すようにした方が汎用性があっていいです

例えば宝石だけを光らせたい、目だけを光らせたいという場合は、ここでその部分のみを描画するといいでしょう


3.のぼかし部分は前のグローと同じなので省略
2.で128x128の小っちゃいぼかし用画面に描画されたので、もう一つのぼかし用画面を使って、ガウスフィルタ縦→ガウスフィルタ横を複数回繰り返してます
画面が小っちゃいので結構な回数掛けても速度低下はあまりないと思います


4.で前フレームのブルームと合成
コード中では
//前フレームと合成
// 描画先をブラー用にする(再利用)
とコメントしてる部分です
Bloom_3PS.pso
sampler  DiffuseMapTexture        : register( s0 ) ;		// ディフューズマップテクスチャ
sampler DecalMapTexture : register( s1 ) ; // デカールテクスチャ

// main関数
float4 main( float2 Tex : TEXCOORD0) : COLOR0
{
float4 D = tex2D(DiffuseMapTexture, Tex);
float4 C = tex2D(DecalMapTexture, Tex) * 0.90f;

return D * (1 - saturate(C)) + C;
}

とっても短いです
レジスタ0にぼかした画面、レジスタ1に前フレームの画面を渡して加算合成してます
float4 C = tex2D(DecalMapTexture, Tex) * 0.90f;
とすることで徐々にアルファが減っていき、残像効果が出ます。この数値を大きくすれば長く尾を引くようになります(ついでに少し明るくもなりますが)

cpp中の
//前フレームに現在の画面をコピー
GetDrawScreenGraph( 0 , 0 , 128 , 128 , Screen_old ) ;
ここで今合成してできた画面をScreen_oldにコピーしてます。これは次のフレームで前フレームとして使います


5.で通常描画した画面と4.でできたブルームの合成です
めんどくさいので4.のピクセルシェーダの使い回しです(Handle_3PS:Bloom_3ps.pso)

ブルームは縮小画面なのでcppで拡大モードをバイリニアに変更してます
デフォルトのニアレストのままだとカックカクになります
cppの
// 描画モードをバイリニアに
SetDrawMode( DX_DRAWMODE_BILINEAR ) ;
部分をコメントアウトすればカクカク具合が見られます(これはこれで面白いのですが)



内容はしょりまくりの説明でしたが終わりです
さすがに3回目の書き込みとなると疲れるのでした

さてイビサ島ドライブしてこよう←おーい


スポンサーサイト
プロフィール

ペチコートさん

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

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

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