1. 程式人生 > >Directx11基礎教程三之VertexShader,PixelShader,buffer

Directx11基礎教程三之VertexShader,PixelShader,buffer

一,看本節教程前應該掌握:      (1)D3D11基礎教程二之D3D11初始化      (2)瞭解3D渲染流水線的知識,如世界變換,相機變換,透視投影變換,視口變換,光柵化,線性插值,gouraud著色等,最好具備一定的圖形學基礎,我推薦一本書<3D遊戲程式設計大師技巧>,這本書完整的講解了3D渲染流水線的方方面面。

二,本節教程的程式結構:

三,VertexShader(頂點著色器)和PixelShader(畫素著色器)的作用: 在講解VertexShader(頂點著色器)和PixelShader(畫素著色器)的作用之前,我先放出一張D3D11簡略版的3D渲染流水線圖:

(1)VertexShader(頂點著色器):頂點著色器顧名思義,也就是對頂點進行操作,對頂點進行變換的著色器,看上面圖紅色圈住的部分就是頂點變換的過程了,而我們的VertexShader(頂點著色器),一般是對頂點進行上面紅色圈的 世界變換(WorldTransform),   相機變換(也稱視角變換,ViewTransform),    透視投影變換(PerspectiveProjection),(得注意的是"視口變換"不屬於VertexShader,而是與"背面剔除'和"頂點屬性線性插值"共屬於光柵化階段")剩下的幾種變換經常是顯示卡幫我們自動進行的。

(2) PixelShader(畫素著色器):畫素著色器顧名思義,也就是對畫素進行的操作的著色器,也就是上面圖裡的藍色圈住的部分。畫素著色器主要是對畫素(畫素的位置,顏色,向量等)操作,最終輸出畫素的顏色。

(3)VertexShader(頂點著色器)和PixelShader(畫素著色器)之間的關係:這裡用一個三角形作為例子,一個三角形由三個頂點ABC組成,三角形ABC位於區域性空間,三個頂點ABC分別在VertexShader進行世界變換,相機變換等等各種變換,最終變換到螢幕空間,然後對螢幕空間的三角形ABC進行光柵化,得到三角形裡面的畫素,然後用PixelShader對畫素進行操作,最終輸出顏色到螢幕上。如下面圖所示:

看圖中的三角形ABC以及三角形ABC裡面的畫素,圖中我用紅色點表示畫素,一個紅色點就是一個畫素。

(其實上面也不對,這裡為了初學者教學的簡便,我們就不引入其它種類的Shader了,以及深度快取一大堆亂七八糟的)

三,程式的程式碼:

首先能GraphicsClass包含四個類,分別為D3DClass,ModelClass,CameraClass,ColorShaderClass

(1) GrapgicsClass

GraphicsClass.h

#pragma once #ifndef _GRAPHICS_CLASS_H #define _GRAPHICS_CLASS_H   #include"D3DClass.h" #include"CameraClass.h" #include"ColorShaderClass.h" #include"ModelClass.h"     //全域性變數 const bool FULL_SCREEN = false; const bool VSYNC_ENABLE = true;  //是儘可能快渲染還是限制幀渲染 const float SCREEN_FAR = 1000.0f;  //視截體遠裁面 const float SCREEN_NEAR = 0.1f;  //視截體近裁面       class GraphicsClass {   private:     //D3D類     D3DClass* mD3D;       //相機類,用於控制場景的相機     CameraClass* mCamera;        //用於控制VertexShader PixelShader,InputLayout     ColorShaderClass* mColorShader;       //用於控制VertexBuffer和IndexBuffer     ModelClass* mModel;   private:     bool Render();   public:     GraphicsClass();     GraphicsClass(const GraphicsClass&);     ~GraphicsClass();   public:     bool Initialize(int ScreenWidth, int ScreenHeight, HWND hwnd);     void Shutdown();     bool Frame();  }; #endif // !_GRAPHICS_CLASS_H

(2) ColorShaderClass

ColorShaderClass.h  (用於載入VertexShader和PixelShader)

