1. 程式人生 > >Directx11教程(5) 畫一個簡單的三角形(1)

Directx11教程(5) 畫一個簡單的三角形(1)

能夠 元素 screen 寫入 eof poi 訪問 處理 output

原文:Directx11教程(5) 畫一個簡單的三角形(1)

      在本篇教程中,我們將通過D3D11畫一個簡單的三角形。在D3D11中,GPU的渲染主要通過shader來操作(當然還有一些操作是由GPU固定管線完成,比如光柵化操作),最常用的shader操作是頂點shader(vertex shader)和像素shader(pixel shader)。其實shader就是在GPU中執行的代碼,這些代碼被driver編譯成硬件依賴的機器碼,最終被GPU中shader pipe執行,從而完成3D渲染。D3D11中shader是用一種類C的語言HLSL編寫的。

     下面我們先來了解幾個概念:

      三維物體的模型,通常3D對象都是通過mesh(三角形)來表示的。比如我們看到的一個渲染後的球體,實際上它是有許許多多的小的mesh組成。

技術分享圖片技術分享圖片

     頂點緩沖就是一個buffer,用來存放3D物體的頂點數據。

     索引緩沖,就是對頂點緩沖的索引,可以用來減少渲染物體時候傳入顯存的頂點數量。比如一個正方體有8個頂點,同時也是由12個三角形組成(每個面2個三角形),我們渲染3D物體,在硬件層次都是通過三角形來渲染的,所以渲染這12個三角形,就需要36個頂點數據,但是通過索引緩沖,我們只需傳入8個頂點,通過索引不同順序的3個頂點,來實現12個三角形的渲染。

[GPU和系統內存之間,通過PCIE總線連接,傳入數據受總線寬度的影響,所以我們要盡可能減少傳輸數據的數量]

程序的框架現在如下圖所示:

技術分享圖片 

我們增加了3個類:

   ModelClass主要用來建立頂點緩沖、索引緩沖,準備渲染數據。

   CamerClass主要用來得到view矩陣,就是得到攝像機在三維空間的位置和方位。

   ColorShaderClass主要用來處理shader相關的代碼。

下面我們將貼出關鍵的程序代碼:

color.vs的代碼如下:

/////////////
// GLOBALS //
//shader中使用的全局變量都在定義在const buffer中
//這樣shader編譯後,這些變量放在gpu的const buffer中

/////////////
cbuffer MatrixBuffer
{
    matrix worldMatrix;
    matrix viewMatrix;
    matrix projectionMatrix;
};

//////////////
// TYPEDEFS //
//註意:POSITION, COLOR等是我們在定義頂點布局時定義的名字。
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float4 color : COLOR;
};

struct PixelInputType
{
    float4 position : SV_POSITION; //SV表示系統自動定義的格式。
    float4 color : COLOR;
};

////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType ColorVertexShader(VertexInputType input)
{
    PixelInputType output;
   

    //頂點坐標擴展成四個分量,並設置為1,以便矩陣運算
    input.position.w = 1.0f;

    // 乘以3個矩陣,得到clip空間的坐標
    output.position = mul(input.position, worldMatrix);
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);
   
    //直接輸出頂點的顏色(頂點之間的顏色,會在光柵化階段采用插值的方式計算
    output.color = input.color;
   
    return output;
}

color.ps的代碼如下,代碼非常簡單,直接輸出像素的顏色:

//////////////
// TYPEDEFS //
//像素輸入格式
//////////////
struct PixelInputType
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};


////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 ColorPixelShader(PixelInputType input) : SV_TARGET
{
    return input.color;
}


GraphicsClass.h代碼如下:

#pragma once

#include <windows.h>
#include "d3dclass.h"
#include "cameraclass.h"
#include "modelclass.h"
#include "colorshaderclass.h"

/////////////
// GLOBALS //
/////////////
const bool FULL_SCREEN = false; //是否全屏
const bool VSYNC_ENABLED = true; //是否垂直同步
const float SCREEN_DEPTH = 1000.0f; //深度,遠點
const float SCREEN_NEAR = 0.1f; //深度,近點

class GraphicsClass
    {
    public:
        GraphicsClass(void);
        GraphicsClass(const GraphicsClass&);
        ~GraphicsClass(void);
        bool Initialize(int, int, HWND);
        void Shutdown();
        bool Frame();

    private:
        bool Render();

       //定義一個D3DClass類成員變量
        D3DClass* m_D3D;
        CameraClass* m_Camera;
        ModelClass* m_Model;
        ColorShaderClass* m_ColorShader;

    };

GraphicsClass.cpp主要代碼如下:

       首先會計算三個矩陣:世界矩陣(模型坐標空間到世界坐標空間轉化矩陣)、視點矩陣(世界坐標空間轉化到視點坐標空間、或者說是攝像機坐標空間)、投影矩陣(視點坐標空間進行投影操作,在透視投影情況下,產生一個稱作frustum的視錐體,在平行投影的情況下,產生一個長方體)。

      然後在Shader類中執行具體的渲染操作。


