天天看點

【寒江雪】Effect架構Effect架構簡述Effect架構的使用建立輸入布局Effect的使用旋轉立方體代碼

Effect架構簡述

  之前我們已經提到過HLSL檔案。并且我還編寫程式編譯它,加載它,使用它。但是那樣使用未免有點複雜。需要繪制複雜圖形的時候,我們可以直接使用Effect架構來加載.fx檔案.這裡提到的.fx檔案和我們之前提到的.HLSL檔案差不多,文法上基本相同。我們把它編譯之後是與一個ID3DX11Effect接口關聯在一起,被關聯的接口就成為了它的經紀人,把外界的資料傳遞給它,比如設定各種常量矩陣。我們通過ID3DX11EffectTechnique來調用其中的着色器。着色器的設定在.fx檔案中完成

定義與外部互動的變量

  在Effect檔案與外部互動的變量通常定義在cbuffer中,或者也可以像HLSL檔案那樣直接定義在全局。如下所示:

cbuffer MatrixBuffer
{
    matrix World;   //世界坐标變換矩陣
    matrix View;    //觀察坐标變換矩陣
    matrix Projection; //投影坐标變換矩陣
    float4 EyePosition;//視點距離
};

Texture2D Texture;  //紋理變量
           

  外部程式通過設定這些變量,供着色器在渲染的時候使用.

Technique

  每個Effect檔案至少包含一個Technique,每個Technique至少包含一個通道.定義方式如下:

//
//定義Techinique
/
technique11 TexTech
{
    pass P0
    {
        SetVertexShader(CompileShader(vs_5_0,VSMain()));
        SetGeometryShader(NULL);
        SetPixelShader(CompileShader(ps_5_0,PSMain()));
    }
}
           

  頂點着色器和像素着色器就很好編寫了.編寫好直接使用即可.

Effect架構的使用

  在Direct3D11中,Effect架構由兩個接口管理着,一個就是經紀人,一個就是執行者.它們分别是

ID3DX11Effect *g_pEffect = NULL;
ID3DX11EffectTechnique *g_pEffectTechnique = NULL;
           

  ID3DX11Effect就是充當着經紀人的角色,溝通着Effect程式内部與外部程式.而ID3DX11EffectTechnique則管理着通道.負責對頂點進行渲染.

編譯Effect檔案

  編譯Effect檔案與之前編譯.HLSL檔案相同,都是通過D3DX11CompileFromFile來完成.不過這裡要注意的是,之前使用的着色器模型要改成fx_5_0.

  之前提到有一個flag2,在MSDN上說明了,建議設為0比較好。那就用0吧。

  下面給出一個調用執行個體:

hr = D3DX11CompileFromFile(L"SimpleShader.fx", NULL, NULL, NULL,
"fx_5_0", D3DCOMPILE_ENABLE_STRICTNESS, ,
NULL, &pTechBlob, NULL, NULL);
    if (FAILED(hr)) {
        ::MessageBox(NULL, L"fx 載入失敗", L"Error", MB_OK);
        return hr;
    }
           

建立Effect

  Effect檔案編譯成功之後,它的位元組碼就儲存在記憶體當中。我們要從記憶體建立Effect接口對象,也就是聘用一個經紀人來管理它.

  通過調用D3DX11CreateEffectFromMemory來完成.該函數的原型如下

HRESULT D3DX11CreateEffectFromMemory(
   void          *pData,
   SIZE_T        DataLength,
   UINT          FXFlags,
   ID3D11Device  *pDevice,
   ID3DX11Effect **ppEffect
);
           
  • void *pData:一個指針,指向Effect資料所在記憶體段的首位址
  • SIZE_T DataLength:資料段長度
  • UINT FXFlags:目前暫時沒有flags,設定為0即可
  • ID3D11Device *pDevice:很明顯,Direct3D裝置接口
  • ID3DX11Effect **ppEffect:Effect接口,就是我們之前說的經紀人.

擷取ID3DX11EffectTechnique接口

  ID3DX11EffectTechnique接口是通過ID3DX11Effect的GetTechniqueByName或者GetTechniqueByIndex方法得到的,這兩個方法的使用都很簡單,隻需要傳入相應的參數就行.

  GetTechniqueByName指的就是根據名字尋找Technique

  GetTechniqueByIndex就是根據索引号來尋找Technique

