スポンサーサイト

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

DXライブラリで地形ブレンド

こんにちニャン!
捻った言い回しをしようとして若干すべった感のあるペチコです

若干ではないという意見は封殺します


今回は地形ブレンド
例えば草と土の境目等を違和感が少ないようにブレンドするアレです
言葉での説明がうまくできませんニポンゴムズカシイネ

まずテクスチャ
草テクスチャ土テクスチャ

草と土をシームレスにした256x256のテクスチャです

これを普通に
アルファ
こういうアルファでブレンドすると
草土ブレンド
こうなります

うん意外と悪くなかった。いつも見切り発車なペチコです
取り敢えずこれに高さを考慮したブレンドをやってみようということなのです

上のテクスチャとは別にハイトマップを用意します。作り方は検索してみてください
草ノーマル・ハイトマップ
左がノーマルマップで右がハイトマップです
今回ノーマルマップは使いませんが、DDS形式でノーマルマップのアルファにハイトマップを埋め込んでいる前提で書いてきます

これに上のアルファを用いてブレンド用シェーダでブレンドすると
シェーダ使用ブレンド
こうなりました

正直どこが違うの?って感じですが、それぞれ別タブで開いて見比べるとわかると思います
通常のアルファブレンドだとただ色がフェードしているだけですが、シェーダを使った方は背の高い草は土の上に出てきた感じで描画されます

でもペチコの作りが甘くイマイチなので色々改良するともっと良くなると思います
ということでHLSL

//TerrainBlendPS.fx
// 1:ディフューズマップテクスチャ
sampler DiffuseMapTexture1 : register( s0 ) ;
// 1:ノーマルマップ・アルファにハイトマップ
sampler NormalMapTexture1 : register( s1 ) ;
// 2:ディフューズマップテクスチャ
sampler DiffuseMapTexture2 : register( s2 ) ;
// 2:ノーマルマップ・アルファにハイトマップ
sampler NormalMapTexture2 : register( s3 ) ;
// ブレンド用テクスチャ
sampler AlphaTexture : register( s4 ) ;

float4 main(float2 Tex : TEXCOORD0) : COLOR0
{
// テクスチャ取得
float4 Col1 = tex2D( DiffuseMapTexture1, Tex );
float Heigt1 = tex2D( NormalMapTexture1, Tex ).a;
float4 Col2 = tex2D( DiffuseMapTexture2, Tex );
float Heigt2 = tex2D( NormalMapTexture2, Tex ).a;
float Alpha1 = tex2D( AlphaTexture, Tex ).r;
float Alpha2 = 1.0 - Alpha1;

float H1 = Col1.a + Heigt1 * Alpha1 ;
float H2 = Col2.a + Heigt2 * Alpha2 ;

// アルファとハイトよりブレンド率取得
float minus = max( H1, H2) - 0.2;

float out1 = max(H1 - minus, 0);
float out2 = max(H2 - minus, 0);

return float4( (Col1.rgb * out1 + Col2.rgb * out2) / (out1 + out2) , 1.0 );
}

いっぱいレジスタを使ってますが、ソースと素材は下の方に置いときますので見比べてください

簡単に説明するとアルファではなくハイトでフェードを行っているだけです
アルファ用テクスチャのつもりでAlphaって変数名にしたからすごく紛らわしいですが

float H1部分のAlpha1を消して
草土ブレンド
草多めにしてみたり

テクスチャとアルファを変えて
土道ブレンド
道テクスチャを作ってみたり(これはイマイチでした)

float minus = max( H1, H2) - 0.2;をfloat minus = min( H1, H2);に変えて
草道ブレンド
高さのみフェードでやってみたり(高さの違いが少ないテクスチャ同士だと変になります)

2x2で並べてみました
2x2並べてみる
よく見ると境界線が若干目立ちますが、これは元テクスチャのシームレス化が下手だったせいかも
割と違和感なく繋がってる方じゃないかと思います


こんな感じで色々と遊べます
マップチップ用のテクスチャ作成に便利かも
バンプマップ等に使う場合はレンダーターゲットでノーマルマップやハイトマップも別個に出力する必要がありますね

↓一応ソースと素材置いときます
terrain_test素材


DXライブラリで炎エフェクト

Привет、ペチコです
TDU2でよくロシア人と間違えられるので、前世はロシア人ではないかと思い始めています

ロシア語全くわかりませんけど


SkyrimとかFalloutをやってると炎の表現ってどうやるんだろうって気になりました
パーティクルを使って云々と概要は知っていても実際の手順は皆目見当も付かないので調べてみました

Fallout NVの炎


途中でワイヤフレームに切り替えてみましたがよくわかりません
なのでこのシーンの炎を切り出してみると、こんなカンジでした

Fo_NV_Flame
無理矢理抜き出したのでパースが狂っていますが、大小の平面ポリゴンが奥行きをずらして並んでます
これを踏まえて実際の画面を見てみると、なんとなくやり方がわかってくるような気がします
あくまで気がするだけです

次はテクスチャを覗いてみます
Fo_NV_FlameTexture
上半分がテクスチャ、下半分がアルファチャンネルです
実際は512x256のアルファ付き横長テクスチャになってました
そのまま載せる訳には参りませんのでこの画像は大きくなりません

動画で見てみるとテクスチャアニメーションはしていないので、各ポリゴンに別々の形の炎を割り振っているだけみたいですね


これを真似て作ってみようと思います
テクスチャはさっきのをお借りしました

flame_effect.cpp
#include "DxLib.h"

#define ArrayLength(array) (sizeof(array) / sizeof(array[0]))

// 関数
void Init_Particle( VECTOR position ); // パーティクル初期化
void Set_Particle( void ); // パーティクルセット

// パーティクル構造体
struct P_FLAMES
{
// 座標
VECTOR pos ;
// スケール(0:小 1:中 2:大)
int iscale ;
// スケール
float scale ;
// y軸移動
float ymove ;
// z軸回転
float rotate ;
// 透明度
float alpha ;
//テクスチャアニメーション
int anim ;
} ;
// パーティクル実体
P_FLAMES flames[30];

// 頂点のデータをセットアップ
VERTEX3DSHADER Vertex[ ArrayLength(flames)*6 ];

