索引緩沖區(qū)和錯(cuò)誤調(diào)試

我們今天來了解一下索引緩沖區(qū),首先要知道在圖形編程中GPU的繪制是基于三角形的,如果我們想要繪制一個(gè)正方形,對(duì)GPU來說就是繪制兩個(gè)三角形,這意味著GPU在畫一個(gè)正方形的時(shí)候,把相同的頂點(diǎn)位置多次存儲(chǔ)在GPU內(nèi)存中,這無疑是一種浪費(fèi),特別是在考慮繪制3D模型時(shí),有大量的三角形互相連接,而且在實(shí)際渲染過程中,頂點(diǎn)可能不僅僅包含位置信息,還包括法線、紋理、顏色等信息,這將產(chǎn)生大量的額外的頂點(diǎn)信息,這樣一次又一次的復(fù)制這么多信息構(gòu)成的頂點(diǎn)緩沖區(qū),將造成特別嚴(yán)重的浪費(fèi),如果只有一個(gè)簡(jiǎn)單的索引就會(huì)更快、更高效,所以我們引入了索引緩沖區(qū),它允許我們反復(fù)利用現(xiàn)有的頂點(diǎn)。
我們首先創(chuàng)建索引緩沖區(qū)需要的數(shù)據(jù),這些數(shù)據(jù)在CPU上,我們會(huì)將它們發(fā)送到GPU,然后告訴OpenGL使用它們進(jìn)行渲染,這個(gè)過程跟我們創(chuàng)建頂點(diǎn)緩沖區(qū)差不多,區(qū)別在于,我們需要將target綁定為GL_ELEMENT_ARRAY_BUFFER。
glGenBuffers
錯(cuò)誤:
如果n為負(fù)數(shù),則生成GL_INVALID_VALUE。
glBindBuffer
該函數(shù)將緩沖區(qū)綁定到指定的綁定點(diǎn)。target需要設(shè)置為可接受的符號(hào)常量之一,將Buffer設(shè)置為緩沖區(qū)的名稱,如果不存在名為buffer的緩沖區(qū)對(duì)象,則會(huì)創(chuàng)建一個(gè)同名的緩沖區(qū)。當(dāng)一個(gè)緩沖區(qū)綁定到一個(gè)目標(biāo)時(shí),該目標(biāo)以前的綁定會(huì)自動(dòng)中斷。
緩沖區(qū)對(duì)象名是無符號(hào)整數(shù)。零值是保留的,目標(biāo)沒有默認(rèn)的緩沖區(qū)對(duì)象,當(dāng)buffer設(shè)置為零會(huì)解除綁定的所有緩沖區(qū),并恢復(fù)該目標(biāo)的客戶端內(nèi)存使用(如果該目標(biāo)支持的話)。
錯(cuò)誤:
如果target不是允許的值之一,則生成GL_INVALID_ENUM。?
如果buffer不是先前調(diào)用glGenBuffers返回的名稱,則生成GL_INVALID_VALUE
glBufferData
glBufferData和glNamedBufferData都是創(chuàng)建一個(gè)緩沖區(qū)對(duì)象的數(shù)據(jù)存儲(chǔ),對(duì)于glBufferData,是當(dāng)前綁定到target的緩沖區(qū)對(duì)象;對(duì)于glNamedBufferData,是buffer中與ID相關(guān)聯(lián)的緩沖區(qū)對(duì)象。
創(chuàng)建新存儲(chǔ)時(shí),預(yù)先存在的數(shù)據(jù)存儲(chǔ)都將被刪除,然后以指定的字節(jié)大小和用途創(chuàng)建新的數(shù)據(jù)存儲(chǔ)。如果數(shù)據(jù)不為空,則使用來自該指針的數(shù)據(jù)初始化數(shù)據(jù)存儲(chǔ)。在初始狀態(tài)下,新的數(shù)據(jù)存儲(chǔ)沒有被映射,它有一個(gè)空映射指針,它的映射訪問是GL_READ_WRITE。
usage是對(duì)GL實(shí)現(xiàn)的一個(gè)指示,說明該如何訪問緩沖區(qū)對(duì)象的數(shù)據(jù)存儲(chǔ)。這使得GL實(shí)現(xiàn)能夠做出更加智能的決策,從而顯著影響緩沖區(qū)對(duì)象的性能。使用可以分為兩部分:第一,訪問的頻率(修改和使用),第二,訪問的性質(zhì)。
訪問頻率可以是以下之一:
STREAM 數(shù)據(jù)存儲(chǔ)內(nèi)容將被修改一次,使用次數(shù)不多。?
STATIC 數(shù)據(jù)存儲(chǔ)內(nèi)容將被修改一次,使用次數(shù)較多。?
DYNAMIC 數(shù)據(jù)存儲(chǔ)內(nèi)容將被反復(fù)修改,并且多次使用。?
訪問的性質(zhì)可能是以下之一:?
DRAW 數(shù)據(jù)存儲(chǔ)內(nèi)容由應(yīng)用程序修改,并用作GL繪圖和圖像規(guī)范命令的源。
READ 數(shù)據(jù)存儲(chǔ)內(nèi)容通過從GL讀取數(shù)據(jù)來修改,并在應(yīng)用程序查詢時(shí)用于返回該數(shù)據(jù)。
COPY 通過從GL讀取數(shù)據(jù)來修改數(shù)據(jù)存儲(chǔ)內(nèi)容,并將其用作GL繪圖和圖像規(guī)范命令的源。
然后在循環(huán)中調(diào)用glDrawElements,而不是glDrawArrays。

