How do I correctly implement texture pixel to texel correction in direct3d 9?

2k views Asked by At

I have read a couple of articles that describe the necessity to move the texture one half unit of the vertex positions in order to get the correct mapping between texels and pixels. Though I think I understand the theory behind it, when I try to implement the the solution (moving half a unit up to the left), all I get are black lines on the opposite sides of the rendered image.

I get the impression that I either do not adjust the correct x and y parameters or that this does not apply to my particular scenario. I use direct3d 9 with linear filtering and render an image that covers the entire screen. I have tried with both the actual size of the texture and with -1 to +1 (i.e. according to the different solution in the two linked articles). Both approaches give the same effect.

My questions are, when is this correction necessary and is there a correct way to do this that I am missing?

1

There are 1 answers

2
miloszmaki On BEST ANSWER

According to DirectX documentation, offsetting by half a pixel is only necessary "when rendering 2D output using pre-transformed vertices". Pre-transformed vertices are those with D3DFVF_XYZRHW flag specified in IDirect3DDevice9::SetFVF call. In order to draw a transformed vertex correctly, you have to set its position to (posx - 0.5, posy - 0.5, 0, 1) where (posx, posy) are screen-space coordinates of the vertex (in pixels).

Here's a code for rendering a full-screen textured quad:

struct TRANSFORMED_VERTEX
{
    D3DXVECTOR4 pos;
    D3DXVECTOR2 tex;
    static const DWORD FVF;
};
const DWORD TRANSFORMED_VERTEX::FVF = D3DFVF_XYZRHW | D3DFVF_TEX1;



void RenderFullScreenQuad()
{
    D3DSURFACE_DESC desc;
    LPDIRECT3DSURFACE9 pSurf;

    g_pd3dDevice->GetRenderTarget(0, &pSurf);
    pSurf->GetDesc(&desc);
    pSurf->Release();

    float width = (float)desc.Width - 0.5f;
    float height = (float)desc.Height - 0.5f;

    TRANSFORMED_VERTEX v[4];
    v[0].pos = D3DXVECTOR4(-0.5f, -0.5f, 0.0f, 1.0f);
    v[1].pos = D3DXVECTOR4(width, -0.5f, 0.0f, 1.0f);
    v[2].pos = D3DXVECTOR4(-0.5f, height, 0.0f, 1.0f);
    v[3].pos = D3DXVECTOR4(width, height, 0.0f, 1.0f);

    v[0].tex = D3DXVECTOR2(0.0f, 0.0f);
    v[1].tex = D3DXVECTOR2(1.0f, 0.0f);
    v[2].tex = D3DXVECTOR2(0.0f, 1.0f);
    v[3].tex = D3DXVECTOR2(1.0f, 1.0f);

    g_pd3dDevice->SetFVF(TRANSFORMED_VERTEX::FVF);
    g_pd3dDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v, sizeof(TRANSFORMED_VERTEX));
}

Of course, you need to call this function between BeginScene() and EndScene(). You have also to set up the texture and sampler states properly (or shader constants).