void Init_Particle( VECTOR position )
{
//パーティクル初期配置
for(int i=0;i<ArrayLength(flames);i++){
//flames[i].iscale = rand()%3;
flames[i].iscale = i%3;
flames[i].scale = (float)(flames[i].iscale+1)*0.1f;
if(flames[i].iscale==0){
flames[i].pos.x = position.x -30.0f+(float)(rand()%61);
}else if(flames[i].iscale==1){
flames[i].pos.x = position.x -25.0f+(float)(rand()%51);
}else{
flames[i].pos.x = position.x -5.0f+(float)(rand()%11);
}
//flames[i].pos.x = position.x -50.0f+(float)(rand()%101);
flames[i].pos.y = position.y;
flames[i].pos.z = ((float)ArrayLength(flames)/2.0f*2.0f)-(float)i*1.0f;
if(flames[i].pos.x<position.x){
flames[i].rotate = 3.14159f / 180.0f * 30.0f;
}else{
flames[i].rotate = 3.14159f / 180.0f * -30.0f;
}
flames[i].ymove = 0.0f;
flames[i].alpha = (float)(rand()%100+1)/100.0f;
flames[i].anim = rand()%8;
}
Set_Particle();
}

void Set_Particle( void )
{
for(int i=0;i<ArrayLength(flames);i++){
int arr = i*6;

// 拡大行列
MATRIX ZoomMtx = MGetScale( VGet( flames[i].scale, flames[i].scale, 1.0f ) ) ;
//Y軸回転行列
MATRIX RotateMtx = MGetRotZ( flames[i].rotate );
//平行移動行列
MATRIX MovemMtx = MGetTranslate( VGet( flames[i].pos.x, flames[i].pos.y+flames[i].ymove, flames[i].pos.z ) ) ;

//回転→平行移動
ZoomMtx = MMult(ZoomMtx,RotateMtx);
ZoomMtx = MMult(ZoomMtx,MovemMtx);

//テクスチャアニメーション
float u1 = (float)(flames[i].anim % 4) * 1.0f / 4.0f;
float v1 = (float)(flames[i].anim % 8 / 4) * 1.0f / 2.0f;
float u2 = u1 + 1.0f / 4.0f;
float v2 = v1 + 1.0f / 2.0f;

//アルファ
int alpha = (int)(flames[i].alpha*255.0f);

// 頂点データをセットアップ
Vertex[ arr ].pos = VGet( -100.0f, 100.0f, 0.0f ) ;
// 変換
Vertex[ arr ].pos = VTransform( Vertex[ arr ].pos, ZoomMtx ) ;
Vertex[ arr ].norm = VGet( 0.0f, 0.0f, -1.0f ) ;
Vertex[ arr ].dif = GetColorU8( 255,255,255,alpha ) ;
Vertex[ arr ].spc = GetColorU8( 0, 0, 0, 0 ) ;
Vertex[ arr ].u = u1 ;
Vertex[ arr ].v = v1 ;
Vertex[ arr ].su = 0.0f ;
Vertex[ arr ].sv = 0.0f ;

Vertex[ arr+1 ].pos = VGet( 100.0f, 100.0f, 0.0f ) ;
// 変換
Vertex[ arr+1 ].pos = VTransform( Vertex[ arr+1 ].pos, ZoomMtx ) ;
Vertex[ arr+1 ].norm = VGet( 0.0f, 0.0f, -1.0f ) ;
Vertex[ arr+1 ].dif = GetColorU8( 255,255,255,alpha ) ;
Vertex[ arr+1 ].spc = GetColorU8( 0, 0, 0, 0 ) ;
Vertex[ arr+1 ].u = u2;
Vertex[ arr+1 ].v = v1 ;
Vertex[ arr+1 ].su = 0.0f ;
Vertex[ arr+1 ].sv = 0.0f ;

Vertex[ arr+2 ].pos = VGet( -100.0f, -100.0f, 0.0f ) ;
// 変換
Vertex[ arr+2 ].pos = VTransform( Vertex[ arr+2 ].pos, ZoomMtx ) ;
Vertex[ arr+2 ].norm = VGet( 0.0f, 0.0f, -1.0f ) ;
Vertex[ arr+2 ].dif = GetColorU8( 255,255,255,alpha ) ;
Vertex[ arr+2 ].spc = GetColorU8( 0, 0, 0, 0 ) ;
Vertex[ arr+2 ].u = u1 ;
Vertex[ arr+2 ].v = v2 ;
Vertex[ arr+2 ].su = 0.0f ;
Vertex[ arr+2 ].sv = 0.0f ;


Vertex[ arr+3 ].pos = VGet( -100.0f, -100.0f, 0.0f ) ;
// 変換
Vertex[ arr+3 ].pos = VTransform( Vertex[ arr+3 ].pos, ZoomMtx ) ;
Vertex[ arr+3 ].norm = VGet( 0.0f, 0.0f, -1.0f ) ;
Vertex[ arr+3 ].dif = GetColorU8( 255,255,255,alpha ) ;
Vertex[ arr+3 ].spc = GetColorU8( 0, 0, 0, 0 ) ;
Vertex[ arr+3 ].u = u1 ;
Vertex[ arr+3 ].v = v2 ;
Vertex[ arr+3 ].su = 0.0f ;
Vertex[ arr+3 ].sv = 0.0f ;

Vertex[ arr+4 ].pos = VGet( 100.0f, 100.0f, 0.0f ) ;
// 変換
Vertex[ arr+4 ].pos = VTransform( Vertex[ arr+4 ].pos, ZoomMtx ) ;
Vertex[ arr+4 ].norm = VGet( 0.0f, 0.0f, -1.0f ) ;
Vertex[ arr+4 ].dif = GetColorU8( 255,255,255,alpha ) ;
Vertex[ arr+4 ].spc = GetColorU8( 0, 0, 0, 0 ) ;
Vertex[ arr+4 ].u = u2 ;
Vertex[ arr+4 ].v = v1 ;
Vertex[ arr+4 ].su = 0.0f ;
Vertex[ arr+4 ].sv = 0.0f ;

Vertex[ arr+5 ].pos = VGet( 100.0f, -100.0f, 0.0f ) ;
// 変換
Vertex[ arr+5 ].pos = VTransform( Vertex[ arr+5 ].pos, ZoomMtx ) ;
Vertex[ arr+5 ].norm = VGet( 0.0f, 0.0f, -1.0f ) ;
Vertex[ arr+5 ].dif = GetColorU8( 255,255,255,alpha ) ;
Vertex[ arr+5 ].spc = GetColorU8( 0, 0, 0, 0 ) ;
Vertex[ arr+5 ].u = u2 ;
Vertex[ arr+5 ].v = v2 ;
Vertex[ arr+5 ].su = 0.0f ;
Vertex[ arr+5 ].sv = 0.0f ;
}
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
//ウインドウモード
ChangeWindowMode( TRUE ) ;

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

// 画面解像度
SetGraphMode( 1024 , 768 , 32 ) ;

//地面
int modelhandle = MV1LoadModel( "ground.mv1" ) ;

// テクスチャを読み込む
int texhandle = LoadGraph( "fire.dds" ) ;

// 頂点シェーダ
int vshandle = LoadVertexShader( "fireVS.vso" ) ;
// ピクセルシェーダ
int pshandle = LoadPixelShader( "firePS.pso" ) ;

//パーティクルの初期化
Init_Particle( VGet(0.0f,0.0f,0.0f) );

// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{

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

// カメラ視点
SetCameraPositionAndTarget_UpVecY( VGet( 100.0f, 100.0f, -200.0f ), VGet( 0.0f, 50.0f, 0.0f ) ) ;

// Zバッファを有効にする
SetUseZBufferFlag( TRUE );
// シェーダ使用しない
MV1SetUseOrigShader( FALSE ) ;
// 地面の描画
MV1SetPosition( modelhandle, VGet( 0.0f, 0.0f, 0.0f ) ) ;
MV1DrawModel( modelhandle ) ;

// パーティクル
for(int i=0;i<ArrayLength(flames);i++){
//拡大率
if(flames[i].iscale<2){
flames[i].scale *= 0.99f;
}else{
flames[i].scale *= 1.0f + (float)(flames[i].iscale)*0.003f;
}
//回転
//flames[i].rotate += 3.14159f / 180.0f * 6.0f*(1.0f-(float)flames[i].scale+0.1f);
//y移動
flames[i].ymove += ( 3.0f-(float)(flames[i].iscale) )*1.4f;
//透明度
flames[i].alpha -= ( (float)(3-flames[i].iscale) )*0.014f;
//初期化
if(flames[i].alpha<0.0f){
flames[i].ymove=0.0f;
flames[i].alpha=1.0f;
flames[i].anim = (flames[i].anim+1)%8;
flames[i].scale = (float)(flames[i].iscale+1)*0.1f;
}
}
Set_Particle();

// ビュー行列
MATRIX mtx = GetCameraViewMatrix() ;
mtx.m[0][0]=1.0f;mtx.m[0][1]=0.0f;mtx.m[0][2]=0.0f;
mtx.m[1][0]=0.0f;mtx.m[1][1]=1.0f;mtx.m[1][2]=0.0f;
mtx.m[2][0]=0.0f;mtx.m[2][1]=0.0f;mtx.m[2][2]=1.0f;
//mtx = MInverse( mtx ) ;
mtx = MTranspose( mtx ) ;

// 回転を無効にしたビュー行列をc6にセット
SetVSConstFMtx( 6, mtx ) ;

//シェーダ使用
MV1SetUseOrigShader( TRUE ) ;
// 使用するテクスチャを0番にセット
SetUseTextureToShader( 0, texhandle ) ;

// 使用する頂点シェーダーのセット
SetUseVertexShader( vshandle ) ;

// 使用するピクセルシェーダーをセット
SetUsePixelShader( pshandle ) ;

// アルファブレンド
SetDrawBlendMode( DX_BLENDMODE_ALPHA, 128 ) ;
// シェーダーを使用したポリゴンの描画
DrawPolygon3DToShader( Vertex, ArrayLength(flames)*2 ) ;
// アルファブレンド解除
SetDrawBlendMode( DX_BLENDMODE_NOBLEND, 255 ) ;

// 使用したfloat4型定数10~13番の設定を無効化する
ResetVSConstF( 6, 4 ) ;

// Zバッファを無効にする
SetUseZBufferFlag( FALSE );

// 裏画面の内容を表画面に反映させる
ScreenFlip() ;

}

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

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

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

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


