第三章的主要目標是完成渲染器的實作,因為進入到了主要引擎核心部分的實作,我們首先需要對引擎進行框架搭建,之后的所有內容將是在引擎框架上進行擴展,下圖為引擎所包含的模塊,由下向上代表了整個引擎的層次結構,上層模塊依賴下次模塊的實作,而下層模塊則不依賴上層模塊,

Dependency模塊是外部依賴項,如渲染包含的圖形API介面、物理引擎、模型庫、聲音等外部依賴項,Dependency中的內容是外部匯入的,因此我們的引擎對這個部分產生依賴關系,
Platform模塊是各平臺相關實作,如視窗的顯示、游戲主回圈的啟動、輸入輸出、渲染驅動的初始化等,都依賴各平臺的不同實作,
Foundation模塊是引擎中用到的基礎功能,如第二章介紹的數學庫、記憶體管理、IO、執行緒、Debugger等內容,上層引擎實作會依賴這些基礎內容庫,
ResourceMgr模塊是對引擎中的貼圖、材質、模型、音視頻、影片、組態檔等資源的管理,上層模塊通過這個ResourceMgr可以方便的對各種引擎資源進行訪問,
Graphic模塊是圖形相關功能的實作,最下面的Renderer實作了不同各類的渲染器,如軟體渲染器、Vulkan渲染器、GL渲染器等都是在這個里實作的,Rendering部分提供了渲染相關的外部介面,Effect、PostProcess、Illumination等則是提供如特效、光照等渲染效果,
AI、Net、HID(Human Interface Device)、Animation System、Physic、Audio從名字可以看出其對應了游戲中會用的到各相關模塊,
Application層是應用程式層,這層封裝了引擎的啟動、初始化、主回圈等邏輯, 用戶使用引擎時,通過實作模塊提供的CApplication類,可以在不關心底層邏輯的基礎上,快速啟動一個視窗并在上面繪制圖形,
當需要渲染一個三角形時,具體代碼類的定義如下面的結構:
1 class RendererApplication : public CApplication 2 { 3 DECLARE_INITIALIZE; 4 5 public: 6 virtual bool OnInitialize() override; 7 virtual bool OnTerminate() override; 8 virtual void OnUpdate() override; 9 10 private: 11 CRenderInput _RenderInput; 12 CGeometry *_Geometry; 13 }; 14 15 REGISTER_INITIALIZE(RendererApplication);
1 void VertexShader(const void *globalUniforms, const void *uniforms, const void *datas, unsigned char*out) 2 { 3 UniformMap globalU= *((UniformMap*)globalUniforms); 4 UniformMap u = *((UniformMap*)uniforms); 5 Matrix4x4f mvpMat4(globalU["mvpMat"].data()); 6 7 Vector3f inPosition; 8 Vector3f inColor; 9 memcpy(inPosition.v, datas, sizeof(inPosition.v)); 10 memcpy(inColor.v, (unsigned char *)datas + sizeof(Vector3f), sizeof(inColor.v)); 11 12 Vector4f outPosition = mvpMat4 * Vector4f(inPosition.x, inPosition.y, inPosition.z, 1.0f); 13 14 memcpy(out, &outPosition, sizeof(outPosition)); 15 memcpy(out + sizeof(outPosition), &inColor, sizeof(inColor)); 16 } 17 18 Color FragmentShader(const void *uniforms, ISampler **, const void *datas) 19 { 20 Vector3f inColor; 21 memcpy(inColor.v, datas, sizeof(inColor.v)); 22 23 return Color(1.0f, inColor.x, inColor.y, inColor.z); 24 } 25 26 IMPLEMENT_APP_INITIALIZE(RendererApplication); 27 28 bool RendererApplication::OnInitialize() 29 { 30 cout << "RendererApplication::OnInitialize" << endl; 31 IVertexBuffer *vertexBuffer = Renderer->CreateVertexBuffer(); 32 IIndexBuffer *indexBuffer = Renderer->CreateIndexBuffer(); 33 34 float triangle[] = { -10.f, -10.f, -100.f, 1.f, 0.f, 0.f, 35 10.f, -10.f, -100.f, 0.0f, 1.0f, 0.0f, 36 0.f, 10.f, -100.f, 0.0f, 0.0f, 1.0f, 37 }; 38 vertexBuffer->BufferData(triangle, sizeof(triangle), sizeof(triangle) / (6 * sizeof(float))); 39 vertexBuffer->GetAttribute()->SetPositionAttr(0, sizeof(float) * 6); 40 vertexBuffer->GetAttribute()->SetColorAttr(sizeof(float) * 3, sizeof(float) * 6); 41 42 _Geometry = NEW CGeometry(vertexBuffer, nullptr); 43 _RenderInput.SetGeometry(_Geometry); 44 IShaderProgram *shaderProgram = nullptr; 45 if (Renderer->GetRendererType() == RendererType::Software) 46 { 47 shaderProgram = NEW CSoftShaderProgram(VertexShader, FragmentShader); 48 } 49 _RenderInput.SetShaderProgram(shaderProgram); 50 _RenderInput.SetTexture(0, nullptr); 51 Renderer->SetClearColor(0, 0, 0); 52 Matrix4x4f vMat, proMat; 53 vMat.BuildCameraLookAtMatrix(Vector3f(0, 0, 0), Vector3f(0, 0, -1), Vector3f(0, 1, 0)); 54 proMat.BuildProjectionMatrixOrthoRH(PI / 6.f, 1.0f * GetWindowHeight() / GetWindowWidth(), 1.0f, 1000.f); 55 Renderer->SetGlobalUniform("mvpMat", (proMat * vMat).m, sizeof(Matrix4x4f)); 56 return true; 57 } 58 59 bool RendererApplication::OnTerminate() 60 { 61 cout << "RendererApplication::OnTerminate" << endl; 62 SAFE_DELETE(_Geometry); 63 return true; 64 } 65 66 void RendererApplication::OnUpdate() 67 { 68 69 Renderer->AddRenderInput(&_RenderInput); 70 }
我們在OnInitialize()、OnTerminate()、OnUpdate()分別進行游戲啟動時的初始化、關閉游戲時的清理和每幀的更新操作,這里初始化OnInitialize()時創建了一個三角形并構建渲染需要的RenderInput資料,在OnUpdate時將RenderInput資料傳遞給渲染器,OnTerminate()中銷毀幾何資料,VertexShader和FragmentShader是軟體渲染器使用的著色器程式,分別對頂點和要繪制的像素進行操作,上面代碼會繪制出我們熟悉的三角形,這個簡單的實作沒有涉及到場景管理,因此通過手動生成RenderInput的方式將資料傳遞給需要器,因為這章我們的重點是渲染器的實作,

