DirectX 9 + Shader Effect: disable a smooth transition of color

1.6k views Asked by At

I created a simple shader effect, which must paint left-half part is blue color and right-half part is red color. But the transition is not sharp colors and is made with a gradient see picture: enter image description here

I need to turn off the smooth transition of colors like here: enter image description here

Shader effect code:

struct VSInputTxVc
{
    float4  Position    : POSITION;
    float2  TexCoord    : TEXCOORD0;
    float4  Color       : COLOR;
};

struct VS_OUTPUT
{
    float4 Position : POSITION;
    float4 Color : COLOR;
};

//Vertex Shader
VS_OUTPUT RenderSceneVS(VSInputTxVc VertexIn)
{
    VS_OUTPUT VertexOut;
    VertexOut.Position = VertexIn.Position;

    if( VertexOut.Position.x > 0 )
    {
        VertexOut.Color = float4(1,0,0,1);  // Right half-part must have red color
    }
    else
    {
        VertexOut.Color = float4(0,0,1,1); // Left half-part must have blue color
    }

    return VertexOut;
}

//Pixel Shader
float4 RenderScenePS(float4 Color : COLOR) : COLOR
{
    return Color;
}

technique RenderScene
{
    pass P0
    { 
        VertexShader  = (compile vs_1_1 RenderSceneVS());
        PixelShader  = (compile ps_2_0 RenderScenePS());
    }
}

I draw full-screen primitive { -1,-1 : -1, 1 : 1,-1 : 1,1 } and I play with different Blending modes but it not help.

C++ code:

   //Create the Direct3D Object
    LPDIRECT3D9 pD3D = NULL;
    if( NULL == (pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
      return E_FAIL;

    //Setup the device presentation parameters
    D3DPRESENT_PARAMETERS d3dpp; 
    ZeroMemory( &d3dpp, sizeof(d3dpp) );
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

    //The final step is to use the IDirect3D9::CreateDevice method to create the Direct3D device, as illustrated in the
    //following code example.
    LPDIRECT3DDEVICE9 pd3dDevice = NULL;
    if( FAILED( pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                    D3DCREATE_HARDWARE_VERTEXPROCESSING,
                                    &d3dpp, &pd3dDevice ) ) )
    {
        MessageBox(hWnd, L"No HAL HARDWARE_VERTEXPROCESSING! Sample will exit!", NULL, 0);
        pD3D->Release();
        pD3D = NULL;
        return E_FAIL;
    }

    //set the vertex buffer size 4 vertices * vertex structure size
    UINT uiBufferSize = 4*sizeof(COLORED_VERTEX);

    //create the buffer
    if( FAILED( pd3dDevice->CreateVertexBuffer( uiBufferSize,
            D3DUSAGE_WRITEONLY, D3DFVF_COLOREDVERTEX, D3DPOOL_DEFAULT, &g_lpVertexBuffer, NULL ) ) )
        return E_FAIL;

    COLORED_VERTEX* pVertices;
    //lock the buffer for writing
    if( FAILED( g_lpVertexBuffer->Lock( 0, uiBufferSize, (void**)&pVertices, 0 ) ) )
        return E_FAIL;

    //write the vertices. Here a simple rectangle
    pVertices[0].x =  -1.0f; //left
    pVertices[0].y =  -1.0f; //bottom
    pVertices[0].z =   0.0f;
    pVertices[0].color =  0xffff0000; //red

    pVertices[1].x =  -1.0f; //left
    pVertices[1].y =   1.0f; //top
    pVertices[1].z =   0.0f;
    pVertices[1].color =  0xff0000ff; //blue

    pVertices[2].x =   1.0f; //right
    pVertices[2].y =  -1.0f; //bottom
    pVertices[2].z =   0.0f;
    pVertices[2].color =  0xff00ff00; //green

    pVertices[3].x =  1.0f; //right
    pVertices[3].y =  1.0f; //top 
    pVertices[3].z =  0.0f;
    pVertices[3].color =  0xffffffff; //white

    //unlock the buffer
    g_lpVertexBuffer->Unlock();

    //set the Vertex Format
    pd3dDevice->SetFVF( D3DFVF_COLOREDVERTEX );

    //transfer the buffer to the gpu
    pd3dDevice->SetStreamSource( 0, g_lpVertexBuffer, 0, sizeof(COLORED_VERTEX) );

    //create an effect
    ID3DXBuffer* errorBuffer = 0;
    wchar_t EffectFileName[] = L"MinimalEffect.fx";
    if(FAILED(D3DXCreateEffectFromFile( pd3dDevice, EffectFileName, NULL, 
        NULL, D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, NULL, &g_lpEffect, &errorBuffer )))
    {
        wchar_t buf[2048];
        wchar_t tmp[2048];
        swprintf_s(buf, L"D3DXCreateEffectFromFile() Error create of effect \"%s\" \n\n", EffectFileName);
        if( errorBuffer )
        {
            lstrcatW(buf, charToWchar_t( (char*)errorBuffer->GetBufferPointer(), tmp ) );
            errorBuffer->Release();
        }
        MessageBox(hWnd, buf, NULL, 0);
        pD3D->Release();
        pD3D = NULL;
        return E_FAIL;
    }

    // Choice the tehnique of shader
    D3DXHANDLE hTechnick;
    if( D3D_OK != g_lpEffect->FindNextValidTechnique( NULL, &hTechnick ) )
    {
        wchar_t buf[1024];
        swprintf_s(buf, L"[FindNextValidTechnique] No finded any valid tehnique in shader effect. \n\n File: \"%s\"",
        EffectFileName );
        MessageBoxW(hWnd, buf, L"Error", MB_OKCANCEL|MB_SETFOREGROUND|MB_TOPMOST);
        return false;
    }
    g_lpEffect->SetTechnique( hTechnick );

    //D3DXMatrixIdentity(&g_ShaderMatrix);
    //g_lpEffect->SetMatrix( "ShaderMatrix", &g_ShaderMatrix );

    //pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
    //pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MIN);
    //pd3dDevice->SetRenderState(D3DRS_COLORVERTEX, FALSE);

    MSG msg; 
    while( g_bContinue )
    {
        //Clear render region with blue
        pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );

        //before rendering something, you have to call this
        pd3dDevice->BeginScene();

        //rendering of scene objects happens here

        //begin the effect
        UINT uiPasses = 0;
        g_lpEffect->Begin(&uiPasses, 0);
        for (UINT uiPass = 0; uiPass < uiPasses; uiPass++)
        {
            //render an effect pass
            g_lpEffect->BeginPass(uiPass);

            //render the rectangle
            pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );

            g_lpEffect->EndPass();
        }
        g_lpEffect->End();

        //after the scene call
        pd3dDevice->EndScene();

        //update screen = swap front and backbuffer
        pd3dDevice->Present(NULL, NULL, NULL, NULL);

        // A window has to handle its messages.
        TranslateMessage( &msg );
        DispatchMessage( &msg );
        PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
    }

