天天看点

像素着色器入门

一、像素着色器简介

像素着色器是在对每个像素进行光栅化处理期间运行在图形卡GPU上的一段程序。(不同于顶点着色器,Direct3D不会以软件运算方式来模拟像素着色器。)像素着色器实质上是取代固定功能流水线中的多重纹理(multitexturing)环节,而且赋予了我们直接操纵单个像素以及访问每个像素的纹理坐标的能力。这种对像素和纹理坐标直接访问的能力使得我们能够获得各种各样的特殊效果,例如多重纹理、景深(depth of field)、云彩模拟、火焰模拟以及比较复杂的阴影技术。           

二、像素着色器的输入和输出

像素着色器的输入包括每个像素的颜色和纹理坐标,输出为计算所得的每个像素的颜色值。

      每个像素的纹理坐标其实就是指定了纹理中将被映射到当前像素的纹理元的坐标(u , v)。在进入像素着色器之前,Direct3D先根据顶点颜色和顶点纹理坐标计算出每个像素的颜色和纹理坐标。输入像素着色器的颜色和纹理坐标对的个数由顶点着色器输出的颜色和纹理坐标对的个数决定。例如,如果一个顶点着色器输出 2 种颜色和 3 个纹理坐标对,则Direct3D将计算出每个像素的 2 种颜色值和 3 个纹理坐标对,并将这些输出结果输入像素着色器。我们需借助语义语法来将输入的颜色和纹理坐标映射为像素着色器程序中的变量。对于上述例子,我们可以写作:

       struct PS_INPUT
       {
            vector c0 : COLOR0;
            vector c1 : COLOR1;
            float2  t0  : TEXCOORD0;
            float2  t1  : TEXCOORD1;
            float3  t2  : TEXCOORD2;
      }

      struct PS_OUTPUT
      {
            vector finalPixelColor : COLOR0;
     }              

三、使用像素着色器的步骤

(1)编写像素着色器程序
 (2)编译像素着色器程序。
 (3)创建一个LPDIRECT3DPIXELSHADER9接口对象,以表示经过编译的着色器代码的像素着色器。
 (4)用SetPixelShader方法启用像素着色器。
 (5)调用像素着色器的Release方法释放着色器。

  接下来详细介绍每一步的具体操作:           

1、编写像素着色器程序

// Globals
sampler EarthTex;
sampler GalaxyTex;

// Structures
struct PS_INPUT
{
    float2 earth     : TEXCOORD0;
    float2 galaxy    : TEXCOORD1;
};

struct PS_OUTPUT
{
    vector diffuse : COLOR0;
};

// Main
PS_OUTPUT Main(PS_INPUT input)
{
    // zero out members of output
    PS_OUTPUT output = (PS_OUTPUT)0;

    // sample appropriate textures
    vector b = tex2D(EarthTex,      input.earth);
    vector s = tex2D(GalaxyTex, input.galaxy);

    // combine texel colors
    vector c = b * s;

    // increase the intensity of the pixel slightly
    c += 0.1f;

    // save the resulting pixel color
    output.diffuse = c;

    return output;
}

   在PS_INPUT结构体中说明输入像素着色器程序的数据格式,在PS_OUTPUT中说明像素着色器输出的数据格式,在Main方法中说明对像素进行的具体操作。           

2、编译像素着色器程序

ID3DXBuffer* shader = 0;
    ID3DXBuffer* errorBuffer = 0;

    //对着色器程序进行编译
    HRESULT hr = D3DXCompileShaderFromFile(
        L"ps_multitex.txt",
        0, 0,
        "Main",
        "ps_1_1",
        D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, 
        &shader,
        &errorBuffer,
        &MultiCT);           

3、获得指向IDirect3DPixelShader9接口的指针

HRESULT hr = g_pd3dDevice->CreatePixelShader(
        (DWORD*)shader->GetBufferPointer(),
        &MultiPS);           

4、启用像素着色器

g_pd3dDevice->SetPixelShader(MultiPS);           

5、释放资源

调用Release()方法释放。

四、程序源码

程序简要说明:对一个矩形进行多重纹理贴图

 像素着色器程序:

// Globals
sampler EarthTex;
sampler GalaxyTex;