這章我們主要目標是實作上面引擎架構Graphics中 Renderer部分,首先我們要實作的是一個軟體渲染器,之所以叫軟體渲染器,是因為沒有利用GPU的硬體加速,所有操作都基于CPU處理,這對于理解如OpenGL、DirectX、Vulkan等圖形API的原理十分重要,當我們通過CPU完成了整個渲染流程,再去看這些晦澀的圖形API時,一切都變的柳暗花明,之后我們實作一個基于Vulkan的硬體渲染器,通過GPU加速,實時渲染速度會得到非常大的提升,在這之前,首先要了解現代實時渲染程序是如何將物體繪制到螢屏上的,
在《從零開始游戲開發——2.5 第二個三角形》節中,我們利用射線實作了在螢屏上繪制三角形,但在實時渲染中這種方式太慢了,現在主流方式主要包括以下幾個步驟:

應用程式處理段階主要是剔除不可見三角形然后將資料提交給渲染器,應用程式通常會通過攝像機的視錐體(可見范圍)、BSP、PVS等技術裁剪掉不可見的模型,這個階段也會將三角形進行合批操作,將相同材質的模型合并成一個整體一次性提交給渲染器,以減少每次drawcall渲染器需要的狀態切換,幾何處理階段是對三角形和頂點的處理,這個階段會將應用程式階段提交的三角形進行空間變換,由模型空間——世界空間——攝像機空間—— 裁剪空間——螢屏空間,這個程序會在后面詳細介紹,需要知道的是這個階段的目的是將處理輸入的世界空間中的幾何圖形輸出為螢屏空間中的幾何圖形, 為螢屏上繪制提供資料,幾何處理階段輸出的是由點構成的螢屏空間幾何圖形,我們最終要在螢屏上顯示的是實心的三角形面而不是點或線框,光柵化階段對三角形頂點資料進行插值,計算螢屏上三角形內部每個像素繪制所需要的資料,像素處理階段通過深度測驗、模板測驗、Alpha測驗、顏色混合等操作對最終螢屏上的像素進行著色操作,軟體渲染器中,上述階段都是在CPU上執行的,硬體渲染器中應用程式階段主要是在CPU中執行的,也有通過撰寫Compute Shder利用GPU加速資料處理,剩下的幾個階段都是在GPU上運行,幾何階段提供了必需要的Vertex Shader和可選的Tessellation Shader和Geometry Shader由我們控制幾何資料的生成,像素處理階段提供了Fragment Shader(或叫Pixel Shader)來處理最終著色效果和可配的模板操作來達到一些特殊效果,
在實作渲染器之前,有幾個概念是比較重要的,第一個要明確的是三角形面的正面是通過順時針方向還是逆時針方向定義的,在渲染程序中,我們告訴渲染器需要渲染三角形的哪個面,因此這需要告訴渲染器第二件事情剔除面的模式是正面剔除、背景剔除還是都不剔除,一次渲染程序我們通常需要顏色Buffer、深度Buffer、模板Buffer,這些都定義在叫做幀快取的物件上,我們的引擎定義名字為RenderTarget,第三個要告訴渲染器的是否需要寫深度、寫模板、需要進行深度測驗、模板測驗,最后我們要設定每次渲染開始前是否要清除顏色Buffer、深度Buffer、模板Buffer,同時可以設定Clear的數值,這些常用狀態沒有設定的時候,我們提供默認的狀態進行渲染,
本文來自博客園,作者:毅安,轉載請注明原文鏈接:https://www.cnblogs.com/primarycode/p/16536536.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/501837.html
標籤:其他
上一篇:類暗黑破壞神屬性系統思路