Vertex data type:

//Definition of the Vertex Format including position and diffuse color
#define D3DFVF_COLOREDVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)

struct COLORED_VERTEX
{
    float x, y, z;  //Position
    DWORD color;    //Color
};
2

There are 2 answers

3
Gnietschow On BEST ANSWER

You're setting the color of the vertices of the quad. All information outputted by the vertexshader is automatically interpolated between the three vertices of a triangle by the rasterizer to generate the fragments. These are passed to the pixelshader, which simply returns the given color leading to the gradient. To circumvent this behaviour, you have 2 possibilities:

First you could branch your color in the pixelshader, e.g. by testing for the texture coordinate.

struct VS_OUTPUT
{
    float4 Position : POSITION;
    float2 Tex: TEXCOORD0;
};

//Vertex Shader
VS_OUTPUT RenderSceneVS(VSInputTxVc VertexIn)
{
    VS_OUTPUT VertexOut;
    VertexOut.Position = VertexIn.Position;
    VertexOut.Tex = VertexIn.TexCoord;

    return VertexOut;
}

//Pixel Shader
float4 RenderScenePS(float2 Tex: TEXCOORD0) : COLOR
{
    float4 Color = 0;
    if( Tex.x > 0.5 )
    {
        Color = float4(1,0,0,1);  // Right half-part must have red color
    }
    else
    {
        Color = float4(0,0,1,1); // Left half-part must have blue color
    }
    return Color;
}

The second approach is to render two half-full-screen quads. Then you can set the color in the vertices and use almost your current approach via the vertexshader.

0
Jarikus On

Updates code of shader after 'Gnietschow' remarks:

struct VSInputTxVc
{
    float4  Position    : POSITION;
    float2  TexCoord    : TEXCOORD0;
    float4  Color       : COLOR;
};

struct VS_OUTPUT
{
    float4 Position : POSITION;
    float2 Tex: TEXCOORD0;
};

//Vertex Shader
VS_OUTPUT RenderSceneVS(VSInputTxVc VertexIn)
{
    VS_OUTPUT VertexOut;
    VertexOut.Position = VertexIn.Position;

    // Convert coordinates from "POSITION" to "TEXCOORD0"
    // And normalize coordinates from [-1,1] to [0,1] (only for DirectX 9)
    // Looks like black magic but works fine ;)
    VertexOut.Tex.x = VertexIn.Position.x + 0.5;
    VertexOut.Tex.y = VertexIn.Position.y + 0.5;

    return VertexOut;
}

//Pixel Shader
float4 RenderScenePS(float2 Tex: TEXCOORD0) : COLOR
{
    float4 Color = 0;

    if( Tex.x > 0.5 )
    {
        Color = float4(1,0,0,1);  // Right half-part must have red color
    }
    else
    {
        Color = float4(0,0,1,1); // Left half-part must have blue color
    }
    return Color;
}

technique RenderScene
{
    pass P0
    { 
        VertexShader  = (compile vs_1_1 RenderSceneVS());
        PixelShader  = (compile ps_2_0 RenderScenePS());
    }
}