#pragma once #ifndef _COLOR_SHADER_CLASS_H #define _COLOR_SHADER_CLASS_H #define HR2(X) {if(FAILED(X)) { MessageBox(0,L"Create Failed",0,0); return false;}}   #include<d3d11.h> #include<xnamath.h> #include<D3DX11.h> //含編譯Shader程式的函式 #include<d3dcompiler.h> #include<fstream> using namespace std;   class ColorShaderClass {   private:     //常量快取結構體     struct CBMatrix     {         XMMATRIX mWorldMatrix;         XMMATRIX mViewMatrix;         XMMATRIX mProjMatrix;     };   private:     ID3D11VertexShader* md3dVertexShader;     ID3D11PixelShader* md3dPixelShader;     ID3D11InputLayout* md3dInputLayout; //這與VertexShader相關聯,因此要放在ColorShaderClass裡,而不是D3DClass     ID3D11Buffer* mCBMatrixBuffer; //(常量)快取,頂點索引也是用這個型別   private:     bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*); //用於建立InputLayout,VertexShader,PixelShader,常量快取     bool ShutdownShader();     void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);       bool SetShaderParameter(ID3D11DeviceContext*, CXMMATRIX, CXMMATRIX, CXMMATRIX);     void RenderShader(ID3D11DeviceContext*, int);   public:     ColorShaderClass();     ColorShaderClass(const ColorShaderClass&);     ~ColorShaderClass();   public:     bool Initialize(ID3D11Device*, HWND);     void Shutdown();     bool Render(ID3D11DeviceContext*, int, CXMMATRIX, CXMMATRIX, CXMMATRIX);   }; #endif 