正直手探りなのでコードは無茶苦茶です
はい、言い訳です。整理整頓ニガテなので綺麗なコードは書けません
インデントずれは直す気がない仕様です
そういえば今まで<とか>を&lt;&gt;で置き換えてなかったからIEだとちゃんと表示されてなかったかもですね


上から適当に説明

ArrayLength(array) (sizeof(array) / sizeof(array[0]))
配列のサイズを取得するようにしましたけど、別にArray_Num=xxみたいにした方が計算が入らない分よかったですね

struct P_FLAMES
パーティクルに必要そうなものを構造体でまとめましたが、無駄な情報を増やすより時間とかフレーム数を保存するようにして、そこから座標計算とかした方が良かったです

粗ばっかり見えてきます

P_FLAMES flames[30];
一つの炎に幾つパーティクルを使うかです。というかここでは一つの炎しか考えてません
実際は幾つか炎が上がる事があるでしょうし、別に炎の数を管理するものを作ると良いと思います

VERTEX3DSHADER Vertex[xx];
で頂点データを作成してます。xx部分は上のパーティクル数*6です(平面ポリゴンに6つの頂点があるため)
同座標の頂点をまとめる、Index版もありますが面倒だったのでこちらを使ってます

void Init_Particle
パーティクルの初期配置関数。表示したい座標を渡してそこに描画するようにしてます
中身はものすごい手探りなのではしょります
時間を取ってないので初期透明度をランダムにすることで、ずれを表現してます

void Set_Particle
こちらが実際に頂点セット関数です
毎フレーム全頂点をセットするという無理矢理さです
この辺はレジスタや頂点テクスチャを使えばシェーダ内で計算ができるのでもっと速く出来ます
頂点テクスチャ上手く値が取れないのです。どうやるんだろう

ベース頂点はそのまま(-100.0f~100.0f)で拡縮、回転、平行移動を行列にセットして動かしてます
最終的に頂点へVTransformするとそれらの計算結果が反映されます

テクスチャのUVはint型のテクスチャ番号から番号に応じたテクスチャを表示するように設定
使ったのが横長のテクスチャだったので横のみ%4になってます

%については今更説明するようなことでもないのですが一応ご説明
%xはxで割った余りを取得します
例えば%2の場合、2/2=1余り0 3/2=1余り1 4/2=2余り0となり繰り返しの数を表現するのに都合が良いのです
この場合だと横に4つテクスチャが並んでいるので%4でxが幾つになっても0~3の4つの数値を取れるようにしてます

