iT邦幫忙

2021 iThome 鐵人賽

DAY 19
1
自我挑戰組

翻車機率極高的2D平台遊戲(2D Platformer)製作系列 第 19

[Day 19] 2D 批次渲染 (一)

今日目標

  • 2D批次渲染

DrawLine

照理來說,今天要實作DrawLine但是為什麼沒有呢?

這是我預計畫線功能接口的樣子

void DrawLine(V2f p0, V2f p1, int thinkness, Color c)

只要給兩點,就會畫出來,然後arg2是線的寬度,所以事實上,他畫出來的不是單純的「線」,而是一條「矩形」。

那應該可以直接用DrawRectangle了吧,但會有一個麻煩的地方,雖說DrawRectangle可以用旋轉,達成從A點畫線到B點的功能,但是這就要利用給的矩形的原點,推出四個點後,然後再看是哪一點到哪一點的斜率,再從斜率推出旋轉的角度,於是我想就索性改寫,可以直接帶已經算好四個座標進去獨立畫出來的功能。

進一步想,那原本的DrawRectangle是不是也可變成代入四個點畫出的功能,而且好像比畫線簡單,又再進一步想...是不是可以把這些點集中起來來一次畫出來,因為事實上都是矩形...

等一下!這不就是批次渲染嗎!

為什麼要批次渲染

現今的商業遊戲引擎,一定都會有批次渲染(或是有Instancing),如果熟悉一點GPU管線的話,CPU算好的頂點資料到GPU稱作應用期,開發者在處理好繪圖相關的邏輯後,也就是在CPU的地方,會傳送到GPU,然後接下來才是GPU的工作。

所以問題的消耗點,就是在這個CPU與GPU溝通的過程。例如之前的DrawRectangle就是四個頂點算好後,直接給GPU,叫他畫。那當我要畫10000個、1000000個矩形的時候,他就會呼叫N次,這會造成消耗,最有可能的結果是,GPU都已經畫好了,但卡在CPU還在計算畫多少個圖形。

上面說到的,「10000個、1000000個矩形的時候,他就會呼叫N次」,這個就是Draw Call,然後在OpenGL裡面呼叫
glDrawElements或是glDrawTriangles,就會是一次Draw Call。

批次渲染規劃

今天只先說一下我的規劃,目前正寫到一半,發生一些問題,還沒寫完也還沒測試。

既然要一次畫完,首先會在新的iron_render_window底下的CONTEXT.RenderData新增一個VertexBuffer的結構來裝我們要的「2D頂點」資料。

一個頂點通常會有這些資料,這些會影響到原本的Shader。

struct Vertex{
    V2f position;
    V2f texture_coordinate;
    V2f color;
}

然後可以看一下color屬性,原本在Shader是用uniform,但這次要把它以VertexAttribute(glVertexAttribPointer)得方式傳進去,因為如果使用uniform的話,他會變更使用中的Shader的狀態,那就要對這個Shader的狀態獨立畫出來,那每改一次Shader狀態的顏色,就要再呼叫Draw一次,於是紀錄頂點資料的結構 - VertexBuffer長這樣

typedef struct VertexBuffer {
    V2f* vertices;
    size_t vertices_count;
    size_t vertices_max;

    V2f* texcoords;
    size_t texcoords_count;
    size_t texcoords_max;

    V4f* colors;
    size_t colors_count;
    size_t colors_max;

    unsigned int* indices;
    size_t indices_count;
    size_t indices_max;

    unsigned int vbo[4]; // positions, texture coordinates, colors, indicess
    unsigned int vao;
} VertexBuffer;

每種資料獨立出來,分別有新的VBO儲存,而不是包成Vertex是因為這樣可以一次性的把同種類的資料進行讀取,不用透過計算間距一部分一部分的讀取。

然後會在EndRendering的時候,把蒐集且算好頂點,一次性丟給GPU繪製,DrawXXX裡的內容就會是把頂點放到這個Vertex Buffer裡面。

但這邊會有一個問題,假如這一幀要畫得頂點已經超過VertexBuffer上限了,換句話說,這一幀的DrawCall有N個(其實通常一定會大於1),目前預想有兩種做法:

  1. 加入新頂點的時候檢查,如果達到上限,就直接畫出
  2. 設置一個新的結構,標示當前Vertex Buffer已滿,需要由新的Draw Call紀錄,一樣在EndRendering匯出

目前會嘗試第二種。

參考

今天批次渲染還沒完成,原本就預計大概會要跨個兩三篇才會完成,所以也沒有新的code上傳。


上一篇
[Day 18] 製作更多的Debug工具 (1) - 連接期錯誤
下一篇
[Day 20] 2D 批次渲染 (二) - BURN OUT
系列文
翻車機率極高的2D平台遊戲(2D Platformer)製作33

尚未有邦友留言

立即登入留言