bool GraphicsClass::Render()
    {

    D3DXMATRIX viewMatrix, projectionMatrix, worldMatrix;
    bool result;


    // 設置framebuffer.為淺藍色
    m_D3D->BeginScene(0.0f, 0.0f, 0.5f, 1.0f);

    // 得到view矩陣.
   m_Camera->Render();

    // 得到3個矩陣.
    m_Camera->GetViewMatrix(viewMatrix);
    m_D3D->GetWorldMatrix(worldMatrix);
    m_D3D->GetProjectionMatrix(projectionMatrix);

    // 把模型頂點和索引緩沖放入管線,準備渲染.
    m_Model->Render(m_D3D->GetDeviceContext());

    // 用shader渲染.
    result = m_ColorShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix);
    if(!result)
        {
        return false;
        }

   
    //把framebuffer中的圖像present到屏幕上.
    m_D3D->EndScene();

    return true;
    }

 

     ModelClass類的關鍵函數是InitializeBuffers和RenderBuffer,在初始化函數中產生頂點緩沖和索引緩沖,在RenderBuffer函數中,把緩沖綁定到管線,並且指定渲染體元類型。

bool ModelClass::InitializeBuffers(ID3D11Device* device)
    {
    VertexType* vertices;
    unsigned long* indices;
    D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
    D3D11_SUBRESOURCE_DATA vertexData, indexData;
    HRESULT result;

    //首先,我們創建2個臨時緩沖存放頂點和索引數據,以便後面使用。.

    // 設置頂點緩沖大小為3,一個三角形.
    m_vertexCount = 3;

    // 設置索引緩沖大小.
    m_indexCount = 3;

    // 創建頂點臨時緩沖.
    vertices = new VertexType[m_vertexCount];
    if(!vertices)
        {
        return false;
        }

    // 創建索引緩沖.
    indices = new unsigned long[m_indexCount];
    if(!indices)
        {
        return false;
        }
    //創建順時針方向的三角形,左手規則
    // 設置頂點數據.

    vertices[0].position = D3DXVECTOR3(-1.0f, -1.0f, 0.0f);  // 左下
    vertices[0].color = D3DXVECTOR4(1.0f, 1.0f, 0.0f, 1.0f);

    vertices[1].position = D3DXVECTOR3(0.0f, 1.0f, 0.0f);  // 中上.
    vertices[1].color = D3DXVECTOR4(0.0f, 1.0f, 0.0f, 1.0f);

    vertices[2].position = D3DXVECTOR3(1.0f, -1.0f, 0.0f);  // 底右
    vertices[2].color = D3DXVECTOR4(0.0f, 1.0f, 1.0f, 1.0f);

   // 設置索引緩沖數據.
    indices[0] = 0;  // Bottom left.
    indices[1] = 1;  // Top middle.
    indices[2] = 2;  // Bottom right.

    // 設置頂點緩沖描述
    vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = 0;
    vertexBufferDesc.MiscFlags = 0;
    vertexBufferDesc.StructureByteStride = 0;

   // 指向保存頂點數據的臨時緩沖.
    vertexData.pSysMem = vertices;
    vertexData.SysMemPitch = 0;
    vertexData.SysMemSlicePitch = 0;

    // 創建頂點緩沖.
    result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
    if(FAILED(result))
        {
        return false;
        }

    // 設置索引緩沖描述.
    indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount;
    indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;
    indexBufferDesc.StructureByteStride = 0;

    // 指向存臨時索引緩沖.
    indexData.pSysMem = indices;
    indexData.SysMemPitch = 0;
    indexData.SysMemSlicePitch = 0;

   // 創建索引緩沖.
    result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer);
    if(FAILED(result))
        {
        return false;
        }

   // 釋放臨時緩沖.
    delete [] vertices;
    vertices = 0;

    delete [] indices;
    indices = 0;

    return true;
    }

void ModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext)
    {
    unsigned int stride;
    unsigned int offset;


   // 設置頂點緩沖跨度和偏移.
    stride = sizeof(VertexType);
    offset = 0;

    //在input assemberl階段綁定頂點緩沖,以便能夠被渲染
    deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);

    //在input assemberl階段綁定索引緩沖,以便能夠被渲染
    deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);

   // 設置體元語義,渲染三角形列表.
    deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    return;
    }

CameraClass類主要是得到視圖矩陣,代碼就不貼了。

    下面是本章的關鍵部分,shader代碼部分,主要包括打開shader文件,編譯shader文件,綁定常量緩沖,定義頂點布局,調用shader執行渲染操作等等。

ColorShaderClass.cpp主要代碼如下:

bool ColorShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix,
    D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
    {
    bool result;

這幾個shader參數是我們在vs中定義在const buffer中的。

    // 設置shader參數.
    result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix);
    if(!result)
        {
        return false;
        }

    // 用shader渲染指定緩沖頂點
    RenderShader(deviceContext, indexCount);

    return true;
    }