ColorShaderClass.CPP #include"ColorShaderClass.h"   ColorShaderClass::ColorShaderClass() {      md3dVertexShader=NULL;      md3dPixelShader=NULL;      md3dInputLayout=NULL;      mCBMatrixBuffer=NULL; }     ColorShaderClass::ColorShaderClass(const ColorShaderClass&) {   }   ColorShaderClass::~ColorShaderClass() {   }     bool ColorShaderClass::Initialize(ID3D11Device* d3dDevice, HWND hwnd) {     bool result;     result = InitializeShader(d3dDevice, hwnd, L"MyShader.fx", L"MyShader.fx");     if (!result)         return false;       return true; }   void ColorShaderClass::Shutdown() {     ShutdownShader(); }   bool ColorShaderClass::Render(ID3D11DeviceContext* d3dDeviceContext, int indexCount, CXMMATRIX worldMatrix, CXMMATRIX viewMatrix, CXMMATRIX ProjMatrix) {     bool result;       //設定用來渲染的Shader屬性     result = SetShaderParameter(d3dDeviceContext, worldMatrix, viewMatrix, ProjMatrix);     if (!result)         return false;       //渲染Shader     RenderShader(d3dDeviceContext, indexCount);          return true; }   bool ColorShaderClass::InitializeShader(ID3D11Device* d3dDevice, HWND hwnd, WCHAR* VSFileName, WCHAR* PSFileName) {     HRESULT result;     ID3D10Blob* errorMessage;     ID3D10Blob* VertexShaderBuffer;       ID3D10Blob* PixelShaderBuffer;          //第一,初始化引數     errorMessage = NULL;     VertexShaderBuffer=NULL;     PixelShaderBuffer=NULL;       //第二,編譯VertexShader程式碼,並建立VertexShader     result = D3DX11CompileFromFile(VSFileName, NULL, NULL, "VS", "vs_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, NULL, &VertexShaderBuffer, &errorMessage, NULL);     if (FAILED(result))     {         //存在錯誤資訊         if (errorMessage)         {             OutputShaderErrorMessage(errorMessage, hwnd, VSFileName);         }         //不存在錯誤資訊,也就是沒有找到Shader檔案         else         {             MessageBox(hwnd, L"can not find VS file", L"error", MB_OK);         }     }       HR2(d3dDevice->CreateVertexShader(VertexShaderBuffer->GetBufferPointer(),VertexShaderBuffer->GetBufferSize(),NULL,&md3dVertexShader));         //第三,編譯PixelShader,並建立PixelShader     result = D3DX11CompileFromFile(PSFileName, NULL, NULL, "PS", "ps_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, NULL, &PixelShaderBuffer, &errorMessage, NULL);     if (FAILED(result))     {         //存在錯誤資訊         if (errorMessage)         {             OutputShaderErrorMessage(errorMessage, hwnd, PSFileName);         }         //不存在錯誤資訊,也就是沒有找到Shader檔案         else         {             MessageBox(hwnd, L"can not find PS file", L"error", MB_OK);         }     }       HR2(d3dDevice->CreatePixelShader(PixelShaderBuffer->GetBufferPointer(), PixelShaderBuffer->GetBufferSize(), NULL, &md3dPixelShader));       //第四,填充輸入佈局形容結構體,建立輸入佈局     D3D11_INPUT_ELEMENT_DESC VertexInputLayout[] =     {         { "POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0 }, // 96位即12個位元組         { "COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0 },     };       unsigned int numElements = sizeof(VertexInputLayout) / sizeof(VertexInputLayout[0]);         //佈局數量          HR2(d3dDevice->CreateInputLayout(VertexInputLayout, numElements, VertexShaderBuffer->GetBufferPointer(), VertexShaderBuffer->GetBufferSize(), &md3dInputLayout));       //第五,釋放VertexShaderBuffer和PixelShaderBuffer     VertexShaderBuffer->Release();     VertexShaderBuffer = NULL;     PixelShaderBuffer->Release();     PixelShaderBuffer = NULL;       //第六,設定(常量)快取形容結構體,並建立常量快取     D3D11_BUFFER_DESC matrixBufferDesc;     ZeroMemory(&matrixBufferDesc, sizeof(matrixBufferDesc));     matrixBufferDesc.Usage = D3D11_USAGE_DEFAULT;     matrixBufferDesc.ByteWidth = sizeof(CBMatrix);   //結構體大小,必須為16位元組倍數     matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;     matrixBufferDesc.CPUAccessFlags = 0;        HR2(d3dDevice->CreateBuffer(&matrixBufferDesc, NULL, &mCBMatrixBuffer));       return true; }   bool ColorShaderClass::ShutdownShader() {     HR2(mCBMatrixBuffer);     HR2(md3dInputLayout);     HR2(md3dPixelShader);     HR2(md3dVertexShader);     return true; }   void ColorShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename) {     char* compileErrors;     unsigned long bufferSize, i;     ofstream fout;         // 獲取指向錯誤資訊文字的指標     compileErrors = (char*)(errorMessage->GetBufferPointer());       // 獲取錯誤資訊文字的長度     bufferSize = errorMessage->GetBufferSize();       // 建立一個txt,用於寫入錯誤資訊     fout.open("shader-error.txt");       //想txt檔案寫入錯誤資訊     for (i = 0; i<bufferSize; i++)     {         fout << compileErrors[i];     }       // 關閉檔案     fout.close();       // Release the error message.     errorMessage->Release();     errorMessage = 0;       //彈出提醒的小視窗     MessageBox(hwnd, L"Error compiling shader.  Check shader-error.txt for message.", shaderFilename, MB_OK);   }     bool ColorShaderClass::SetShaderParameter(ID3D11DeviceContext* d3dDeviceContext, CXMMATRIX worldMatrix, CXMMATRIX viewMatrix, CXMMATRIX ProjMatrix) {     //D3D11_MAPPED_SUBRESOURCE mappedResource;            //CBMatrix* cbPtr;     unsigned int bufferNum;       //將矩陣轉置,在傳入常量快取前進行轉置,因為GPU對矩陣資料會自動進行一次轉置     CBMatrix cb;     XMMATRIX worldMa = XMMatrixTranspose(worldMatrix);     XMMATRIX viewMa = XMMatrixTranspose(viewMatrix);     XMMATRIX ProjMa = XMMatrixTranspose(ProjMatrix);     cb.mWorldMatrix = worldMa;     cb.mViewMatrix = viewMa;     cb.mProjMatrix = ProjMa;     d3dDeviceContext->UpdateSubresource(mCBMatrixBuffer, 0, NULL, &cb, 0, 0);     /**/     //鎖定常量快取,這時候常量快取和子資源關聯在一起     //HR2(d3dDeviceContext->Map(mCBMatrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource));       //獲取指向常量快取資料的指標     //cbPtr = (CBMatrix*)mappedResource.pData;       //賦予常量快取資料     //cbPtr->mProjMatrix = worldMa;     //cbPtr->mViewMatrix = viewMa;     //cbPtr->mProjMatrix = ProjMa;       //解鎖常量快取     //d3dDeviceContext->Unmap(mCBMatrixBuffer, 0);          //設定在頂點快取中常量快取的位置,註冊號     bufferNum = 0;       //設定在VertexShader的常量快取的值(帶著更新的值)     d3dDeviceContext->VSSetConstantBuffers(bufferNum, 1, &mCBMatrixBuffer);        return true; }   void ColorShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount) {     //設定頂點輸入佈局     deviceContext->IASetInputLayout(md3dInputLayout);       //設定VertexShader和PixelShader     deviceContext->VSSetShader(md3dVertexShader, NULL, 0);     deviceContext->PSSetShader(md3dPixelShader, NULL, 0);       //渲染三角形     deviceContext->DrawIndexed(indexCount, 0, 0); }

