DirectX 9 + Shader Effect: отключить плавный переход цвета

Я создал простой шейдерный эффект, который должен окрашивать левую половину в синий цвет, а правую половину в красный цвет. Но переход не резких цветов и сделан с градиентом см. картинку: введите здесь описание изображения

Мне нужно отключить плавный переход цветов, как здесь: введите здесь описание изображения

Код эффекта шейдера:

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
   //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);
    }
0 RenderScenePS()); } }

Я рисую полноэкранный примитив {-1,-1:-1, 1:1,-1:1,1} и играю с разными режимами наложения, но это не помогает.

Код C++:

   //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);
    }

Тип данных вершины:

//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
};

person Jarikus    schedule 25.11.2014    source источник


Ответы (2)


Вы устанавливаете цвет вершин четырехугольника. Вся информация, выдаваемая вершинным шейдером, автоматически интерполируется растеризатором между тремя вершинами треугольника для создания фрагментов. Они передаются пиксельному шейдеру, который просто возвращает заданный цвет, ведущий к градиенту. Чтобы обойти это поведение, у вас есть 2 возможности:

Сначала вы можете разветвить свой цвет в шейдере пикселей, например. путем тестирования координаты текстуры.

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;
}

Второй подход заключается в рендеринге двух полуполноэкранных четырехугольников. Затем вы можете установить цвет в вершинах и использовать почти ваш текущий подход через вершинный шейдер.

person Gnietschow    schedule 26.11.2014
comment
Привет, я использую ваш метод с пиксельным шейдером, но все точки имеют Tex.X = 1.0, а код: //Pixel Shader float4 RenderScenePS(float2 Tex: TEXCOORD0) : COLOR { float4 Color = 0; if( Tex.x == 1.0 ) { 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; } заливает все красным цветом. Прикрепил проект: spiribit.com/temp/DemoShaderEffect.zip - person Jarikus; 27.11.2014
comment
Ах, простите, я не видел, чтобы вы не установили координаты текстуры для своих вершин на стороне C++. Если вы замените цвет координатами текстуры в своей структуре и FVF, это должно сработать. - person Gnietschow; 27.11.2014
comment
Gnietschow› Поскольку я не создаю текстуры, я решил преобразовать POSITION в TEXCOORD0: VertexOut.Tex.x = VertexIn.Position.x + 0.5; VertexOut.Tex.y = VertexIn.Position.y + 0.5; код обновления, который я показал ниже. Большое тебе спасибо. - person Jarikus; 27.11.2014

Обновляет код шейдера после замечаний Gnietschow:

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());
    }
}
person Jarikus    schedule 27.11.2014