建立輸入布局

  建立Effect的輸入布局,必須提前擷取通道的描述資訊,以便在建立輸入布局的時候要用到.

  前面說過,ID3DX11EffectTechnique就是負責管理通道的接口,可以通過調用它的GetPassByIndex或者GetPassByName來擷取類型為ID3DX11EffectPass的通道接口對象.通過該接口的GetDesc就可以擷取到通道的相關參數資訊.

  GetDesc隻有一個D3DX11_PASS_DESC指針的參數,它的原型如下:

HRESULT GetDesc(
    _Out D3DX11_PASS_DESC *pDesc
    );
           

  而提到D3DX11_PASS_DESC就順便把該結構體的内部資訊展示出來:

struct D3DX11_PASS_DESC
{
    LPCSTR Name;                    // Name of this pass (nullptr if not anonymous)    
    uint32_t Annotations;           // Number of annotations on this pass

    uint8_t *pIAInputSignature;     // Signature from VS or GS (if there is no VS)
                                    // or nullptr if neither exists
    size_t IAInputSignatureSize;    // Singature size in bytes                                

    uint32_t StencilRef;            // Specified in SetDepthStencilState()
    uint32_t SampleMask;            // Specified in SetBlendState()
    FLOAT BlendFactor[];           // Specified in SetBlendState()
};
           

  其中的注釋已經相當清楚,就不再做過多的解釋了.

  下面是一個調用示例:

g_technique = g_effect->GetTechniqueByName("TexTech");
    D3DX11_PASS_DESC PassDesc;
    g_technique->GetPassByIndex()->GetDesc(&PassDesc);
           

  當調用了GetDesc得到通道的參數資訊之後,可以通過D3D11_INPUT_ELEMENT_DESC結構體數組來描述輸入的元素特征。接着通過裝置接口調用CreateInputLayout即可獲得輸入布局.

  下面建立的示例代碼:

hr = g_pd3dDevice->CreateInputLayout(
        layout,         //上面定義的D3D11_INPUT_ELEMENT_DESC數組
        numElements,    //D3D11_INPUT_ELEMENT_DESC數組的元素個數
        PassDesc.pIAInputSignature,//Effect Pass描述的輸入辨別
        PassDesc.IAInputSignatureSize,//Effect Pass描述的輸入辨別的大小
        &g_vertexLayout);//傳回生成的輸入布局對象
           

  輸入布局建立成功基本上就完成了初始化的工作。在後續的渲染操作中需要使用到輸入布局的時候,隻需要通過裝置上下文接口來設定一下.

Effect的使用

  遊戲中每個頂點都不會是一成不變的。我們在Effect檔案中通常會定義一些變量,用于和外部程式溝通。我們可以從外部程式設定這些變量的值.

  明星都不會直接與你打交道的,都是經紀人在打點這一切。是以Effect程式也是相當地高貴,我們隻能通過ID3DX11Effect接口對象來設定變量的值.

  我們可以通過ID3DX11Effect接口對象的GetVariableByName或者GetVariableByIndex或者GetVariableBySemantic方法來擷取變量接口:ID3DX11EffectVariable.

  通過ID3DX11EffectVariable接口可以将得到的變量解讀為某一種類型的接口.如若要解釋為矩陣,則調用它的AsMatrix方法即可.然後通過SetMatrix方法來将值設定到變量中.

  設定完成之後,就需要對頂點進行着色。這是ID3DX11EffectPass接口對裝置上下文接口的一次調用,很明顯,在裝置上下文接口中應該已經設定好了頂點緩存,或者頂點緩存+索引緩存.接着通過ID3DX11EffectPass接口的Apply方法即可把頂點作為着色器的輸入參數,輸出結果頂點.

  這一過程的示例代碼如下:

//設定世界坐标系
        g_effect->GetVariableByName("World")->AsMatrix()->SetMatrix((float*)&world);
        //設定觀察坐标系
        g_effect->GetVariableByName("View")->AsMatrix()->SetMatrix((float*)&view);
        //設定投影坐标系
        g_effect->GetVariableByName("Projection")->AsMatrix()->SetMatrix((float*)&projection);

        //定義一個D3DX11_TECHNIQUE_DESC對象來描述technique
        //D3DX11_TECHNIQUE_DESC techDesc;
        //g_technique->GetDesc(&techDesc);//擷取techinique的描述
        //擷取通道Pass,把它設定到執行上下文中
        g_technique->GetPassByIndex()->Apply(, g_pImmediateContext);


        //繪制立方體,注意,這裡調用的是DrawIndexed
        g_pImmediateContext->DrawIndexed(, , );
        g_pSwapChain->Present(, );
           