Vertex[ xxx ].dif
の4つめの要素が透明度です
0~255のintなので0.0~1.0のfloat型を255倍するようにしてます
int型のままだとアルファを0.5だけずらしたいとかがやりにくいので構造体のアルファはfloatで持つようにしてます

後はパーティクルの数分ループを回す事で、複数ポリゴンを生成しています


WinMain部分
シェーダで描画しますのでそれ用のあれこれを読み込んで、Init_Particleでパーティクルを原点で初期化してます

while( ProcessMessageのループ部分
最初にカメラの位置を決めたり、Zバッファを有効にしたり、地面を描画したりしてます

// パーティクル
for(int i=0;iこのループで各パーティクルの計算を行ってます
中身はなんとか手を抜こうと適当にやった結果あんまり良くなかったのではしょります
放射状に広がる処理とか入れるとステキかも

MATRIX mtx = GetCameraViewMatrix() ;~
シェーダで描画する前にビュー行列の左上から3x3を単位行列にしてます
こうすることでワールド→ビュー変換の回転が無効になり常にカメラを向くビルボードになります
ただし、ここで全方位ビルボードはダメでした
真上に近い角度から見てもカメラの方を向くので違和感がすごいです
用途に合わせてY軸のみのビルボードにした方がいいと思います

後はアルファブレンドでシェーダ描画
シェーダは何も特別なことはやってません
// flameEffectVS.fx
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( VERTEX3DSHADER構造体の pos の値 )
float4 DiffuseColor : COLOR0 ;
float3 Normal : NORMAL0 ; // 法線( VERTEX3DSHADER構造体の norm の値 )
float2 TextureCoord0 : TEXCOORD0 ; // テクスチャ座標0( VERTEX3DSHADER構造体の u, v の値 )
} ;

// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 ProjectionPosition : POSITION ; // 座標( 射影空間 )
float4 DiffuseColor : COLOR0 ;
float2 TextureCoord0 : TEXCOORD0 ; // テクスチャ座標
} ;


// C++ 側で設定する定数の定義
float4x4 cfViewMatrix : register( c6 ) ; // ワールド座標をビュー座標に変換する行列の転置行列
float4x4 cfProjectionMatrix : register( c2 ) ; // ビュー座標を射影座標に変換する行列の転置行列

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

/*
float4x4 bmtx = cfViewMatrix;
bmtx[0][0]=1.0;bmtx[1][0]=0.0;bmtx[2][0]=0.0;
bmtx[0][1]=0.0;bmtx[1][1]=1.0;bmtx[2][1]=0.0;
bmtx[0][2]=0.0;bmtx[1][2]=0.0;bmtx[2][2]=1.0;
*/

// 入力の頂点座標にC++プログラム側で設定した頂点座標を加算する
lWorldPosition = VSInput.Position ;

// 頂点座標をビュー空間の座標に変換する
lViewPosition = mul( lWorldPosition, cfViewMatrix ) ;

// ビュー空間の座標を射影空間の座標に変換する
VSOutput.ProjectionPosition = mul( lViewPosition, cfProjectionMatrix ) ;

// テクスチャ座標はそのまま代入
VSOutput.TextureCoord0 = VSInput.TextureCoord0;

// 頂点カラーはそのまま代入
VSOutput.DiffuseColor = VSInput.DiffuseColor ;

// 関数の戻り値がピクセルシェーダーに渡される
return VSOutput ;
}


// flameEffectPS.fx
// ピクセルシェーダーの入力
struct PS_INPUT
{
float4 DiffuseColor : COLOR0 ;
float2 TextureCoord0 : TEXCOORD0 ; // テクスチャ座標
} ;

// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 DrawColor : COLOR0 ; // 描画カラー
} ;


// C++ 側で設定する定数の定義

// 描画するテクスチャ
sampler DiffuseMapTexture : register( s0 ) ;

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

// テクスチャーの色を取得
lTextureColor = tex2D( DiffuseMapTexture, PSInput.TextureCoord0 ) ;

// 出力する色はテクスチャの色とディフューズカラーを乗算したもの
PSOutput.DrawColor = lTextureColor * PSInput.DiffuseColor;

// 関数の戻り値がラスタライザに渡される
return PSOutput ;
}


頂点シェーダ途中のコメントアウト部分は、シェーダ内でビルボードできる!って考えて書きましたが、なんか意味ないしわかりにくいので止めた名残です

実際のゲームの画面と再現したもので炎の色合いが違ったので、その辺弄ると良いのでしょうがめんどくさくなったので単純出力です


で結果がこちら↓


うん、なんか上手くいきませんでした
この辺はパーティクルの移動部分の調整でどうにでもなるので無視
要はプロはこんなカンジでやってるみたいというのがわかっただけでも収穫なのです

プロの手口(←こう言ったらなんか危ない響きですね)を見るのも面白くて参考になりますね


DXライブラリでSSAO3

こんにちは、ペチ子です
ペチってなんだろう


そういえばSSAOが中途半端になってたので今回もSSAO
いえ、毎回中途半端ですが

SSAOってなんだって人は詳細はwebで!

シンプルルーム
効果がわかりやすいようにシンプルな部屋を作ってみました
犯人はヤスだと思います

これにSSAO
SSAO強め
強めにしてます。英語で言うとyarisugiです
ランダムと法線を適当に計算してる手抜きなので色々おかしなとこもありますが
部屋の隅とか、接地してる部分とかちゃんと暗くなってます
それっぽければよいのです

これをぼかして上の元画像と合成すればできあがり
SSAO適用
うんこれは掛けない方がマシなレベル
シーンをちゃんとして、パラメーターを調整すれば見映えは良くなります

ということでHLSLです
事前に深度バッファを適当なスクリーンに出力しといてください
その辺はめんどくさい前にやった気がするので省略

SSAO_PS.pso
// 深度バッファ
sampler DepthMapTexture : register( s1 ) ;
// 法線
//sampler NormalMapTexture : register( s2 ) ;
// ランダム
//sampler NoiseTexture : register( s3 ) ;

//パラメーター
static const float strength = 0.125;//0.125
static const float falloff = 0.0000002;
static const float rad = 0.02;//0.006
static const float total_strength = 1.0;

