在頂點(diǎn)著色器處理圖元頂點(diǎn)之后進(jìn)入圖元裝配階段扼脐。這一階段瓦侮,執(zhí)行裁剪肚吏、透視分割和Viewport變換操作狭魂。
光柵化是將圖元轉(zhuǎn)換為一組二維片元的過程趁蕊。片元由片元著色器處理掷伙,代表可以在屏幕上繪制的像素任柜。
一沛厨、圖元
圖標(biāo)是一組表示頂點(diǎn)位置的頂點(diǎn)描述逆皮,是可以用OpenGLES 中的glDrawArrays电谣、glDrawElements抹蚀、glDrawRangeElements环壤、GLDrawArraysInstanced 和glDrawElementsInstanced 命令繪制的幾何形狀對(duì)象郑现。
OpenGLES 可以繪制的圖元:點(diǎn)接箫、線列牺、三角形
三角形圖元
代表著描述3D應(yīng)用程序渲染的幾何形狀對(duì)象時(shí)最常用的方法拗窃。OpenGLES支持的三角形圖元有 GL_TRIANGLES随夸、GL_TRIANGLES_STRIP宾毒、GL_TRINAGLES_FAN诈铛。類型示意圖如下:
GL_TRIANGLES 繪制一系列單獨(dú)的三角形幢竹,共繪制 n/3 個(gè)三角形
GL_TRIANGLES_STRIP 繪制一系列相互連接的三角形焕毫,共n - 2 個(gè)三角形
GL_TRIANGLES_FAN 繪制一系列項(xiàng)鏈的三角形驶乾,共繪制 n - 2 個(gè)三角形
直線圖元
OpenGLES 支持的直線圖元有GL_LINES级乐、GL_LINE_STRIP 和 GL_LINELOOP风科, 類型示意圖如下:
GL_LINES 繪制一系列不相連的直線丐重,共繪制 n/2 根直線
GL_LINE_STRIP 繪制一系列相連的直線扮惦,共繪制 n - 1 跟直線
GL_LINE_LOOP 繪制一系列相連的封閉直線崖蜜,共繪制 n 條直線豫领。
直線寬度用 glLineWidth(GLfloat width) 指定
void glLineWidth(GLfloat width)
參數(shù)說明:
width : 指定線寬舔琅,以像素表示备蚓,默認(rèn)寬度為1.0
查詢線寬支持范圍可以用如下命令查詢:
GLfloat lineWidthRange[2];
glGetFoatv(GL_ALIASED_WIDTH_RANGE, lineWidthRange);
點(diǎn)圖元
支持的點(diǎn)圖元是GL_POINTS郊尝。點(diǎn)圖元對(duì)指定的每個(gè)頂點(diǎn)繪制流昏。通常用于粒子系統(tǒng)效果當(dāng)作點(diǎn)而非正方形繪制况凉,從而實(shí)現(xiàn)高效渲染。點(diǎn)圖元是指定位置和半徑的屏幕對(duì)齊的正方形襟锐。位置表示正方形的中心粮坞,半徑用于計(jì)算正方形的4個(gè)坐標(biāo)莫杈。
gl_PointSize 是用于頂點(diǎn)著色器中輸出點(diǎn)半徑的內(nèi)建變量筝闹。
查詢半徑支持非平滑尺寸范圍的限制关顷,可以入以下命令:
GLfloat pointSizeRange[2];
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
OpenGLES 窗口原點(diǎn)(0, 0) 表示左下方,而點(diǎn)圖元的坐標(biāo)原點(diǎn)則表示左上方议双。
gl_PointCoord 是只能在渲染圖元為點(diǎn)圖元時(shí)用于片元著色器的內(nèi)建變量痘番,用mediump 精度限定符限定的vec2 變量,范圍是 0~1
繪制一個(gè)帶有紋理的點(diǎn)圖元的片元著色器程序:
#version 300 es
precision mediump float;
uniform sampler2D s_texSprite;
layout(location = 0) out vec4 outColor;
void main() {
outColor = texture(s_texSprite, gl_PointCoord);
}
二平痰、繪制圖元
void glDrawArrays(GLenum mode, GLint first, ?GLsizei count)
參數(shù)說明
mode : 指定要渲染的圖元汞舱,GL_POINTS、GL_LINES宗雇、GL_LINE_STRIP昂芜、GL_LINE_LOOP、GL_TRIANGLES泌神、GL_TRIANGLES_STRIP、GL_TRIANGLE_FAN
first : 指定啟用的頂點(diǎn)數(shù)組中的起始頂點(diǎn)索引
count : 指定要繪制的頂點(diǎn)數(shù)量
備注:使用glDrawArrays 繪制多面體舞虱,共享的點(diǎn)必須重復(fù)計(jì)算欢际。因此,使用glDrawArrays 繪制立方體需要的是24個(gè)或36個(gè)頂點(diǎn)砾嫉,而不是8個(gè)頂點(diǎn)幼苛。
void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
void glDrawElements(GLenum mode. GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices)
參數(shù)說明:
mode : 指定要渲染的圖元
start : 指定indices 中的最小數(shù)組索引
end : 指定indices 中的最大數(shù)組索引
count : 指定要繪制的索引數(shù)量
type : 指定indices 中保存的元素索引類型窒篱,GL_UNSIGNED_BYTE焕刮、GL_UNSIGNED_SHORT、GL_UNSIGNED_INT
indices : 指向元素索引存儲(chǔ)位置的指針
glDrawArrays 和 glDrawElements 區(qū)別
glDrawArrays :傳輸或指定的數(shù)據(jù)是真實(shí)數(shù)據(jù)墙杯,則繪制時(shí)效率更高配并,但比較吃內(nèi)存/顯存。圖形較少/圖形很多相同高镐,則更節(jié)省空間
glDrawElements : 指定的是索引溉旋,更節(jié)省內(nèi)存/顯存。圖形多且形狀不相同時(shí)優(yōu)先考慮嫉髓。
圖元重啟
使用圖元重啟可以在一次繪圖調(diào)用中渲染多個(gè)不相連的圖元(如三角形/條形帶)观腊,能夠降低繪圖API調(diào)用的開銷邑闲。圖元重啟的另一種方法時(shí)生成退化三角形,但方法不夠簡潔梧油。
可以通過在索引列表中插入一個(gè)特殊索引來重啟一個(gè)用于索引繪圖調(diào)用的圖元實(shí)現(xiàn)苫耸。
這個(gè)特殊索引時(shí)該索引類型的最大可能索引,GL_UNSIGNED_BYTE為255儡陨,GL_UNSIGNED_SHORT為65535
啟用和禁用圖元重啟:
// 啟用圖元
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
// 繪制圖元
…
// 禁用圖元
glDisable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
驅(qū)動(dòng)頂點(diǎn)
如果沒有限定符褪子,那么頂點(diǎn)著色器的輸出值在圖元中使用線性插值。但是使用平面著色時(shí)沒有發(fā)生插值骗村。因?yàn)闆]有發(fā)生插值嫌褪,所以片元著色器中只有一個(gè)頂點(diǎn)值可用。對(duì)于給定的圖元實(shí)例胚股,驅(qū)動(dòng)頂點(diǎn)確定使用頂點(diǎn)著色器的哪個(gè)頂點(diǎn)輸出笼痛,因?yàn)橹荒苁褂靡粋€(gè)頂點(diǎn)。
驅(qū)動(dòng)頂點(diǎn)選擇規(guī)則如下:
GL_POINTS?? ?? ? ?i
GL_LINES?? ??? ?? ? 2i
GL_LINE_LOOP? ?如果 i < n, 為 i + 1信轿,i = n晃痴,為1
GL_LINE_STRIP? ?i + 1
GL_TRIANGLES? ?3i
GL_TRIANGLES_STRIP i + 2
GL_TRIANGLES_FAN? ? i + 2
幾何形狀實(shí)例化
幾何形狀實(shí)例化可以用一次API調(diào)用多次渲染具有不同屬性的一個(gè)對(duì)象。這一功能在渲染大量類似對(duì)象時(shí)很有用财忽,幾何圖形實(shí)例化降低了想OpenGLES 引擎發(fā)送許多 API 調(diào)用的CPU處理開銷倘核。
void glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount);
void glDrawElementsInstanced(GLenum mode, GLsizei num, GLenum type, const GLvoid *indices, GLsizei instanceCount);
訪問每個(gè)實(shí)例數(shù)據(jù)的方法:
1、使用 glVertexAttribDivisor
void glVertexAttribDivisor(GLuint index, GLuint divisor)
參數(shù)說明
index : 指定通用頂點(diǎn)屬性索引
divisor : 指定index位置的通用屬性更新之間傳遞的實(shí)例數(shù)量
2即彪、使用哪件輸入變量gl_InstanceID
性能提示
應(yīng)用程序應(yīng)該確保用盡可能大的圖元尺寸調(diào)用glDrawElements 和 glDrawElementsInstanced紧唱。如果繪制GL_TRIANGLES,這很容易做到隶校。但如果有三角形條帶或扇形的網(wǎng)格漏益,則可以用圖元重啟將這些網(wǎng)格連接在一起,而不用對(duì)每個(gè)三角形條帶網(wǎng)格單獨(dú)調(diào)用glDrawElements*深胳。
如果無法使用圖元重啟機(jī)制將網(wǎng)格連接在一起(為了維護(hù)與舊版本OpenGLES的兼容性)绰疤,可以添加造成退化三角形的元素索引,代價(jià)是使用更多的索引舞终,并且需要注意這里討論的一些事項(xiàng)轻庆。退化三角形是兩個(gè)或者更多頂點(diǎn)相同的三角形。GPU可以非常簡單地檢測和拒絕退化三角形敛劝,所以這是很好的性能改進(jìn)余爆,我們可以將一個(gè)很大的圖元放入由GPU渲染的隊(duì)列。
三夸盟、圖元裝配
通過glDraw*** 提供的頂點(diǎn)由頂點(diǎn)著色器執(zhí)行蛾方,頂點(diǎn)著色器變換的每個(gè)頂點(diǎn)包括描述頂點(diǎn)(x, y, z, w)值的頂點(diǎn)位置。圖元類型和頂點(diǎn)索確定將被渲染的單獨(dú)圖元。對(duì)于每個(gè)單獨(dú)圖元及其對(duì)應(yīng)的頂點(diǎn)桩砰,圖元裝配階段執(zhí)行的操作包括:將頂點(diǎn)著色器的輸出值執(zhí)行裁剪拓春、透視分割、視口變換后進(jìn)入光柵化階段亚隅。
坐標(biāo)系統(tǒng)
頂點(diǎn)以物體或者本地坐標(biāo)空間輸入到OpenGLES痘儡,這是最可能用來建模和存儲(chǔ)一個(gè)對(duì)象的坐標(biāo)空間。在頂點(diǎn)著色器執(zhí)行之后枢步,頂點(diǎn)位置被認(rèn)為是在裁剪坐標(biāo)空間內(nèi)沉删。頂點(diǎn)位置從本地坐標(biāo)系統(tǒng)(也就是物體坐標(biāo)) 到裁剪坐標(biāo)的變換通過加載執(zhí)行這一行轉(zhuǎn)換的對(duì)應(yīng)矩陣來完成,這些矩陣保存在頂點(diǎn)著色器中定義的對(duì)應(yīng)統(tǒng)一變量中醉途。
下圖展示了通過頂點(diǎn)著色器和圖元裝配階段時(shí)的坐標(biāo)系統(tǒng)矾瑰。
裁剪
為了避免在可視景體之外處理圖元,圖元被裁剪到裁剪空間隘擎。執(zhí)行頂點(diǎn)著色器之后的頂點(diǎn)位置處于裁剪坐標(biāo)空間內(nèi)殴穴。裁剪坐標(biāo)是由(x, y, z, w)指定的同類坐標(biāo)。在裁剪空間(x, y, z, w)中定義的頂點(diǎn)坐標(biāo)根據(jù)視景體裁剪货葬。下圖是一個(gè)裁剪體采幌,由6個(gè)裁剪平面定義,稱作近震桶、遠(yuǎn)休傍、左、右蹲姐、上磨取、下裁剪平面。
裁剪階段將把每個(gè)圖元裁剪為上圖所示的裁剪體柴墩。對(duì)于每種圖元類型忙厌,執(zhí)行以下操作:
裁剪三角形 —— 如果三角形完全在視景體內(nèi)部,則不執(zhí)行任何裁剪江咳。如果三角形完全在視景體外逢净,則該三角形被放棄。如果三尖形部分在視景體內(nèi)歼指,則根據(jù)相應(yīng)的屏幕裁剪三角形爹土。裁剪操作將生成新的頂點(diǎn),這些頂點(diǎn)被裁剪到三角扇形的平面东臀。
裁剪直線 —— 直線完全在視景體內(nèi)部着饥,則不執(zhí)行任何裁剪犀农。如果直線完全在視景體之外惰赋,則該直線被放棄。
裁剪點(diǎn) —— 如果點(diǎn)位置在近平面或遠(yuǎn)平面之外,則被拋棄赁濒。否則將不做變化地通過該階段轨奄。
頂點(diǎn)坐標(biāo)經(jīng)過透視裁剪分割之后,變成規(guī)范化的設(shè)備坐標(biāo)拒炎。規(guī)范化的設(shè)備坐標(biāo)范圍是 -1.0 到 1.0挪拟。
透視分割
透視分割將裁剪坐標(biāo)(x, yy, z, w) 指定的點(diǎn)投影到屏幕或者視口上。投影動(dòng)作將(x, y, z)執(zhí)行(x/w)击你、(y/w)玉组、(z/w) 之后,我們得到規(guī)范化的設(shè)備坐標(biāo)(x’, y‘, z’)丁侄。這些坐標(biāo)被稱為規(guī)范化設(shè)備坐標(biāo)惯雳。這些規(guī)范化的坐標(biāo)根據(jù)視口的大小將被轉(zhuǎn)換為真正的屏幕(或窗口)坐標(biāo)。規(guī)范化的z坐標(biāo)將用glDepthRangef 指定的near 和far深度值轉(zhuǎn)換為屏幕的z值鸿摇。這些轉(zhuǎn)換在視口變換階段進(jìn)行石景。
視口變換
視口是一個(gè)二維舉行窗口區(qū)域,是所有OpenGLES渲染操作最終顯示的地方拙吉。視口變換可用如下API調(diào)用誰在
void glViewport(GLint x, GLint y, GLsizei w, GLsizei h)
x, y, 指定視口左下角的窗口坐標(biāo)潮孽,以像素表示
w, h,指定視口的寬度和高度,這些值必須大于0
從規(guī)范化設(shè)備坐標(biāo)(xd, yd, zd)到窗口坐標(biāo)(sw, yw, zw)的變換如下:
在這個(gè)變換中筷黔, ox = x + w/ 2往史, oy = y + h / 2, n和f代表所需的深度范圍
void glDepthRangef(GLclampf n, GLclampf f)
n, f 指定所需的深度范圍佛舱。n和f 的默認(rèn)值分別為0.0 和 1.0怠堪。這兩個(gè)值限于(0.0, 1.0)區(qū)間內(nèi)。
初始視口狀體被設(shè)置為w = width名眉,h = height粟矿,分別為OpenGLES渲染應(yīng)用程序創(chuàng)建的窗口的寬度和高度。
四损拢、光柵化
下圖是光柵化管線陌粹。在頂點(diǎn)變換和圖元裁剪之后,光柵化管線取得單獨(dú)圖元并為該圖元生成對(duì)應(yīng)的片段福压。每個(gè)片段由屏幕空間中的整數(shù)位置(x, y)標(biāo)識(shí)掏秩。
剔除
在三角形被光柵化之前,我們需要確定它們是正面還是背面荆姆。剔除(Culling) 操作拋棄背向觀看者的三角形蒙幻。要確定三角形是正面還是背面,首先要知道它的方向胆筒。
三角形的方向指定從第一個(gè)頂點(diǎn)開始邮破,經(jīng)過第二個(gè)和第三個(gè)頂點(diǎn)诈豌,最后回到第一個(gè)頂點(diǎn)的彎曲方向或者路徑順序。
三角形根據(jù)方向可以分為順時(shí)針方向(CW)和逆時(shí)針方向(CCW)抒和,如下圖所示:
映射方向可以通過調(diào)用glFrontFace指定
void glFrontFace(GLenum dir)
參數(shù)說明:
dir : 指定正面三角形的方向矫渔,GL_CW、GL_CCW摧莽,默認(rèn)值為GL_CCW
剔除三角形的面庙洼,可以通過調(diào)用glCullFace 指定
void glCullFace(GLenum dir)
參數(shù)說明:
dir : 指定正面三角形的方向,GL_FRONT镊辕、GL_BACK 和 GL_FRONT_AND_BACK油够,默認(rèn)值為GL_BACK
要知道剔除操作可以通過glEnable 和 glDisable 啟用/禁用
void ?glEnable(GLenum cap)
void glDisable(GLenum cap)
參數(shù)說明:
cap :GL_CULL_FACE,默認(rèn)情況下征懈,剔除被禁用
備注:剔除應(yīng)該始終啟用叠聋,以避免GPU浪費(fèi)時(shí)間去光柵化不可見的三角形。能夠改善程序的整體性能受裹。
多邊形偏移
繪制兩個(gè)重疊多邊形碌补,很可能產(chǎn)生偽像,這是因?yàn)楣鈻呕木扔邢蕻a(chǎn)生的棉饶,使用的參數(shù)和生成的片元深度值有限精度再好也無法完全解決這個(gè)問題厦章。
為了避免產(chǎn)生這個(gè)問題,我們需要在執(zhí)行深度測試和深度值寫入深度緩沖區(qū)之前照藻,在計(jì)算出來的深度值上添加一個(gè)偏移量袜啃。如果深度測試通過,使用原始的深度值幸缕。
多邊形偏移用如下API調(diào)用設(shè)置:
void glPolygonOffset(GLfloat factor, GLfloat units)
深度偏移的計(jì)算如下:
深度偏移 = m * 因數(shù) + r * 單位數(shù)
m 是三角形最大深度斜率群发,即 m = max{ |az/ax|, |ax/ay| }
斜率項(xiàng)在三角形光柵化階段期間由OpenGLES 實(shí)現(xiàn)計(jì)算, r是OpenGLES實(shí)現(xiàn)定義的常量发乔,代表深度值中可以保證產(chǎn)生差異的最小值熟妓。
五、遮擋查詢
遮擋查詢用查詢對(duì)象來跟蹤通過深度測試的任何片元或樣本栏尚。這種方法可用于不同的技術(shù)起愈,例如鏡頭炫光特效的可見性測試以及避免在包圍體背遮擋的不可見對(duì)象上進(jìn)行幾何狀處理的優(yōu)化。
遮擋查詢可以分別在GL_ANY_SAMPLES_PASSED 或 GL_ANY_SAMPLES_PASSED_CONSERVATIVE 目標(biāo)上用glBeginQuery 和 glEndQuery 開始和結(jié)束
void glBeginQuery(GLenum target, GLuint id)
void glEndQuery(GLenum target)
參數(shù)說明:
target :指定查詢對(duì)象的目標(biāo)類型译仗,GL_ANY_SAMPLES_PASSED抬虽、GL_ANY_SAMPLES_PASSED_CONSERVATIVE、GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
id :指定查詢對(duì)象的名稱
id 用 glGenQueries創(chuàng)建纵菌,用glDeleteQueries刪除
void glGenQueries(GLsizei n, GLuint *ids)
n :指定生成的查詢名稱對(duì)象的數(shù)量
ids : 指定一個(gè)數(shù)組阐污,以存儲(chǔ)查詢名稱對(duì)象的列表。
void glDeleteQueries(GLsizei n, const GLuint *ids)
n :指定要?jiǎng)h除的查詢名稱對(duì)象的數(shù)量
ids :指定一個(gè)需要?jiǎng)h除的查詢名稱對(duì)象的列表數(shù)組
在用glBeginQuery 和 glEndQuery 指定查詢對(duì)象邊界之后咱圆,可以使用 glGetQueryObjectuiv 檢索查詢對(duì)象的結(jié)果笛辟。
void glGetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params)
參數(shù)說明
target :指定查詢對(duì)象名稱
pname : 指定需要檢索的查詢對(duì)象參數(shù)功氨,可以為GL_QUERY_RESULT 或 GL_QUERY_RESULT_AVAILABLE
params : 指定存儲(chǔ)返回參數(shù)值的對(duì)應(yīng)類型的數(shù)組