旋轉立方體代碼

Cube.fx

cbuffer ConstantBuffer
{
    matrix WORLD;
    matrix VIEW;
    matrix PROJECTION;
};

struct VS_INPUT
{
    float4 Pos:SV_POSITION;
    float4 Color:COLOR;
};

VS_INPUT VS(float4 Pos:POSITION, float4 Color : COLOR)
{
    VS_INPUT input = (VS_INPUT);
    input.Pos = mul(Pos, WORLD);
    input.Pos = mul(input.Pos, VIEW);
    input.Pos = mul(input.Pos, PROJECTION);

    input.Color = Color;
    return input;
}


float4 PS(VS_INPUT input):SV_TARGET
{
    return input.Color;
}

technique11 CubeTex
{
    pass P0 {
        SetVertexShader(CompileShader(vs_5_0, VS()));
        SetGeometryShader(NULL);
        SetPixelShader(CompileShader(ps_5_0, PS()));
    }
};
           

Cube.h

#pragma once
#include"dxUtil.h"
struct Vertex
{
    XMFLOAT3 Position;
    XMFLOAT4 Color;
};
class Cube
{
private:
    ID3DX11Effect     *m_pEffect;
    ID3DX11EffectTechnique *m_pTechnique;

    ID3D11InputLayout *m_pInputLayout;  //輸入布局
    ID3D11Buffer      *m_pVertexBuffer;//頂點緩存
    UINT               m_uStrid;
    UINT               m_uOffset;
    ID3D11Buffer      *m_pIndexBuffer;//索引緩存
    XMFLOAT4X4 m_world; //物體的位置
public:
    Cube()
        :m_pEffect(NULL),m_pTechnique(NULL),m_pInputLayout(NULL),m_pVertexBuffer(NULL),m_pIndexBuffer(NULL)
    {
        XMStoreFloat4x4(&m_world, XMMatrixIdentity());
    }

    HRESULT Init(DirectSystem* d3dSys) {
        HRESULT hr;
        /*建立Effect*/
        ID3DBlob *effectFileBlob = NULL;
        hr=D3DX11CompileFromFile(L"Cube.fx", NULL, NULL, NULL, "fx_5_0", NULL, , NULL, &effectFileBlob, NULL, NULL);

        if (FAILED(hr))
        {
            return hr;
        }

        hr = D3DX11CreateEffectFromMemory(
            effectFileBlob->GetBufferPointer(),
            effectFileBlob->GetBufferSize(),
            , d3dSys->D3DDevice(),
            &m_pEffect,
            NULL
            );

        m_pTechnique = m_pEffect->GetTechniqueByName("CubeTex");

        /*建立輸入布局*/
        D3D11_INPUT_ELEMENT_DESC layout[] = {
            {
                "POSITION",,DXGI_FORMAT_R32G32B32_FLOAT,,,D3D11_INPUT_PER_VERTEX_DATA,
            },
            {
                "COLOR",,DXGI_FORMAT_R32G32B32A32_FLOAT,,,D3D11_INPUT_PER_VERTEX_DATA,
            }
        };

        UINT numLayoutSize = ARRAYSIZE(layout);
        D3DX11_PASS_DESC passDesc;
        m_pTechnique->GetPassByIndex()->GetDesc(&passDesc);

        d3dSys->D3DDevice()->CreateInputLayout(layout, numLayoutSize, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, &m_pInputLayout);



        /*建立頂點緩存*/
        Vertex vertices[]=
        {
            { XMFLOAT3(-,,-),XMFLOAT4(,,,) },
            { XMFLOAT3(,,-),XMFLOAT4(,,,) },
            { XMFLOAT3(,,),XMFLOAT4(,,,) },
            { XMFLOAT3(-,,),XMFLOAT4(,,,) },
            { XMFLOAT3(-,-,-),XMFLOAT4(,,,) },
            { XMFLOAT3(,-,-),XMFLOAT4(,,,) },
            { XMFLOAT3(,-,),XMFLOAT4(,,,) },
            { XMFLOAT3(-,-,),XMFLOAT4(,,,) }
        };//頂點集合

        D3D11_BUFFER_DESC vertexDesc;
        ::ZeroMemory(&vertexDesc, sizeof(D3D11_BUFFER_DESC));
        vertexDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
        vertexDesc.ByteWidth = sizeof(Vertex) * ;
        vertexDesc.CPUAccessFlags = ;
        vertexDesc.MiscFlags = ;
        vertexDesc.StructureByteStride = sizeof(Vertex);
        vertexDesc.Usage = D3D11_USAGE_DEFAULT;

        D3D11_SUBRESOURCE_DATA vertexData;
        ::ZeroMemory(&vertexData,sizeof(D3D11_SUBRESOURCE_DATA));
        vertexData.pSysMem = vertices;
        d3dSys->D3DDevice()->CreateBuffer(&vertexDesc, &vertexData, &m_pVertexBuffer);
        m_uStrid = sizeof(Vertex);
        m_uOffset = ;


        /*建立索引緩存*/
        WORD indexs[] = {
            ,,,
            ,,,
            ,,,
            ,,,
            ,,,
            ,,,
            ,,,
            ,,,
            ,,,
            ,,,
            ,,,
            ,,
        };
        UINT numElements = ARRAYSIZE(indexs);
        D3D11_BUFFER_DESC indexDesc;
        indexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
        indexDesc.ByteWidth = sizeof(WORD)*numElements;
        indexDesc.CPUAccessFlags = ;
        indexDesc.MiscFlags = ;
        indexDesc.StructureByteStride = sizeof(WORD);
        indexDesc.Usage = D3D11_USAGE_DEFAULT;
        D3D11_SUBRESOURCE_DATA indexData;
        indexData.pSysMem = indexs;
        d3dSys->D3DDevice()->CreateBuffer(&indexDesc, &indexData, &m_pIndexBuffer);

    }