#define samples 16
static const float invSamples = 1.0 / (float)samples;
static const float3 sample_sphere[samples] = {
float3( 0.5381, 0.1856,-0.4319), float3( 0.1379, 0.2486, 0.4430),
float3( 0.3371, 0.5679,-0.0057), float3(-0.6999,-0.0451,-0.0019),
float3( 0.0689,-0.1598,-0.8547), float3( 0.0560, 0.0069,-0.1843),
float3(-0.0146, 0.1402, 0.0762), float3( 0.0100,-0.1924,-0.0344),
float3(-0.3577,-0.5301,-0.4358), float3(-0.3169, 0.1063, 0.0158),
float3( 0.0103,-0.5869, 0.0046), float3(-0.0897,-0.4940, 0.3287),
float3( 0.7119,-0.0154,-0.0918), float3(-0.0533, 0.0596,-0.5411),
float3( 0.0352,-0.0631, 0.5460), float3(-0.4776, 0.2847,-0.0271)
};

float3 normal_from_depth(float depth, float2 UV) {

const float2 offset1 = float2(0.0,0.001);
const float2 offset2 = float2(0.001,0.0);

float depth1 = tex2D(DepthMapTexture, UV + offset1).r;
float depth2 = tex2D(DepthMapTexture, UV + offset2).r;

float3 p1 = float3(offset1, depth1 - depth);
float3 p2 = float3(offset2, depth2 - depth);

float3 normal = cross(p1, p2);
normal.z = -normal.z;

return normalize(normal);
}

float3 get_Rnd(float2 UV) {
float noiseX = (frac(sin(dot(UV, float2(12.9898,78.233) * 2.0)) * 43758.5453));
float noiseY = sqrt(1 - noiseX * noiseX);
float noiseZ = abs(noiseX + noiseX) * 0.5;
return float3(noiseX, noiseY, noiseZ);
}

float4 main(float2 Tex : TEXCOORD0) : COLOR0
{
float3 rnd = get_Rnd(Tex);
//float2 noiseScale = float2(1024.0 / 64.0, 768.0 / 64.0);
//float3 rnd = tex2D(NoiseTexture, Tex * noiseScale).xyz * 2.0 - 1.0;

float depth = tex2D(DepthMapTexture, Tex).r;
//float3 normal = (tex2D(NormalMapTexture, Tex).rgb - 0.5f) * 2.0f;
float3 normal = normal_from_depth(depth, Tex);

float bl = 0.0;
float radD = rad / depth;

float depthDiff;
float2 rpos;
float3 ray;
float4 occFrag;

for( int i = 0; i < samples; ++i )
{
ray = radD * reflect( sample_sphere[i], rnd );

rpos = Tex + sign(dot(ray, normal)) * ray.xy * float2(1.0f, -1.0f);
//occFrag.xyz = (tex2D(NormalMapTexture, rpos).xyz -0.5f ) * 2.0f;
occFrag.a = tex2D(DepthMapTexture, rpos).r;
occFrag.xyz = normal_from_depth(occFrag.a, rpos);

depthDiff = depth - occFrag.a;

bl += step( falloff, depthDiff ) * (1.0 - dot( normalize(occFrag.xyz), normal )) *
(1.0 - smoothstep( falloff, strength, depthDiff ));
}

float ao = 1.0 - total_strength * bl * invSamples;

return float4( ao, ao, ao, 1.0 );
}


コメントアウトしてるところは、ランダムテクスチャとか法線をテクスチャ出力した場合の処理
めんどうなので深度だけの手抜きなんちゃってSSAOです

パラメーターは上の方にまとまっているので色々弄ると良いでしょう

これとは関係ないですが、マルチレンダーターゲットで深度バッファを出力してるせいか、アンチエイリアスが効きませんでした
DirectXの仕様だそうですが、EdgeAAの時はマルチでAAできたような。ううむ


適当なテクスチャを貼って、パラメーターも調整してみましたの図

通常描画
通常描画

SSAOあり描画
SSAO

ああ、わかりました
エフェクトを使ったからって絵面が劇的に良くなるはずもなく
必要なのは画作りのセンスってことですね

センスをください

DXライブラリでDLAA

こんにちは、ぺっちりぺちぺちペチコです

言葉の意味はわからないけど語呂はいいネ


アンチエイリアス第三弾
今回はDirectionally Localized Anti-Aliasing (DLAA)
EdgeAAなどと同じく、エッジにブラーを掛ける技法ですが、長いエッジにも対応した比較的新しいものです
スターウォーズのゲームに搭載されて話題になったそうです

AAなし4x
AAなし
4x

DLAAEdgeAA2
DLAA
EdgeAA
毎度のことで、クリックで拡大してタブ切り替えで比較しないと違いはわかりません

拡大比較
拡大比較
ブラーベースなのでどうしてもボケたカンジになります
各々のシェーダは得意分野が違うので、単純に比較はできないのですが、参考程度に

4x vs DLAA
船首のこの部分なんかはDLAAの方が滑らかに見えるような気がします
ただDLAAは暗い部分(この画像だと右側の重なり部分)に弱いのでアンチエイリアスが掛かっていません
こういう所に強いのはFXAAですが、FXAAはそれ故画像全体がボケボケになります
一長一短ですね

実装ですが、使うスクリーンは通常用とプリパス用(エッジ検出)の2つです
深度も法線もいりません

ただしアルファ値を計算に使うので
プリパス用スクリーンをint ScreenHandle2 = MakeScreen( 1024, 768 ,TRUE ); こんなカンジでアルファTRUE
プリパス→DLAA描画前にSetDrawAlphaTest( DX_CMP_ALWAYS, 0 );で常に描画にしておきます

アルファテストはDXライブラリの他の部分でも使っているそうなので、描画が終わったら念のため
SetDrawAlphaTest( -1 ,0);
でデフォルトに戻しておいた方がいいでしょう

後はいつものように、2D描画用頂点を用意しておいて、DrawPolygon2DToShaderやDrawPrimitive2DToShaderで通常描画の結果をシェーダで描画するだけです(プリパスとDLAA描画の2Passです)

HLSLはすがすがしいまでの丸パクリなので説明等はありません!
コピーライトが付いてたのでパス毎に付記しました

まずプリパス。エッジの検出を行ってます
DLAA_1PS.pso
//
// Directionally Localized Anti-Aliasing (DLAA)
// by Dmitry Andreev
// Copyright (C) LucasArts 2010-2011
//

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

//スクリーン比用。ここでは決め打ち(通常はスクリーンサイズをレジスタで渡します)
const static float2 rcpres = float2(1.0f/1024.0f,1.0f/768.0f);

#define LD(o, dx, dy) o = tex2D(DiffuseMapTexture, Tex + float2(dx, dy) * rcpres);

