スポンサーサイト

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

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ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。