(3) ModelClass

ModelClass.h(用於載入頂點快取和索引快取)

#pragma once #ifndef _MODEL_CLASS_H #define _MODEL_CLASS_H   #include<d3d11.h> #include<xnamath.h> #define HR1(X) {if(FAILED(X)) { MessageBox(0,L"Create Failed",0,0); return false;}} #define ReleaseCOM1(x) { if (x) { x->Release(); x = 0; } }   class ModelClass { private:     struct Vertex     {         XMFLOAT3 pos;         XMFLOAT4 color;     }; private:     ID3D11Buffer* md3dVertexBuffer; //頂點快取     ID3D11Buffer* md3dIndexBuffer;  //索引快取     int mVertexCount;     int mIndexCount;   private:     bool InitializeBuffer(ID3D11Device* d3dDevice);     void ShutdownBuffer();     void RenderBuffers(ID3D11DeviceContext* d3dDeviceContext);   public:     ModelClass();     ModelClass(const ModelClass&);     ~ModelClass();   public:     //Initialize是建立元素,Render是設定元素,Shutdown是Release     bool Initialize(ID3D11Device* d3dDevice);     void Shutdown();     void Render(ID3D11DeviceContext* d3dDeviceContext);       int GetIndexCount() { return mIndexCount; }   }; #endif 