float GetIntensity( const float3 rgb )
{
return dot( rgb, float3( 0.333f, 0.334f, 0.333f ) );
//return dot( rgb, float3( 0.299f, 0.587f, 0.114f ) );
}

float4 main(float2 Tex : TEXCOORD0) : COLOR0
{
float4 center, left, right, top, bottom;

LD( center, 0, 0 )
LD( left, -1, 0 )
LD( right, 1, 0 )
LD( top, 0, -1 )
LD( bottom, 0, 1 )

float4 edges = 4.0f * abs( ( left + right + top + bottom ) - 4.0f * center );
float edges_lum = GetIntensity( edges.xyz );

return float4( center.xyz, edges_lum );
}

通常描画画面をこのシェーダで描画します
//ディフューズマップテクスチャにレジスタs0で通常描画画面を渡しておいてください

↑で描画した画面を裏画面に次のシェーダで描画します
DLAA_2PS.pso
//
// Directionally Localized Anti-Aliasing (DLAA)
// by Dmitry Andreev
// Copyright (C) LucasArts 2010-2011
//

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

//スクリーン比用。ここでは決め打ち(通常はスクリーンサイズをレジスタで渡します)
const static float2 rcpres = float2(1.0f/1024.0f,1.0f/768.0f);

#define LD(o, dx, dy) o = tex2D(DiffuseMapTexture, Tex + float2(dx, dy) * rcpres);


float GetIntensity( const float3 rgb )
{
// return dot( rgb, float3( 0.333f, 0.334f, 0.333f ) );
return dot( rgb, float3( 0.299f, 0.587f, 0.114f ) );
}

float4 main(float2 Tex : TEXCOORD0) : COLOR0
{
const float lambda = 3.0f;
const float epsilon = 0.1f;

//
// Short Edges
//

float4 center, left_01, right_01, top_01, bottom_01;

// sample 5x5 cross
LD( center, 0, 0 )
LD( left_01, -1.5, 0 )
LD( right_01, 1.5, 0 )
LD( top_01, 0,-1.5 )
LD( bottom_01, 0, 1.5 )

float4 w_h = 2.0f * ( left_01 + right_01 );
float4 w_v = 2.0f * ( top_01 + bottom_01 );

// Sharper (3-pixel wide high-pass)
float4 left, right, top, bottom;

LD( left, -1, 0 )
LD( right, 1, 0 )
LD( top, 0, -1 )
LD( bottom, 0, 1 )

float4 edge_h = abs( left + right - 2.0f * center ) / 2.0f;
float4 edge_v = abs( top + bottom - 2.0f * center ) / 2.0f;


float4 blurred_h = ( w_h + 2.0f * center ) / 6.0f;
float4 blurred_v = ( w_v + 2.0f * center ) / 6.0f;

float edge_h_lum = GetIntensity( edge_h.xyz );
float edge_v_lum = GetIntensity( edge_v.xyz );
float blurred_h_lum = GetIntensity( blurred_h.xyz );
float blurred_v_lum = GetIntensity( blurred_v.xyz );

float edge_mask_h = saturate( ( lambda * edge_h_lum - epsilon ) / blurred_v_lum );
float edge_mask_v = saturate( ( lambda * edge_v_lum - epsilon ) / blurred_h_lum );

float4 clr = center;
clr = lerp( clr, blurred_h, edge_mask_v );
clr = lerp( clr, blurred_v, edge_mask_h * 0.5f ); // TFU2 uses 1.0f instead of 0.5f

//
// Long Edges
//

float4 h0, h1, h2, h3, h4, h5, h6, h7;
float4 v0, v1, v2, v3, v4, v5, v6, v7;

// sample 16x16 cross (sparse-sample on X360, incremental kernel update on SPUs)
LD( h0, 1.5, 0 ) LD( h1, 3.5, 0 ) LD( h2, 5.5, 0 ) LD( h3, 7.5, 0 ) LD( h4, -1.5,0 ) LD( h5, -3.5,0 ) LD( h6, -5.5,0 ) LD( h7, -7.5,0 )
LD( v0, 0, 1.5 ) LD( v1, 0, 3.5 ) LD( v2, 0, 5.5 ) LD( v3, 0, 7.5 ) LD( v4, 0,-1.5 ) LD( v5, 0,-3.5 ) LD( v6, 0,-5.5 ) LD( v7, 0,-7.5 )

float long_edge_mask_h = ( h0.a + h1.a + h2.a + h3.a + h4.a + h5.a + h6.a + h7.a ) / 8.0f;
float long_edge_mask_v = ( v0.a + v1.a + v2.a + v3.a + v4.a + v5.a + v6.a + v7.a ) / 8.0f;

long_edge_mask_h = saturate( long_edge_mask_h * 2.0f - 1.0f );
long_edge_mask_v = saturate( long_edge_mask_v * 2.0f - 1.0f );

// if ( long_edge_mask_h > 0 || long_edge_mask_v > 0 ) // faster but less resistant to noise (TFU2 X360)
if ( abs( long_edge_mask_h - long_edge_mask_v ) > 0.2f ) // resistant to noise (TFU2 SPUs)
{
float4 left, right, top, bottom;

LD( left, -1, 0 )
LD( right, 1, 0 )
LD( top, 0, -1 )
LD( bottom, 0, 1 )

float4 long_blurred_h = ( h0 + h1 + h2 + h3 + h4 + h5 + h6 + h7 ) / 8.0f;
float4 long_blurred_v = ( v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7 ) / 8.0f;

float lb_h_lum = GetIntensity( long_blurred_h.xyz );
float lb_v_lum = GetIntensity( long_blurred_v.xyz );

float center_lum = GetIntensity( center.xyz );
float left_lum = GetIntensity( left.xyz );
float right_lum = GetIntensity( right.xyz );
float top_lum = GetIntensity( top.xyz );
float bottom_lum = GetIntensity( bottom.xyz );

float4 clr_v = center;
float4 clr_h = center;

// naive search
float hx = saturate( ( lb_h_lum - top_lum ) / ( center_lum - top_lum ) );
float hy = saturate( ( lb_h_lum - center_lum ) / ( bottom_lum - center_lum ) );
float vx = saturate( ( lb_v_lum - left_lum ) / ( center_lum - left_lum ) );
float vy = saturate( ( lb_v_lum - center_lum ) / ( right_lum - center_lum ) );

if ( hx == 0 ) hx = 1;
if ( vx == 0 ) vx = 1;

clr_v = lerp( left, clr_v, vx );
if ( vy < 1 ) clr_v = lerp( clr_v, right, vy );

clr_h = lerp( top, clr_h, hx );
if ( hy < 1 ) clr_h = lerp( clr_h, bottom, hy );


clr = lerp( clr, clr_v, long_edge_mask_v );
clr = lerp( clr, clr_h, long_edge_mask_h );
}

//
// Preserve high frequencies
//

float4 r0, r1;
float4 r2, r3;

LD( r0, -1.5, -1.5 )
LD( r1, 1.5, -1.5 )
LD( r2, -1.5, 1.5 )
LD( r3, 1.5, 1.5 )

// faster version
//r0 = top_01;
//r1 = bottom_01;
//r2 = left_01;
//r3 = right_01;

float4 r = ( 4.0f * ( r0 + r1 + r2 + r3 ) + center + top_01 + bottom_01 + left_01 + right_01 ) / 25.0f;
clr = lerp( clr, center, saturate( r.a * 3.0f - 1.5f ) );

return clr;
}