bool ColorShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename)
    {
    HRESULT result;
    ID3D10Blob* errorMessage;
    ID3D10Blob* vertexShaderBuffer;
    ID3D10Blob* pixelShaderBuffer;
    D3D11_INPUT_ELEMENT_DESC polygonLayout[2];
    unsigned int numElements;
    D3D11_BUFFER_DESC matrixBufferDesc;


   // 初始化指針為空.
    errorMessage = 0;
    vertexShaderBuffer = 0;
    pixelShaderBuffer = 0;

   // 編譯vs代碼.
    result = D3DX11CompileFromFile(vsFilename, NULL, NULL, "ColorVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL,
        &vertexShaderBuffer, &errorMessage, NULL);
    if(FAILED(result))
        {
        // 如果vs編譯失敗,輸出錯誤消息.
        if(errorMessage)
            {
            OutputShaderErrorMessage(errorMessage, hwnd, vsFilename);
            }
        // 如果沒有任何錯誤消息,可能是shader文件丟失.
        else
            {
            MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK);
            }

        return false;
        }

    // 編譯ps.
    result = D3DX11CompileFromFile(psFilename, NULL, NULL, "ColorPixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL,
        &pixelShaderBuffer, &errorMessage, NULL);
    if(FAILED(result))
        {
        // 如果ps編譯失敗,輸出錯誤信息.
        if(errorMessage)
            {
            OutputShaderErrorMessage(errorMessage, hwnd, psFilename);
            }
        // 如果沒有任何錯誤消息,可能是shader文件丟失.
        else
            {
            MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK);
            }

        return false;
        }


   // 從緩沖創建vs shader.
    result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &m_vertexShader);
    if(FAILED(result))
        {
        return false;
        }

    // 從緩沖創建ps shader.
    result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &m_pixelShader);
    if(FAILED(result))
        {
        return false;
        }

    // 設置數據布局layout,以便在shader中使用.
    // 定義要和ModelClass中的頂點結構一致.
    polygonLayout[0].SemanticName = "POSITION"; //vs中的輸入參數
    polygonLayout[0].SemanticIndex = 0;
    polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
    polygonLayout[0].InputSlot = 0;
    polygonLayout[0].AlignedByteOffset = 0;
    polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    polygonLayout[0].InstanceDataStepRate = 0;

    polygonLayout[1].SemanticName = "COLOR";
    polygonLayout[1].SemanticIndex = 0;
    polygonLayout[1].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    polygonLayout[1].InputSlot = 0;
    polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
    polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    polygonLayout[1].InstanceDataStepRate = 0;

   // 得到layout中的元素數量
    numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

    // 創建頂點輸入布局.
    result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(),
        vertexShaderBuffer->GetBufferSize(), &m_layout);
    if(FAILED(result))
        {
        return false;
        }

    //釋放頂點和像素緩沖.
    vertexShaderBuffer->Release();
    vertexShaderBuffer = 0;

    pixelShaderBuffer->Release();
    pixelShaderBuffer = 0;

    // 設置動態矩陣描述.
    matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
    matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    matrixBufferDesc.MiscFlags = 0;
    matrixBufferDesc.StructureByteStride = 0;

    // 創建const buffer指針,以便訪問shader常量.
    result = device->CreateBuffer(&matrixBufferDesc, NULL, &m_matrixBuffer);
    if(FAILED(result))
        {
        return false;
        }

    return true;
    }

 

bool ColorShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix,
    D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
    {
    HRESULT result;
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    MatrixBufferType* dataPtr;
    unsigned int bufferNumber;

    // 傳入shader前,確保矩陣轉置,這是D3D11的要求.
    D3DXMatrixTranspose(&worldMatrix, &worldMatrix);
    D3DXMatrixTranspose(&viewMatrix, &viewMatrix);
    D3DXMatrixTranspose(&projectionMatrix, &projectionMatrix);

    //  鎖定常量緩沖,以便能夠寫入.
    result = deviceContext->Map(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    if(FAILED(result))
        {
        return false;
        }

    // 得到const buffer指針.
    dataPtr = (MatrixBufferType*)mappedResource.pData;

    // 設置world,view以及projection矩陣.
    dataPtr->world = worldMatrix;
    dataPtr->view = viewMatrix;
    dataPtr->projection = projectionMatrix;

    // 解鎖常量緩沖.
    deviceContext->Unmap(m_matrixBuffer, 0);

    // 設置常量緩沖位置.
    bufferNumber = 0;

    // 用更新後的值設置常量緩沖.
    deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer);

    return true;
    }

DrawIndexed函數會根據指定的索引緩沖、體元類型、相關shader,向GPU傳送一個draw primitive的命令,從而真正開始體元的渲染操作。

void ColorShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount)
    {
    // 綁定頂點布局.
    deviceContext->IASetInputLayout(m_layout);

    // 設置渲染使用vs和ps.
    deviceContext->VSSetShader(m_vertexShader, NULL, 0);
    deviceContext->PSSetShader(m_pixelShader, NULL, 0);

   // 渲染三角形
    deviceContext->DrawIndexed(indexCount, 0, 0);

    return;
    }

程序運行後如下圖所示:

技術分享圖片

 

完整的代碼請參考:

工程文件myTutorialD3D11_4

代碼下載:

http://files.cnblogs.com/mikewolf2002/myTutorialD3D11.zip

Directx11教程(5) 畫一個簡單的三角形(1)