    VOID Update(float deltaTime)
    {
        static float angle = ;
        angle += deltaTime;
        if (angle >= )
            angle = ;

        //XMMATRIX world = XMLoadFloat4x4(&m_world);
        XMMATRIX mov = XMMatrixTranslation(, , );
        XMVECTOR axis = XMVectorSet(, , , );
        XMMATRIX rotAxis = XMMatrixRotationAxis(axis, angle);
        XMMATRIX rot = XMMatrixRotationY(angle);
        XMMATRIX world = rotAxis*mov*rot;
        XMStoreFloat4x4(&m_world, world);
    }


    HRESULT Draw(DirectSystem* d3dSys)
    {
        d3dSys->D3DDeviceContext()->IASetVertexBuffers(, , &m_pVertexBuffer,&m_uStrid,&m_uOffset);
        d3dSys->D3DDeviceContext()->IASetIndexBuffer(m_pIndexBuffer, DXGI_FORMAT_R16_UINT, );
        d3dSys->D3DDeviceContext()->IASetInputLayout(m_pInputLayout);
        d3dSys->D3DDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        XMMATRIX world;
        world=XMLoadFloat4x4(&m_world);

        XMMATRIX view;
        XMMATRIX projection;
        view = d3dSys->GetViewMatrix();
        projection = d3dSys->GetProjectionMatrix();
        m_pEffect->GetVariableByName("WORLD")->AsMatrix()->SetMatrix((float*)&world);
        m_pEffect->GetVariableByName("VIEW")->AsMatrix()->SetMatrix((float*)&view);
        m_pEffect->GetVariableByName("PROJECTION")->AsMatrix()->SetMatrix((float*)&projection);
        m_pTechnique->GetPassByIndex()->Apply(, d3dSys->D3DDeviceContext());
        d3dSys->D3DDeviceContext()->DrawIndexed(, , );

        return S_OK;
    }
    ~Cube() {
        SAFE_RELEASE(m_pTechnique);
        SAFE_RELEASE(m_pEffect);
        SAFE_RELEASE(m_pVertexBuffer);
        SAFE_RELEASE(m_pIndexBuffer);
    }
};
           

Main.cpp核心部分

MSG msg = {  };
    while (msg.message != WM_QUIT)
    {

        if (PeekMessage(&msg,NULL, , , PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else {
            static float lastTime = f;
            float currentTime = (float)timeGetTime();
            float deltaTime = (currentTime - lastTime)*0;
            g_pCube->Update(deltaTime);
            g_pDirect->Direct3D_Clear();
            g_pCube->Draw(g_pDirect);

            g_pDirect->Direct3D_Present();
            lastTime = currentTime;
        }
    }
           

Copyright© by 寒江雪

Date:2016.12.30

繼續閱讀