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 版權宣告:本文為博主原創文章,轉載請附上博文連結!