透過性 (およびその他のもの) を容易にするために、DirectX9 を使用して C++ で記述されている 3D エンジンで、オブジェクトの 2 段階のレンダリングを設定しようとしています。この 2 段階の方法を使用して、オブジェクトの前にレンダリングされたオブジェクトの端にいくらか怪しい点があることに気付くまで、すべてうまく機能していると思っていました。
2 段階の方法は簡単です。
同じ zbuffer を使用して、同じサイズのオフスクリーン (「サイド」) テクスチャにモデルを描画します (MSAA はどこにも使用されません)。
メイン レンダー ターゲットの上部にオフスクリーン (「サイド」) テクスチャを適切なブレンドで描画し、アルファ テストや書き込みは行いません
左下の画像では、グレーのオブジェクト (街灯柱) を 2 段階でレンダリングし、その前に本体をターゲット テクスチャに直接レンダリングしています。右側のビューは 2 段階のレンダリングが無効になっているため、両方がターゲット サーフェスに直接レンダリングされます。
よく見ると、ターゲット サーフェス上にレンダリングすると、サイド テクスチャが正確に 1 ピクセル「下」および 1 ピクセル「右」にオフセットされているように見えます (ただし、正確にその場でレンダリングされます)。D3DXSaveTextureToFile
これは、以下のスクリーン ショットにオフ スクリーン テクスチャ (プログラムを介してビットマップ ファイルに書き出す) のオーバーレイで確認できます。
最後の画像は、サイド テクスチャのエッジがどこから来ているかを確認できるようにするためです (これは、サイド テクスチャへのレンダリングでz テストが使用されるためです)。左はスクリーン ショート、右はサイド テクスチャです (上に重ねて表示)。
このすべてが、私の「オーバーレイ」があまり効果的ではないと私に信じさせます. メイン レンダー ターゲット上にサイド テクスチャをレンダリングするコードを以下に示します (すべてのシーン レンダリング (オンスクリーンとオフスクリーン) で同じビューポートが使用されることに注意してください)。「効果」オブジェクトは、それ自体LPD3DXEFFECT
である「効果」フィールド (見掛け倒しの名前付けで申し訳ありません) を持つ薄いラッパーのインスタンスLPD3DXEFFECT
です。
void drawSideOver(LPDIRECT3DDEVICE9 dxDevice, drawData* ddat)
{ // "ddat" drawdata contains lots of render state information, but all we need here is the handles for the targetSurface and sideSurface
D3DXMATRIX idMat;
D3DXMatrixIdentity(&idMat); // create identity matrix
dxDevice->SetRenderTarget(0, ddat->targetSurface); // switch to targetSurface
dxDevice->SetRenderState(D3DRS_ZENABLE, false); // disable z test and z write
dxDevice->SetRenderState(D3DRS_ZWRITEENABLE, false);
vertexOver overVerts[4]; // create square
overVerts[0] = vertexOver(-1, -1, 0, 0, 1);
overVerts[1] = vertexOver(-1, 1, 0, 0, 0);
overVerts[2] = vertexOver(1, -1, 0, 1, 1);
overVerts[3] = vertexOver(1, 1, 0, 1, 0);
effect.setTexture(ddat->sideTex); // use side texture as shader texture ("tex")
effect.effect->SetTechnique("over"); // change to "over" technique
effect.setViewProj(&idMat); // set viewProj to identity matrix so 1/-1 map directly
effect.effect->CommitChanges();
setAlpha(dxDevice); // this sets up the alpha blending which works fine
UINT numPasses, pass;
effect.effect->Begin(&numPasses, 0);
effect.effect->BeginPass(0);
dxDevice->SetVertexDeclaration(vertexDecOver);
dxDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, overVerts, sizeof(vertexOver));
effect.effect->EndPass();
effect.effect->End();
dxDevice->SetRenderState(D3DRS_ZENABLE, true); // revert these so we don't mess everything up drawn after this
dxDevice->SetRenderState(D3DRS_ZWRITEENABLE, true);
}
VertexOver 構造体とコンストラクターの C++ 側の定義 (HLSL 側は以下のどこかに示されています):
struct vertexOver
{
public:
float x;
float y;
float z;
float w;
float tu;
float tv;
vertexOver() { }
vertexOver(float xN, float yN, float zN, float tuN, float tvN)
{
x = xN;
y = yN;
z = zN;
w = 1.0;
tu = tuN;
tv = tvN;
}
};
頂点を再作成して GPU に渡すことの非効率性は脇に置きます。私が本当に知りたいのは、この方法がうまくいかない理由と、このようなテクスチャをアルファ ブレンドでオーバーレイするためのより良い方法があるかどうかです。この問題は発生しません
この問題ではテクスチャ サンプリングが多少重要である可能性があると考えましたが、オプションをいじってもあまり役に立たないようです (たとえば、LINEAR フィルタを使用すると、オフセットが明確でないことを暗示していると予想されるため、あいまいになります) -1 ピクセルの不一致としてカットされます)。シェーダー コード:
struct VS_Input_Over
{
float4 pos : POSITION0;
float2 txc : TEXCOORD0;
};
struct VS_Output_Over
{
float4 pos : POSITION0;
float2 txc : TEXCOORD0;
float4 altPos : TEXCOORD1;
};
struct PS_Output
{
float4 col : COLOR0;
};
Texture tex;
sampler texSampler = sampler_state { texture = <tex>;magfilter = NONE; minfilter = NONE; mipfilter = NONE; AddressU = mirror; AddressV = mirror;};
// side/over shaders (these make up the "over" technique (pixel shader version 2.0)
VS_Output_Over VShade_Over(VS_Input_Over inp)
{
VS_Output_Over outp = (VS_Output_Over)0;
outp.pos = mul(inp.pos, viewProj);
outp.altPos = outp.pos;
outp.txc = inp.txc;
return outp;
}
PS_Output PShade_Over(VS_Output_Over inp)
{
PS_Output outp = (PS_Output)0;
outp.col = tex2D(texSampler, inp.txc);
return outp;
}
「ブレンドブリット」などを探しましたが、何も見つかりませんでした。他の関連する検索では、正射投影でクワッドをレンダリングすることがこれを行う方法であることを示唆するフォーラムしか表示されませんでした。
この問題について詳しく説明しすぎて申し訳ありませんが、興味深いと同時に腹立たしいことでもあります。フィードバックをいただければ幸いです。