// Structures
struct PS_INPUT
{
    float2 earth      : TEXCOORD0;
    float2 galaxy    : TEXCOORD1;
};

struct PS_OUTPUT
{
    vector diffuse : COLOR0;
};

// Main
PS_OUTPUT Main(PS_INPUT input)
{
    // zero out members of output
    PS_OUTPUT output = (PS_OUTPUT)0;

    // sample appropriate textures
    vector b = tex2D(EarthTex,      input.earth);
    vector s = tex2D(GalaxyTex, input.galaxy);

    // combine texel colors
    vector c = b * s;

    // increase the intensity of the pixel slightly
    c += 0.1f;

    // save the resulting pixel color
    output.diffuse = c;

    return output;
}

 程序源码:

#include <Windows.h>
#include <mmsystem.h>
#include <d3dx9.h>
#pragma warning( disable : 4996 ) // disable deprecated warning 
#include <strsafe.h>
#pragma warning( default : 4996 )
#include <d3dx9math.h>

LPDIRECT3D9             g_pD3D = NULL; 
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL;
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
LPDIRECT3DTEXTURE9  earthtexture = NULL;
LPDIRECT3DTEXTURE9  galaxytexture = NULL;

LPDIRECT3DPIXELSHADER9 MultiPS = 0;
LPD3DXCONSTANTTABLE MultiCT = 0;

D3DXHANDLE EatrhHandle = 0;
D3DXHANDLE GalaxyHandle = 0;

D3DXCONSTANT_DESC earthDesc;
D3DXCONSTANT_DESC galaxyDesc;

struct CUSTOMVERTEX
{
    FLOAT x, y, z;
    FLOAT u0,v0;
    FLOAT u1,v1;
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEX2)

HRESULT InitD3D(HWND hWnd)
{
    // Create the D3D object.
    if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
        return E_FAIL;

    // Set up the structure used to create the D3DDevice
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    d3dpp.EnableAutoDepthStencil = TRUE;
        d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
    // Create the D3DDevice
    if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING,
        &d3dpp, &g_pd3dDevice)))
    {
        return E_FAIL;
    }
    return S_OK;
}

void CreateShader()
{
    ID3DXBuffer* shader = 0;
    ID3DXBuffer* errorBuffer = 0;

    //对着色器程序进行编译
    HRESULT hr = D3DXCompileShaderFromFile(
        L"ps_multitex.txt",
        0, 0,
        "Main",
        "ps_1_1",
        D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, 
        &shader,
        &errorBuffer,
        &MultiCT);

    //创建像素着色器
    hr = g_pd3dDevice->CreatePixelShader(
        (DWORD*)shader->GetBufferPointer(),
        &MultiPS);

    shader->Release();
}

void GetHandles()
{
    EatrhHandle = MultiCT->GetConstantByName(0, "EarthTex");
    GalaxyHandle = MultiCT->GetConstantByName(0, "GalaxyTex");

    UINT count;
    MultiCT->GetConstantDesc(EatrhHandle, &earthDesc, &count);
    MultiCT->GetConstantDesc(GalaxyHandle, &galaxyDesc, &count);

    MultiCT->SetDefaults(g_pd3dDevice);
}

bool CreateTecture()
{
    if(FAILED(D3DXCreateTextureFromFile(g_pd3dDevice,L"galaxy.jpg",&galaxytexture)))
        return false;

    if(FAILED(D3DXCreateTextureFromFile(g_pd3dDevice,L"earth.jpg",&earthtexture)))
        return false;

    return true;
}