長いです
ところどころfaster versionがコメントアウトになっていますが、速いのかしら?(ためしてない)


パフォーマンスはFXAAの時の400隻の大船団で測ってみました

DLAAパフォーマンス
※計測は前と同じシーンで行っています

AAなし1300-1600FXAA1400-18004x1600-200016x2000-16000DLAA2000-14000


ううむ、揺れ幅が大きくてよくわかりませんでした
きっとシェーダ以外のペチコード(ペチコのコード。うまいこと言ったつもり)に問題がある気がします
一般的にFXAAと同等、4xより軽い位と言われています
DXライブラリではtechniqueが使えず、パス処理毎にGPUからCPUを経緯するのでその辺も関係してるのかも
※あくまで推測です

何はともあれ、アンチエイリアス一つとっても、他にもまだまだ技法があって、色々遊べる所がシェーダの面白いところだと思います

パクりしかやってませんけど


DXライブラリでEdgeAA(失敗)

こんにちは、失敗ばかりのペチコです。
もはやペチコ自体が失敗作なのではと思えて来ました

そういえば造船でも失敗ばかりでした


めんどくさくなったので失敗作でもいいんです
もう投げやりです。ジャベリンです

今回はEdgeAA
その名の通り、エッジを検出してそこにブラーを掛けて滑らかにする、アンチエイリアシング技法のひとつです

結果はこうなりました

EdgeAA通常
AAなし
EdgeAA

毎度、クリックで拡大して、タブ切り替えで見比べないと違いはわかりません

拡大拡大
AAなしとEdgeAA比較
見ただけで失敗とわかります
元々エッジのみに掛かるので、重なった部分に弱いのがEdgeAAですが、ううむ

エッジの抽出の方法をちょっと変えてみたら少しはましになりましたが、ううむ
EdgeAA2EdgeAA2
EdgeAA
拡大


流れとしましては、通常描画、深度バッファ描画、深度からエッジ抽出、エッジ部分をブラー、というカンジになります

必要なスクリーンは3つ
1.通常描画用
2.深度バッファ用(16bit,1チャンネル)
3.エッジ描画用(エッジはアルファに格納するため、MakeScreen( 1024, 768 ,TRUE )としてアルファONにしときます)

通常描画はいつも通りなんでもいいです
ペチコはレンダーターゲットを使ってみたかったので、
SetRenderTargetToShader( 0, ScreenHandle1 ) ;
SetRenderTargetToShader( 1, ScreenHandle2 ) ;
として、ScreenHandle1を通常描画、ScreenHandle2を深度バッファにしました

レンダーターゲットを使うと、通常描画用頂点シェーダに
VSOutput.Depth = VSOutput.Pos;

そのピクセルシェーダ
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
float4 Color1 : COLOR1 ;
} ;
<略>
PSOutput.Color1 = PSInput.Depth.z / PSInput.Depth.w;
といったカンジで、深度等の他の情報も一緒に出力するようにできるのです

これでスクリーン1に通常描画、スクリーン2に深度バッファが格納されました
深度バッファ
ここで悩んだ所
ペチコはBulletの推奨が1m=1.0fなので、カメラのnearfarを大体1m換算で決めてるのですが
SetCameraNearFar( 1.0f, 1000.0f ) ;だと深度が上手く取れず
SetCameraNearFar( 10.0f, 10000.0f ) ;にするとちゃんと取れました
なんでだろう…うーんよくわからんですばい

ここでエッジAAシェーダの出番です
使うシェーダは2つ。輪郭検出用と、輪郭からブラー用です
EdgeAA_1PS.pso
// ディフューズマップテクスチャ
sampler DiffuseMapTexture : register( s0 ) ;
// 深度バッファ
sampler DepthMapTexture : register( s1 ) ;

static const int sizeKernelWG = 8;
static const float2 blurCoordWG[sizeKernelWG] = {
{ -2.0, 0.0},
{ 2.0, 0.0},

{ -2.0, 2.0},
{ -2.0, -2.0},

{ 2.0, 2.0},
{ 2.0, -2.0},

{ 0.0, 2.0},
{ -0.0, -2.0},
};

static const float blurMagnitudeWG[sizeKernelWG] = {
{1.0},
{1.0},

{1.0},
{1.0},

{0.9},
{0.9},

{0.8},
{0.8},
};

//スクリーン比用。ここでは決め打ち(通常はスクリーンサイズをレジスタで渡します)
const static float2 rcpres = float2(1.0f/1024.0f,1.0f/768.0f);

float4 POWDepth(float2 UV) {
//深度マップ読み込み
float4 colDepth = tex2D(DepthMapTexture, UV);
//累乗
colDepth = pow(colDepth, 19);

return colDepth;
}

float4 main(float2 Tex : TEXCOORD0) : COLOR0
{
float colEdge = 0.0f;
float colOutput = 0.0f;
float colInput = POWDepth(Tex).r;

float3 color = tex2D(DiffuseMapTexture, Tex).rgb;

for (int i = 0; i < 8; i++) {
//周辺を読み込み
colOutput = POWDepth(Tex + blurCoordWG[i] * rcpres).r;
//周辺との差を加算
colEdge += 3 * saturate(colOutput * blurMagnitudeWG[i] - colInput) * 10;
}

//差の大きい所をエッジと見なす(0になる)
colOutput = 1 - colEdge;
//エッジ(.a=0)をアルファに格納
return float4(color, saturate(colOutput));
}