運(yùn)行就可以得到一個(gè)正方形了。

所以,我們創(chuàng)建了一個(gè)索引緩沖區(qū),然后用4行代碼將其發(fā)送給顯卡,最后使用glDrawElements函數(shù)繪制這些數(shù)據(jù),這里要注意,如果glDrawElements參數(shù)中GL_UNSIGNED_INT不小心寫成GL_INT,程序不會(huì)報(bào)錯(cuò),但窗口只會(huì)顯示一片黑,像這樣的無效枚舉的情況很容易發(fā)生,排查起來也十分麻煩,因?yàn)槟憧赡軟]有正確設(shè)置頂點(diǎn)數(shù)據(jù),也許索引緩沖區(qū)設(shè)置不正確,也可能著色器沒運(yùn)行。這里有兩種主要的方法來檢查在OpenGL中的錯(cuò)誤,一個(gè)是glGetError函數(shù),還有一個(gè)是glDebugMessageCallback函數(shù),今天我們只討論glGetError函數(shù)。

glGetError
每個(gè)可檢測(cè)的錯(cuò)誤都被分配了一個(gè)數(shù)字代碼和符號(hào)名稱。當(dāng)錯(cuò)誤發(fā)生時(shí),錯(cuò)誤標(biāo)志被設(shè)置為適當(dāng)?shù)腻e(cuò)誤代碼值。調(diào)用glGetError時(shí),它在返回錯(cuò)誤代碼并將標(biāo)志重置為GL_NO_ERROR之前,不會(huì)記錄其他錯(cuò)誤。如果glGetError返回GL_NO_ERROR,則自上次調(diào)用glGetError以來,或者自GL初始化以來,沒有可檢測(cè)到的錯(cuò)誤。
為了允許分布式實(shí)現(xiàn),可能有幾個(gè)錯(cuò)誤標(biāo)志。如果任何單個(gè)錯(cuò)誤標(biāo)志記錄了一個(gè)錯(cuò)誤,則返回該標(biāo)志的值,并且在調(diào)用glGetError時(shí)將該標(biāo)志重置為GL_NO_ERROR。如果多個(gè)標(biāo)志記錄了錯(cuò)誤,glGetError返回并清除任意錯(cuò)誤標(biāo)志值。因此,如果要重置所有錯(cuò)誤標(biāo)志,則應(yīng)始終在循環(huán)中調(diào)用glGetError,直到它返回GL_NO_ERROR。
最初,所有錯(cuò)誤標(biāo)志都設(shè)置為GL_NO_ERROR。?
目前定義了以下錯(cuò)誤:
GL_NO_ERROR?沒有記錄任何錯(cuò)誤。這個(gè)符號(hào)常量的值為0。?
GL_INVALID_ENUM 為枚舉參數(shù)指定了不可接受的值。違規(guī)命令被忽略,除了設(shè)置錯(cuò)誤標(biāo)志之外,沒有其他副作用。?
GL_INVALID_VALUE?數(shù)值參數(shù)超出范圍。違規(guī)命令被忽略,除了設(shè)置錯(cuò)誤標(biāo)志之外,沒有其他副作用。?
GL_INVALID_?OPERATION當(dāng)前狀態(tài)下不允許指定的操作。違規(guī)命令被忽略,除了設(shè)置錯(cuò)誤標(biāo)志之外,沒有其他副作用。
GL_INVALID_FRAMEBUFFER_OPERATION The?framebuffer對(duì)象不完整。違規(guī)命令被忽略,除了設(shè)置錯(cuò)誤標(biāo)志之外,沒有其他副作用。?
GL_OUT_OF_MEMORY?沒有足夠的內(nèi)存來執(zhí)行該命令。記錄該錯(cuò)誤后,除了錯(cuò)誤標(biāo)志的狀態(tài)外,GL的狀態(tài)未定義。?
GL_STACK_UNDERFLOW??試圖執(zhí)行會(huì)導(dǎo)致內(nèi)部堆棧下溢的操作。?
GL _ STACK _UNDERFLOW?試圖執(zhí)行會(huì)導(dǎo)致內(nèi)部堆棧溢出的操作。
我們定義兩個(gè)函數(shù),在其中用循環(huán)的方式來調(diào)用glGetError

在glDrawElements函數(shù)前調(diào)用ClearError來清除之前的其他的錯(cuò)誤干擾,只關(guān)心glDrawElements函數(shù)有沒有錯(cuò)誤

得到的錯(cuò)誤信息,我們拿到數(shù)字代碼,在調(diào)試中轉(zhuǎn)化為16進(jìn)制,到glew.h文件中去找對(duì)應(yīng)的錯(cuò)誤信息


更智能一點(diǎn)的做法,就是使用斷言,這樣在出現(xiàn)錯(cuò)誤的時(shí)候,就會(huì)在錯(cuò)誤的代碼處自動(dòng)斷點(diǎn)


這樣方便我們更快的定位到錯(cuò)誤處,且不會(huì)一直向控制臺(tái)發(fā)送錯(cuò)誤信息。

我們可以用一個(gè)宏來攬括以上操作