HRESULT CreateBuffer()
{
    CUSTOMVERTEX vertices[] =
    {
        {  -10.0f, -10.0f, 0.0f, 0,1,0,1},
        {  -10.0f, 10.0f, 0.0f, 0,0,0,0 },
        {  10.0f, 10.0f, 0.0f, 1,0,1,0},
        {  10.0f, -10.0f, 0.0f, 1,1,1,1},
    };

    // 创建顶点缓存
    if (FAILED(g_pd3dDevice->CreateVertexBuffer(sizeof(vertices),
        0, D3DFVF_CUSTOMVERTEX,
        D3DPOOL_DEFAULT, &g_pVB, NULL)))
    {
        return E_FAIL;
    }

    VOID* pVertices;
    if (FAILED(g_pVB->Lock(0, sizeof(vertices), (void**)&pVertices, 0)))
        return E_FAIL;
    memcpy(pVertices, (void*)vertices, sizeof(vertices));
    g_pVB->Unlock();

    // 创建索引缓存
    WORD indices[] = {0,1,2,0,2,3};

    if( FAILED( g_pd3dDevice->CreateIndexBuffer( sizeof(indices),
                                                  0, D3DFMT_INDEX16,
                                                  D3DPOOL_DEFAULT, &g_pIB, NULL ) ) )
    {
        return E_FAIL;
    }

    VOID* pIndices = NULL;
    if(FAILED(g_pIB->Lock(0, sizeof(indices), (void**)&pIndices,0)))
        return E_FAIL;
    memcpy(pIndices,(VOID*)indices, sizeof(indices));
    g_pIB->Unlock();

    return S_OK;
}

VOID SetupMatrices()
{
    D3DXVECTOR3 position( 0, 0, -20);
    D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
    D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
    D3DXMATRIX V;
    D3DXMatrixLookAtLH(&V, &position, &target, &up);
    g_pd3dDevice->SetTransform(D3DTS_VIEW, &V);

    //D3DXMatrixPerspectiveFovLH()函数中的最远、最近距离为相对于视点的距离(即vEyePt中的距离)
    D3DXMATRIXA16 matProj;
    D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1.0f, 1.0f, 1000.0f);
    g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}

VOID Cleanup()
{
    if( earthtexture != NULL )
        earthtexture->Release();

    if( galaxytexture != NULL )
        galaxytexture->Release();

    if (g_pVB != NULL)
        g_pVB->Release();

    if(g_pIB != NULL)
        g_pIB->Release();

    if (g_pd3dDevice != NULL)
        g_pd3dDevice->Release();

    if (g_pD3D != NULL)
        g_pD3D->Release();

    if(MultiPS != NULL)
        MultiPS->Release();

    if(MultiCT != NULL)
        MultiCT->Release();
}

VOID Render()
{
    CreateShader();
    GetHandles();
    CreateTecture();

    g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0);

    // Begin the scene
    if (SUCCEEDED(g_pd3dDevice->BeginScene()))
    {
        SetupMatrices();

        g_pd3dDevice->SetPixelShader(MultiPS);
        g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
        g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
        g_pd3dDevice->SetIndices(g_pIB);

        g_pd3dDevice->SetTexture(earthDesc.RegisterIndex, earthtexture);
        g_pd3dDevice->SetSamplerState(earthDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
        g_pd3dDevice->SetSamplerState(earthDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
        g_pd3dDevice->SetSamplerState(earthDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

        g_pd3dDevice->SetTexture(galaxyDesc.RegisterIndex, galaxytexture);
        g_pd3dDevice->SetSamplerState(galaxyDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
        g_pd3dDevice->SetSamplerState(galaxyDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
        g_pd3dDevice->SetSamplerState(galaxyDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

        g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
        g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
        g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

        g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,4,0,2);

        g_pd3dDevice->EndScene();
    }
    g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}

//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_DESTROY:
        Cleanup();
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
}

//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
INT WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR, INT)
{
    UNREFERENCED_PARAMETER(hInst);

    // Register the window class
    WNDCLASSEX wc =
    {
        sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
        GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
        L"D3D Tutorial", NULL
    };
    RegisterClassEx(&wc);

    // Create the application's window
    HWND hWnd = CreateWindow(L"D3D Tutorial", L"D3D Tutorial 03: MultiTexture",
        WS_OVERLAPPEDWINDOW, 100, 100, 700, 700,
        NULL, NULL, wc.hInstance, NULL);

    // Initialize Direct3D
    if (SUCCEEDED(InitD3D(hWnd)))
    {
        // Create the scene geometry
        if (SUCCEEDED(CreateBuffer()))
        {
            // Show the window
            ShowWindow(hWnd, SW_SHOWDEFAULT);
            UpdateWindow(hWnd);

            // Enter the message loop
            MSG msg;
            ZeroMemory(&msg, sizeof(msg));
            while (msg.message != WM_QUIT)
            {
                if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                else
                    Render();
            }
        }
    }

    UnregisterClass(L"D3D Tutorial", wc.hInstance);
    return 0;
}           

继续阅读