精确纹理叠加

时间:2013-05-18 16:20:55

标签: c++ directx

我正在尝试在3D引擎中设置一个两阶段渲染对象,我正在使用DirectX9用C ++编写,以促进透明度(以及其他方面)。我想到这一切都很好用,直到我注意到使用这两个阶段方法在对象之前渲染的对象边缘有些狡猾。

两阶段方法很简单:

  • 使用相同的zbuffer(不使用任何MSAA)将模型绘制到相同大小的屏幕外(“侧面”)纹理

  • 使用合适的混合物在主渲染目标的顶部绘制离屏(“侧面”)纹理,不进行alpha测试或写入

在下面的图像中,左视图是灰色物体(灯柱)的两个阶段渲染,其前面的主体直接渲染到目标纹理。右视图禁用了两阶段渲染,因此两者都直接渲染到目标曲面上。

Dodgy/Not Dodgy Comparison

在仔细检查时,就好像在目标表面上渲染时,侧面纹理正好偏移1个像素“向下”和1个像素“向右”(但是就地渲染正确)。这可以通过下面的屏幕截图在屏幕外纹理(通过D3DXSaveTextureToFile将我的程序写入位图文件)的叠加中看到。

enter image description here

最后一张图像,这样你就可以看到边纹理边缘的来源(这是因为渲染到边纹理 使用z测试)。左边是屏幕短,右边是侧面纹理(如上面覆盖)。

enter image description here

这一切让我相信我的“覆盖”效果不是很好。在主渲染目标上渲染侧面纹理的代码如下所示(请注意,相同的视口用于所有场景渲染(打开和关闭屏幕))。 “效果”对象是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的效率低下每个都被拉开,我真正想知道的是为什么这个方法不能正常工作,如果是使用alpha混合覆盖纹理的更好方法 不会展现此问题

我认为纹理采样在这个问题上可能有些重要,但是对选项的搞乱并没有多大帮助(例如,使用LINEAR滤波器只会使它模糊,因为你可能会暗示偏移是'n' t像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;
}

我看过“混合Blit”或其他东西,但我找不到任何东西,而其他相关的搜索只提出论坛暗示用正交投影渲染四边形就是这样做的方法

很抱歉,如果我已经为这个问题提供了太多细节,但它既有趣又令人愤怒,任何反馈都会非常感激。

2 个答案:

答案 0 :(得分:2)

我认为你的问题是纹素到像素的映射。您必须使用半像素偏移屏幕对齐的四边形,以匹配直接与屏幕像素相关的纹素。这个问题在这里有解释:Directly Mapping Texels to Pixels (MSDN)

答案 1 :(得分:0)

对于其他任何打过类似墙的人来说,我的特定的问题通过调整发送到GPU的顶点的U和V值来解决覆盖的纹理三角形:

for (int i = 0; i < 4; i++)
{
    overVerts[i].tu += 0.5 / (float)ddat->targetVp->Width; // ddat->targetVp is the viewport in use, and the viewport is the same size as the texture
    overVerts[i].tv += 0.5 / (float)ddat->targetVp->Height;
}

请参阅Gnietschow的答案提供的Directly Mapping Texels to Pixels,以解释为什么这是有道理的。