ModelClass.CPP #include"ModelClass.h"   ModelClass::ModelClass() {     md3dVertexBuffer=NULL; //頂點快取     md3dIndexBuffer=NULL;  //索引快取     mVertexCount = 0;     mIndexCount = 0;   }     ModelClass::ModelClass(const ModelClass& other) {   }   ModelClass::~ModelClass() {   }   bool ModelClass::Initialize(ID3D11Device* d3dDevice) {     bool result;     result = InitializeBuffer(d3dDevice);       if (!result)         return false;           return true; }   void ModelClass::Shutdown() {     ShutdownBuffer(); }     void ModelClass::Render(ID3D11DeviceContext* d3dDeviceContext) {     //設定渲染管線的頂點快取和索引快取(IA階段)     RenderBuffers(d3dDeviceContext); }   bool ModelClass::InitializeBuffer(ID3D11Device* d3dDevice) {     Vertex* vertexs=NULL;     WORD*indices=NULL;  //一個字為兩個位元組        mVertexCount = 4;     mIndexCount = 6;       //建立頂點陣列     vertexs = new Vertex[mVertexCount];     if (!vertexs)         return false;       //建立索引陣列     indices = new WORD[mIndexCount];     if (!indices)         return false;          //賦予頂點陣列資料和索引陣列資料     vertexs[0].pos = XMFLOAT3(-1.0f, -1.0f, 0.0f);     vertexs[0].color = XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f);     vertexs[1].pos = XMFLOAT3(1.0f, 1.0f, 0.0f);     vertexs[1].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);     vertexs[2].pos = XMFLOAT3(1.0f, -1.0f, 0.0f);     vertexs[2].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);     vertexs[3].pos = XMFLOAT3(-1.0f, 1.0f, 0.0f);     vertexs[3].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);     //賦予索引陣列資料       //注意用左手定則判定是不是背面     indices[0] = 0;     indices[1] = 3;     indices[2] = 2;     indices[3] = 1;     indices[4] = 2;     indices[5] = 3;         //第一,填充(頂點)快取形容結構體和子資源資料結構體,並建立頂點快取     D3D11_BUFFER_DESC vertexBufferDesc;     vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;     vertexBufferDesc.ByteWidth = sizeof(Vertex) * mVertexCount;     vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;     vertexBufferDesc.CPUAccessFlags = 0;     vertexBufferDesc.MiscFlags = 0;     vertexBufferDesc.StructureByteStride = 0;       D3D11_SUBRESOURCE_DATA vertexData;     vertexData.pSysMem = vertexs;     vertexData.SysMemPitch = 0;     vertexData.SysMemSlicePitch = 0;     HR1(d3dDevice->CreateBuffer(&vertexBufferDesc, &vertexData, &md3dVertexBuffer));       //第二,填充(索引)快取形容結構體和子資源資料結構體,並建立索引快取     D3D11_BUFFER_DESC  indexBufferDesc;     indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;     indexBufferDesc.ByteWidth = sizeof(WORD) * mIndexCount;     indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;     indexBufferDesc.CPUAccessFlags = 0;     indexBufferDesc.MiscFlags = 0;     indexBufferDesc.StructureByteStride = 0;       D3D11_SUBRESOURCE_DATA indexData;     indexData.pSysMem = indices;     indexData.SysMemPitch = 0;     indexData.SysMemSlicePitch = 0;     HR1(d3dDevice->CreateBuffer(&indexBufferDesc, &indexData, &md3dIndexBuffer));       //釋放頂點陣列和索引陣列(這時資料已經載入快取,不需要這些陣列了)     delete[]vertexs;     vertexs = NULL;     delete[]indices;     indices = NULL;          return true; }   void ModelClass::ShutdownBuffer() {     //釋放頂點快取和索引快取     ReleaseCOM1(md3dIndexBuffer);     ReleaseCOM1(md3dVertexBuffer); }   void ModelClass::RenderBuffers(ID3D11DeviceContext* d3dDeviceContext) {     //設定頂點快取     UINT stride = sizeof(Vertex); //每個頂點元素的跨度大小,或者說每個頂點元素的大小     UINT offset = 0;     d3dDeviceContext->IASetVertexBuffers(0, 1, &md3dVertexBuffer, &stride, &offset);       //設定索引快取     d3dDeviceContext->IASetIndexBuffer(md3dIndexBuffer, DXGI_FORMAT_R16_UINT, 0); //Word為兩個位元組       //設定拓撲方式     d3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); }

四,本節教程的程式得注意的地方: (1)在一個.h檔案裡包含XNAMATH.h一定得先包含一個windows.h。

(2)XMMATRIX作為函式引數時應該用CXMMATRIX。

(3)指定頂點的索引值時得注意繞線方向,用左手定則,得到三角形法線方向跟相機LookAt方向為鈍角的被正面,反之為背面.

(4)我的VertexShader和PixelShader都在從MyShader.fx檔案中編譯的,其實.HLSL檔案.VS檔案.PS檔案.FX檔案都能編譯成Shader

(5)在.FX檔案中常量快取CBMatrix注意用register()註冊,註冊為b0,則在ColorShaderClass::SetShaderParameter()的d3dDeviceContext->VSSetConstantBuffers(bufferNum, 1, &mCBMatrixBuffer)的bufferNum為0,若註冊為b1,則bufferNum為1,以此類推.

(6)在VertexShader階段和PixelShade階段,常量快取的值一直未曾改變,不會進行光柵化,因此世界空間的平行光,聚光燈,點光源都是採用常量快取。

(7)在對應與常量快取的結構體一定是16個位元組的倍數,XMMATRIX為64個位元組,float為4個位元組,實在要是怕忘記16位元組對齊規則,可在結構體前加個字首,__declspec(align(16))  ,比如

    __declspec(align(16))       struct CbNeverChange   //常量快取結構嚴格16位元組的倍數     {         DirectionLight DireLight;         PointLight PoLight;         SpotLight SpLight;         Material SkullMaterial;         XMFLOAT3 EyePostion;     };

五,本節教程的程式執行的結果:

六,程式原始碼連結如下 點選開啟連結 ---------------------  作者:小毛狗  來源:CSDN  原文:https://blog.csdn.net/qq_29523119/article/details/52716762  版權宣告:本文為博主原創文章,轉載請附上博文連結!