網(wǎng)站優(yōu)化內(nèi)鏈怎么做如何做網(wǎng)站推廣私人
目錄
- 一、說(shuō)明
- 二、關(guān)于幾何著色器
- 三、原始輸入/輸出規(guī)范
- 3.1 實(shí)例
- 四、輸入
- 五、輸出
- 5.1 分層渲染
- 六、輸出限制
一、說(shuō)明
幾何著色器對(duì)于渲染管線(xiàn)設(shè)計(jì)是一個(gè)新生事物;目前對(duì)應(yīng)于幾何著色器的資料不多,并且說(shuō)法不一,因此如何用幾何著色器,依然需要參照當(dāng)前管線(xiàn)的設(shè)計(jì)細(xì)節(jié),因而該手冊(cè)也就是參考性的,并非權(quán)威。
二、關(guān)于幾何著色器
幾何著色器是可選的,不必使用。
幾何著色器調(diào)用采用單個(gè) Primitive 作為輸入,并且可以輸出零個(gè)或多個(gè) Primitive。對(duì)于可以從單個(gè) GS 調(diào)用生成多少個(gè)基元,存在實(shí)現(xiàn)定義的限制。編寫(xiě) GS 以接受特定的輸入基元類(lèi)型并輸出特定的基元類(lèi)型。
雖然 GS 可用于放大幾何圖形,從而實(shí)現(xiàn)粗略的曲面細(xì)分形式,但這通常不是 GS 的良好用途。使用 GS 的主要原因是:
- 分層渲染:獲取一個(gè)基元并將其渲染到多個(gè)圖像,而無(wú)需更改綁定的渲染目標(biāo)等。
- 變換反饋:這通常用于在 GPU 上執(zhí)行計(jì)算任務(wù)(顯然是預(yù)計(jì)算著色器)。
- 在 OpenGL 4.0 中,GS 獲得了兩個(gè)新功能。一個(gè)是能夠?qū)懭攵鄠€(gè)輸出流。這專(zhuān)門(mén)用于轉(zhuǎn)換反饋,以便不同的反饋緩沖區(qū)集可以獲取不同的轉(zhuǎn)換反饋數(shù)據(jù)。
另一個(gè)功能是 GS 實(shí)例化,它允許對(duì)同一輸入原語(yǔ)進(jìn)行多次調(diào)用。這使得分層渲染更易于實(shí)現(xiàn),并且可能執(zhí)行得更快,因?yàn)槊總€(gè)層的基元都可以由單獨(dú)的 GS 實(shí)例計(jì)算。
注意:雖然幾何著色器以前有 GL_EXT_geometry_shader4 和 GL_ARB_geometry_shader4 等擴(kuò)展,但這些擴(kuò)展以與核心功能截然不同的方式公開(kāi) API 和 GLSL 功能。本頁(yè)僅介紹核心功能。
目錄
- 一、說(shuō)明
- 二、關(guān)于幾何著色器
- 三、原始輸入/輸出規(guī)范
- 3.1 實(shí)例
- 四、輸入
- 五、輸出
- 5.1 分層渲染
- 六、輸出限制
三、原始輸入/輸出規(guī)范
每個(gè)幾何著色器都設(shè)計(jì)為接受特定的 Primitive 類(lèi)型作為輸入,并輸出特定的 Primitive 類(lèi)型。接受的輸入基元類(lèi)型在著色器中定義:
layout(input_primitive?) in;
input_primitive類(lèi)型必須與提供給 GS 的頂點(diǎn)流的基元類(lèi)型匹配。如果啟用了 Tessellation,則基元類(lèi)型由 Tessellation Evaluation Shader 的輸出限定符指定。如果未啟用 Tesslation,則基元類(lèi)型由使用此著色器程序渲染的繪圖命令提供。input_primitive的有效值以及有效的 OpenGL 基元類(lèi)型或曲面細(xì)分形式為:
GS輸入 | OpenGL 基元 | TES參數(shù) | 頂點(diǎn)計(jì)數(shù) |
---|---|---|---|
點(diǎn) | GL_POINTS | point_mode | 1 |
線(xiàn) | GL_LINES、GL_LINE_STRIP GL_LINE_LOOP | 等值線(xiàn) | 2 |
lines_adjacency | GL_LINES_ADJACENCY,GL_LINE_STRIP_ADJACENCY | 不適用 | 4 |
三角形 | GL_TRIANGLES、GL_TRIANGLE_STRIP GL_TRIANGLE_FAN | 三角形、四邊形 | 3 |
triangles_adjacency | GL_TRIANGLES_ADJACENCY,GL_TRIANGLE_STRIP_ADJACENCY | 不適用 | 6 |
頂點(diǎn)計(jì)數(shù)是 GS 接收的每個(gè)輸入基元的頂點(diǎn)數(shù)。
輸出基元類(lèi)型定義如下:
layout(output_primitive?, max_vertices = vert_count?) out;
output_primitive必須是以下項(xiàng)之一:
- point
- line_strip
- triangle_strip
它們的工作方式與它們的對(duì)應(yīng) OpenGL 渲染模式完全相同。要輸出單個(gè)三角形或直線(xiàn),只需在發(fā)出每組 3 或 2 個(gè)頂點(diǎn)后使用 EndPrimitive(見(jiàn)下文)。
輸出必須有max_vertices聲明。該數(shù)字必須是編譯時(shí)常量,它定義了 GS 的單次調(diào)用將寫(xiě)入的最大頂點(diǎn)數(shù)。它不能大于實(shí)現(xiàn)定義的MAX_GEOMETRY_OUTPUT_VERTICES限制。此限制的最小值為 256。請(qǐng)參閱下面的限制。
3.1 實(shí)例
GS 實(shí)例化
核心版本 4.6
核心自版本起 4.0
核心 ARB 擴(kuò)展 ARB_gpu_shader5
GS 也可以實(shí)例化(這與實(shí)例化渲染是分開(kāi)的,因?yàn)樗驯镜鼗?GS)。這會(huì)導(dǎo)致 GS 對(duì)同一輸入基元執(zhí)行多次。每次對(duì)特定輸入原語(yǔ)的 GS 調(diào)用都會(huì)得到一個(gè)不同的gl_InvocationID值。這對(duì)于分層渲染和輸出到多個(gè)流非常有用(見(jiàn)下文)。
若要使用實(shí)例化,必須有一個(gè)輸入布局限定符:
layout(invocations = num_instances?) in;
num_instances 的值不得大于 MAX_GEOMETRY_SHADER_INVOCATIONS(至少為 32)。內(nèi)置值 gl_InvocationID 指定此著色器的特定實(shí)例;它將處于半開(kāi)范圍 [0, num_instances)。
實(shí)例的輸出基元按gl_InvocationID排序。因此,如果用戶(hù)渲染兩個(gè)基元,并將num_instances設(shè)置為 3,則 GS 將按以下順序有效調(diào)用:(prim0, inst0), (prim0, inst1), (prim0, inst2), (prim1, inst0), …GS 的輸出基元將根據(jù)該輸入序列進(jìn)行排序。因此,如果 (prim0, inst0) 輸出兩個(gè)三角形,則在渲染 (prim0, inst1) 中的任何三角形之前,它們都將被渲染。
四、輸入
幾何著色器采用基元作為輸入;每個(gè)基元都由一定數(shù)量的頂點(diǎn)組成,這些頂點(diǎn)由著色器中的輸入基元類(lèi)型定義。
因此,頂點(diǎn)著色器(或 Tessellation Stage,視情況而定)的輸出將作為變量數(shù)組饋送到 GS。這些變量可以組織為單個(gè)變量,也可以組織為接口塊的一部分。每個(gè)單獨(dú)的變量都是一個(gè)與基元頂點(diǎn)計(jì)數(shù)長(zhǎng)度相同的數(shù)組;對(duì)于接口塊,塊本身將以此長(zhǎng)度排列。輸入數(shù)組中頂點(diǎn)的順序?qū)?yīng)于先前著色器階段指定的頂點(diǎn)順序。
幾何著色器輸入可能具有插值限定符。如果這樣做,則前一階段的輸出必須使用相同的限定符。
五 ·E
幾何著色器提供以下內(nèi)置輸入變量:
in gl_PerVertex
{vec4 gl_Position;float gl_PointSize;float gl_ClipDistance[];
} gl_in[];
這些變量?jī)H具有通過(guò)它們的先前著色器階段賦予它們的含義。
有些 GS 輸入值基于基元,而不是頂點(diǎn)。這些不會(huì)聚合到數(shù)組中。這些是:
in int gl_PrimitiveIDIn;
in int gl_InvocationID; // Requires GLSL 4.0 or ARB_gpu_shader5
gl_PrimitiveIDIn
當(dāng)前輸入基元的 ID,基于自當(dāng)前繪圖命令啟動(dòng)以來(lái) GS 處理的基元數(shù)。
gl_InvocationID
實(shí)例化幾何著色器時(shí)定義的當(dāng)前實(shí)例。
五、輸出
幾何著色器可以根據(jù)需要輸出任意數(shù)量的頂點(diǎn)(最多為 max_vertices 布局規(guī)范指定的最大值)。為此,幾何著色器中的輸出值不是數(shù)組。相反,使用基于函數(shù)的接口。
GS 代碼寫(xiě)入頂點(diǎn)的所有輸出值,然后調(diào)用 EmitVertex()。這告訴系統(tǒng)將這些輸出值寫(xiě)入輸出頂點(diǎn)的寫(xiě)入位置。調(diào)用此函數(shù)后,所有輸出變量都包含未定義的值。因此,在發(fā)出下一個(gè)頂點(diǎn)(如果有下一個(gè)頂點(diǎn))之前,您需要再次寫(xiě)入它們。
注意:您必須在每次 EmitVertex() 調(diào)用之前寫(xiě)入每個(gè)輸出變量(對(duì)于每個(gè) EmitStreamVertex() 調(diào)用的流的所有輸出)。
GS 定義了這些頂點(diǎn)輸出所代表的基元類(lèi)型。GS 還可以通過(guò)調(diào)用 EndPrimitive() 函數(shù)來(lái)結(jié)束基元并啟動(dòng)新基元。這不會(huì)發(fā)出頂點(diǎn)。
為了從 GS 寫(xiě)入兩個(gè)獨(dú)立的三角形,您必須使用前三個(gè)頂點(diǎn)的 EmitVertex() 編寫(xiě)三個(gè)單獨(dú)的頂點(diǎn),然后調(diào)用 EndPrimitive() 以結(jié)束條帶并啟動(dòng)一個(gè)新條帶。然后你用 EmitVertex() 再寫(xiě)三個(gè)頂點(diǎn)。
對(duì)于 GLSL,輸出變量定義為正常變量。根據(jù)需要,它們可以分組為接口塊或單個(gè)值??梢允褂貌逯迪薅ǚx輸出變量。Fragment Shader 等效接口變量應(yīng)使用相同的限定符定義相同的變量。
五 ·E
幾何著色器具有以下內(nèi)置輸出。
out gl_PerVertex
{vec4 gl_Position;float gl_PointSize;float gl_ClipDistance[];
};
gl_PerVertex定義了輸出的接口塊。該塊在沒(méi)有實(shí)例名稱(chēng)的情況下定義,因此不需要在名稱(chēng)前加上前綴。
GS 是最后的頂點(diǎn)處理階段。因此,除非關(guān)閉柵格化,否則必須寫(xiě)入其中一些值。這些輸出始終與流 0 相關(guān)聯(lián)。因此,如果要向其他流發(fā)出頂點(diǎn),則不必寫(xiě)入它們。
gl_Position
當(dāng)前頂點(diǎn)的剪輯空間輸出位置。如果要向流 0 發(fā)出頂點(diǎn),則必須寫(xiě)入此值,除非柵格化處于關(guān)閉狀態(tài)。
gl_PointSize
被柵格化的點(diǎn)的像素寬度/高度。只有在輸出點(diǎn)基元時(shí)才需要寫(xiě)入它。
gl_ClipDistance
允許著色器設(shè)置從頂點(diǎn)到每個(gè)用戶(hù)定義的裁剪平面的距離。正距離表示頂點(diǎn)位于裁剪平面的內(nèi)部/后面,負(fù)距離表示頂點(diǎn)位于裁剪平面的外部/前面。為了使用此變量,用戶(hù)必須手動(dòng)重新聲明它(以及接口塊),并具有顯式大小。
某些預(yù)定義的輸出具有特殊的含義和語(yǔ)義。
out int gl_PrimitiveID;
原始 ID 將傳遞給片段著色器。特定直線(xiàn)/三角形的原始 ID 將從該直線(xiàn)/三角形的挑釁頂點(diǎn)中獲取,因此請(qǐng)確保為正確的挑釁頂點(diǎn)編寫(xiě)正確的值。
這個(gè)值的含義是你想要的。但是,如果要匹配標(biāo)準(zhǔn)的 OpenGL 含義(即:如果不使用 GS,Fragment Shader 會(huì)得到什么),則必須在發(fā)出之前對(duì)每個(gè)頂點(diǎn)執(zhí)行此操作:
gl_PrimitiveID = gl_PrimitiveIDIn;
這自然假設(shè) GS 輸出的基元數(shù)等于 GS 接收的基元數(shù)。
5.1 分層渲染
分層渲染是讓 GS 將特定基元發(fā)送到分層幀緩沖區(qū)的不同層的過(guò)程。這對(duì)于執(zhí)行基于立方體的陰影貼圖非常有用,甚至可以用于渲染立方體環(huán)境貼圖,而無(wú)需多次渲染整個(gè)場(chǎng)景。
五 ·E
GS 中的分層渲染通過(guò)兩個(gè)特殊的輸出變量工作:
out int gl_Layer;
out int gl_ViewportIndex; // Requires GL 4.1 or ARB_viewport_array.
gl_Layer輸出定義基元轉(zhuǎn)到分層圖像中的哪個(gè)層?;械拿總€(gè)頂點(diǎn)都必須獲得相同的層索引。請(qǐng)注意,當(dāng)渲染到立方體貼圖數(shù)組時(shí),gl_Layer值表示圖層面(圖層中的面),而不是立方體貼圖的層。
gl_ViewportIndex需要 GL 4.1 或 ARB_viewport_array,它指定要與此基元一起使用的視口索引。
注意:ARB_viewport_array雖然在技術(shù)上是一項(xiàng) 4.1 功能,但在 NVIDIA 和 AMD 的 3.3 硬件上廣泛可用。
使用 GS 實(shí)例化可以提高分層渲染的效率,因?yàn)椴煌?GS 調(diào)用可以并行處理實(shí)例。但是,雖然 ARB_viewport_array 通常在 3.3 硬件中實(shí)現(xiàn),但沒(méi)有 3.3 硬件提供ARB_gpu_shader5支持。
警告:gl_Layer 和 gl_ViewportIndex 是 GS 輸出變量。因此,每次調(diào)用 EmitVertex 時(shí),它們的值都將變?yōu)槲炊x。因此,每次循環(huán)輸出時(shí)都必須設(shè)置這些變量。
如果幾何著色器從不寫(xiě)入 gl_ViewportIndex,則所有內(nèi)容的行為就像寫(xiě)入 0 一樣。
哪個(gè)頂點(diǎn)
gl_Layer 和 gl_ViewportIndex 是每個(gè)頂點(diǎn)的參數(shù),但它們指定了適用于整個(gè)基元的屬性。因此,出現(xiàn)了一個(gè)問(wèn)題:特定基元中的哪個(gè)頂點(diǎn)定義了該基元的圖層和視口索引?
答案是它依賴(lài)于實(shí)現(xiàn)。但是,OpenGL 確實(shí)有兩個(gè)查詢(xún)來(lái)確定當(dāng)前實(shí)現(xiàn)使用哪一個(gè):GL_LAYER_PROVOKING_VERTEX 和 GL_VIEWPORT_INDEX_PROVOKING_VERTEX。
從 glGetIntegerv 返回的值將是以下枚舉器之一:
- GL_PROVOKING_VERTEX:使用的頂點(diǎn)將跟蹤當(dāng)前激發(fā)的頂點(diǎn)約定。
- GL_LAST_VERTEX_CONVENTION:使用的頂點(diǎn)將由最后一個(gè)頂點(diǎn)激發(fā)頂點(diǎn)約定定義的頂點(diǎn)。
- GL_FIRST_VERTEX_CONVENTION:使用的頂點(diǎn)將由第一個(gè)頂點(diǎn)激發(fā)頂點(diǎn)約定定義的頂點(diǎn)。
- GL_UNDEFINED_VERTEX:實(shí)現(xiàn)不是說(shuō)。
為了獲得最大的可移植性,您必須為每個(gè)基元提供相同的圖層和視口索引。因此,如果您想輸出一個(gè)三角形條帶,其中不同的三角形具有不同的索引,那就太糟糕了。您必須將其拆分為不同的基元。
輸出流
輸出流
核心版本 4.6
核心自版本起 4.0
核心 ARB 擴(kuò)展 ARB_transform_feedback3
使用變換反饋計(jì)算值時(shí),能夠以不同的速率將不同的頂點(diǎn)集發(fā)送到不同的緩沖區(qū)通常很有用。例如,GS 可以將頂點(diǎn)數(shù)據(jù)發(fā)送到一個(gè)流,同時(shí)在另一個(gè)流中構(gòu)建每個(gè)實(shí)例的數(shù)據(jù)。頂點(diǎn)數(shù)據(jù)和每個(gè)實(shí)例的數(shù)據(jù)將具有不同的長(zhǎng)度,以不同的速度寫(xiě)入。
多流輸出要求輸出基元類(lèi)型為點(diǎn)。您仍然可以接受您喜歡的任何輸入。
為了提供這一點(diǎn),可以為輸出變量提供帶有布局限定符的流索引:
layout(stream = stream_index) out vec4 some_output;
stream_index范圍從 0 到 GL_MAX_VERTEX_STREAMS - 1。
可以使用以下命令設(shè)置流的默認(rèn)值:
layout(stream = 2) out;
所有以下變量都將使用流 2,除非它們指定了流。以后可以更改默認(rèn)值。初始默認(rèn)值為 0。
若要將頂點(diǎn)寫(xiě)入特定流,請(qǐng)使用函數(shù) EmitStreamVertex。此函數(shù)采用流索引;僅寫(xiě)入這些輸出變量。同樣,EndStreamPrimitive 結(jié)束特定流的基元。但是,由于多流輸出需要使用點(diǎn)基元,因此后一個(gè)函數(shù)不是很有用。
實(shí)際上,只有發(fā)送到流 0 的基元才會(huì)傳遞給 Vertex Post-Processing 并呈現(xiàn);其余的流只有在使用轉(zhuǎn)換反饋時(shí)才有意義。調(diào)用 EmitVertex 或 EndPrimitive 等同于使用流 0 調(diào)用它們的流對(duì)應(yīng)項(xiàng)。
六、輸出限制
幾何著色器的輸出存在兩個(gè)相互競(jìng)爭(zhēng)的限制:
- GS 的單次調(diào)用可以輸出的最大頂點(diǎn)數(shù)。
- GS 的單次調(diào)用可以輸出的最大輸出組件總數(shù)。
第一個(gè)限制(由 GL_MAX_GEOMETRY_OUTPUT_VERTICES 定義)是可以提供給max_vertices輸出布局限定符的最大數(shù)量。單個(gè)幾何著色器調(diào)用都不能超過(guò)此數(shù)字。
另一個(gè)限制,由 GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 定義,通俗地說(shuō),是單個(gè) GS 調(diào)用可以寫(xiě)入的內(nèi)容總量。它是輸出值的總數(shù)(在 GLSL 術(shù)語(yǔ)中,分量是向量的分量。所以浮點(diǎn)數(shù)是一個(gè)組成部分;vec3 是單個(gè) GS 調(diào)用可以寫(xiě)入的 3 個(gè)組件。這與 GL_MAX_GEOMETRY_OUTPUT_COMPONENTS(輸出變量中允許的最大組件數(shù))不同。總輸出分量是可以寫(xiě)入的分量總數(shù) + 頂點(diǎn)。
例如,如果總輸出組件計(jì)數(shù)為 1024(GL 4.3 中的最小最大值),并且輸出流寫(xiě)入 12 個(gè)組件,則可寫(xiě)入的頂點(diǎn)總數(shù)為
f l o o r ( 1024 12 ) = 85 {\displaystyle floor({\tfrac {1024}{12}})=85} floor(121024?)=85
這是對(duì)可以寫(xiě)入的頂點(diǎn)數(shù)的絕對(duì)硬性限制。即使GL_MAX_GEOMETRY_OUTPUT_VERTICES大于 85,由于此頂點(diǎn)著色器為每個(gè)頂點(diǎn)寫(xiě)入 12 個(gè)分量,因此此幾何著色器可以寫(xiě)入的真正最大值為 85 個(gè)頂點(diǎn)。如果幾何著色器每個(gè)頂點(diǎn)只寫(xiě)入 8 個(gè)分量,那么它可以寫(xiě)入 128 個(gè)分量(當(dāng)然,受輸出頂點(diǎn)限制的約束)。
請(qǐng)注意,即使是像 gl_Layer 這樣的內(nèi)置輸出也計(jì)入GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS。例如,總輸出組件計(jì)數(shù)為 1024 的幾何著色器,輸出 vec4 gl_Position 和 int gl_Layer 最多支持
f l o o r ( 1024 4 + 1 ) = 204 {\displaystyle floor({\tfrac {1024}{4+1}})=204} floor(4+11024?)=204
頂點(diǎn)。