/*
float GetDepth(float x, float y)
{
return tex2D(DepthMapTexture, float2(x, y)).r;
}


float4 main(float2 Tex : TEXCOORD0) : COLOR0
{

float3 color = tex2D(DiffuseMapTexture, Tex).rgb;

float b = GetDepth(Tex.x, Tex.y);
float d = abs(b - GetDepth(Tex.x + rcpres.x, Tex.y).r)
+ abs(b - GetDepth(Tex.x, Tex.y + rcpres.y).r);
return float4(color,saturate(1 - d * 150));
}
*/

こちらが輪郭検出用シェーダ
SetUseTextureToShader( 0, ScreenHandle1 ) ;//通常描画スクリーン
SetUseTextureToShader( 1, ScreenHandle2 ) ;//深度バッファスクリーン
として、スクリーンをテクスチャとして渡しときます
描画はいつものように、DrawPolygon2DToShaderかDrawPrimitive2DToShaderで2D描画します

下の方でコメントアウトしているのが、エッジ抽出の方法を変更したものです
テクスチャ読み込み部分より下がバッサリいらなくなるので、こっちの方が楽と言えば楽です

どちらもカラーは通常描画そのままで、エッジが検出された部分のアルファを0にしてます
なのでC++の方で
//アルファテストは常に描画
SetDrawAlphaTest( DX_CMP_ALWAYS, 0 );
//EdgeAA
// 描画先を3にする
SetDrawScreen( ScreenHandle3 ) ;
ClearDrawScreen() ; //画面クリア
// 使用するピクセルシェーダーをセット
SetUsePixelShader( Handle_EdgeAA_1PS ) ;
//テクスチャ設定 0:通常 1:深度
SetUseTextureToShader( 0, ScreenHandle1 ) ;
SetUseTextureToShader( 1, ScreenHandle2 ) ;
//2D描画
DrawPolygon2DToShader( Vert, 2 ) ;
//テクスチャ解除
SetUseTextureToShader( 0, -1 ) ;
SetUseTextureToShader( 1, -1 ) ;
//アルファテストをデフォルトに
SetDrawAlphaTest( -1 ,0);

という風にアルファテストを常に描画にします
こうしないと、アルファ0部分は描画されずカラーも0(黒)になってしまうからです

こんなカンジでエッジが取れます
エッジエッジ
エッジ1
エッジコメントアウト法

海まで大胆にエッジとみなされているのはなぜだろう…
実際はアルファにエッジを格納しているので、途中描画ではエッジは見られませんです
↑でも書きましたが、エッジと見なされた部分(深度の差が大きい部分)のアルファが0になります

次でそのエッジ部分(アルファが0)にブラーを掛けて描画します
スクリーンは裏画面で、ディフューズに今描画した画面(ここではScreenHandle3)、深度バッファもまたテクスチャに設定します

EdgeAA_2PS.pso
// ディフューズマップテクスチャ
sampler DiffuseMapTexture : register( s0 ) ;
// 深度バッファ
sampler DepthMapTexture : register( s1 ) ;

const static float2 gBlurKernel[8] = {
{ -4, 0 },
{ -3, 0 },
{ -2, 0 },
{ -1, 0 },
{ 1, 0 },
{ 2, 0 },
{ 3, 0 },
{ 4, 0 },
};

const static float gBlurWeights[8] = {
0.002216,
0.008764,
0.026995,
0.064759,
0.064759,
0.026995,
0.008764,
0.002216,
};

//スクリーン比用。ここでは決め打ち(通常はスクリーンサイズをレジスタで渡します)
const static float2 rcpres = float2(1.0f/1024.0f,1.0f/768.0f);

float4 POWDepth(float2 UV) {
float4 colDepth = tex2D(DepthMapTexture, UV);

colDepth = pow(colDepth, 19);

return colDepth;
}

//ブラー
float4 gBlurify(float2 UV, float blurAmt, int4 blurDir)
{
float4 colOutput = 0.0f;
float2 dirMod1 = 0.0f;
float2 dirMod2 = 0.0f;

for (int i = 0; i < 8; i++) {
dirMod1 = gBlurKernel[i].yx;

if (dirMod1.y > 0) dirMod1.y *= blurDir[2];
if (dirMod1.y < 0) dirMod1.y *= blurDir[3];

colOutput += tex2D(DiffuseMapTexture, UV + (gBlurKernel[i].yx * blurAmt) * gBlurWeights[i]);

dirMod2 = gBlurKernel[i].xy;

if (dirMod2.x > 0) dirMod2.x *= blurDir[0];
if (dirMod2.x < 0) dirMod2.x *= blurDir[1];

colOutput += tex2D(DiffuseMapTexture, UV + (gBlurKernel[i].xy * blurAmt) * gBlurWeights[i]);
}

return colOutput / 16;
}

float4 main(float2 Tex : TEXCOORD0) : COLOR0
{
float4 colOutput = tex2D(DiffuseMapTexture, Tex);
float depCenter = POWDepth(Tex).x;

//エッジ(.a=0)とした部分のみブラー処理
if (colOutput.a == 0) {
float4 dirDepth;
dirDepth.x = POWDepth(Tex + float2(-1, 0) * rcpres.xy).x;
dirDepth.y = POWDepth(Tex + float2( 1, 0) * rcpres.xy).x;
dirDepth.z = POWDepth(Tex + float2( 0,-1) * rcpres.xy).x;
dirDepth.w = POWDepth(Tex + float2( 0, 1) * rcpres.xy).x;

float4 depSamp = float4(depCenter - dirDepth.x, depCenter - dirDepth.y, depCenter - dirDepth.z, depCenter - dirDepth.w);
float threshold = 0.016 * depCenter;

depSamp.xyzw = (depSamp.xyzw > threshold);
depSamp = dot(depSamp, 1);

colOutput = gBlurify(Tex, 0.04f, depSamp);
}

/*
if (colOutput.a > 0) {
colOutput=0;
}
*/
return float4(colOutput.rgb, 1);

}

コメントアウトしている部分はエッジのみ描画に使ったものです。無視してください

if (colOutput.a == 0) {
としてアルファが0部分のみブラーが掛かるようになっています

そういえばパフォーマンス測ってないけど、多分FXAAと4xの間くらいだと思います。画質も


説明がめんどくさくなってきましたが、元々失敗作ですしもうゴールしてもいいよね


プロフィール

ペチコートさん

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

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

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