版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2018.10.10 星期三 |
前言
很多做視頻和圖像的,相信對這個框架都不是很陌生,它渲染高級3D圖形,并使用GPU執(zhí)行數(shù)據(jù)并行計(jì)算。接下來的幾篇我們就詳細(xì)的解析這個框架。感興趣的看下面幾篇文章。
1. Metal框架詳細(xì)解析(一)—— 基本概覽
2. Metal框架詳細(xì)解析(二) —— 器件和命令(一)
3. Metal框架詳細(xì)解析(三) —— 渲染簡單的2D三角形(一)
4. Metal框架詳細(xì)解析(四) —— 關(guān)于GPU Family 4(一)
5. Metal框架詳細(xì)解析(五) —— 關(guān)于GPU Family 4之關(guān)于Imageblocks(二)
6. Metal框架詳細(xì)解析(六) —— 關(guān)于GPU Family 4之關(guān)于Tile Shading(三)
7. Metal框架詳細(xì)解析(七) —— 關(guān)于GPU Family 4之關(guān)于光柵順序組(四)
8. Metal框架詳細(xì)解析(八) —— 關(guān)于GPU Family 4之關(guān)于增強(qiáng)的MSAA和Imageblock采樣覆蓋控制(五)
9. Metal框架詳細(xì)解析(九) —— 關(guān)于GPU Family 4之關(guān)于線程組共享(六)
10. Metal框架詳細(xì)解析(十) —— 基本組件(一)
11. Metal框架詳細(xì)解析(十一) —— 基本組件之器件選擇 - 圖形渲染的器件選擇(二)
12. Metal框架詳細(xì)解析(十二) —— 基本組件之器件選擇 - 計(jì)算處理的設(shè)備選擇(三)
13. Metal框架詳細(xì)解析(十三) —— 計(jì)算處理(一)
14. Metal框架詳細(xì)解析(十四) —— 計(jì)算處理之你好,計(jì)算(二)
15. Metal框架詳細(xì)解析(十五) —— 計(jì)算處理之關(guān)于線程和線程組(三)
16. Metal框架詳細(xì)解析(十六) —— 計(jì)算處理之計(jì)算線程組和網(wǎng)格大小(四)
17. Metal框架詳細(xì)解析(十七) —— 工具、分析和調(diào)試(一)
18. Metal框架詳細(xì)解析(十八) —— 工具、分析和調(diào)試之Metal GPU Capture(二)
19. Metal框架詳細(xì)解析(十九) —— 工具、分析和調(diào)試之GPU活動監(jiān)視器(三)
20. Metal框架詳細(xì)解析(二十) —— 工具、分析和調(diào)試之關(guān)于Metal著色語言文件名擴(kuò)展名、使用Metal的命令行工具構(gòu)建庫和標(biāo)記Metal對象和命令(四)
21. Metal框架詳細(xì)解析(二十一) —— 基本課程之基本緩沖區(qū)(一)
22. Metal框架詳細(xì)解析(二十二) —— 基本課程之基本紋理(二)
23. Metal框架詳細(xì)解析(二十三) —— 基本課程之CPU和GPU同步(三)
24. Metal框架詳細(xì)解析(二十四) —— 基本課程之參數(shù)緩沖 - 基本參數(shù)緩沖(四)
25. Metal框架詳細(xì)解析(二十五) —— 基本課程之參數(shù)緩沖 - 帶有數(shù)組和資源堆的參數(shù)緩沖區(qū)(五)
26. Metal框架詳細(xì)解析(二十六) —— 基本課程之參數(shù)緩沖 - 具有GPU編碼的參數(shù)緩沖區(qū)(六)
27. Metal框架詳細(xì)解析(二十七) —— 高級技術(shù)之圖層選擇的反射(一)
28. Metal框架詳細(xì)解析(二十八) —— 高級技術(shù)之使用專用函數(shù)的LOD(一)
29. Metal框架詳細(xì)解析(二十九) —— 高級技術(shù)之具有參數(shù)緩沖區(qū)的動態(tài)地形(一)
Deferred Lighting - 延遲照明
演示如何實(shí)現(xiàn)利用獨(dú)特Metal功能的延遲照明渲染器。
此示例演示了延遲照明渲染器,該渲染器使用陰影貼圖實(shí)現(xiàn)陰影,并使用模板緩沖區(qū)剔除光量。
與前向照明相比,延遲照明可以更容易地渲染大量燈光。 例如,對于前向照明,在具有許多光源的場景中,每個片段計(jì)算每個光的貢獻(xiàn)是不可行的。 必須實(shí)施復(fù)雜的排序和分級算法,以限制僅對影響每個片段的那些光的光貢獻(xiàn)的計(jì)算。 通過延遲照明,可以輕松地將多個燈光應(yīng)用于場景。
Review Important Concepts - 回顧重要概念
在開始使用示例應(yīng)用程序之前,請查看這些概念,以便更好地了解延遲照明渲染器的關(guān)鍵細(xì)節(jié)以及一些獨(dú)特的Metal功能。
1. Traditional Deferred Lighting Renderer - 傳統(tǒng)的延遲照明渲染器
傳統(tǒng)的延遲光照渲染器通常分為兩個渲染過程:
First pass: G-buffer rendering - 第一遍:G緩沖區(qū)渲染。渲染器繪制并轉(zhuǎn)換場景的模型,片段函數(shù)將結(jié)果渲染為稱為幾何緩沖區(qū)或G緩沖區(qū)
(geometry buffer or G-buffer)
的紋理集合。 G緩沖區(qū)包含模型中的材質(zhì)顏色,以及每個片段的法線,陰影和深度值。Second pass: Deferred lighting and composition - 第二關(guān):延遲照明和構(gòu)圖。渲染器繪制每個光量,使用G緩沖區(qū)數(shù)據(jù)重建每個片段的位置并應(yīng)用光照計(jì)算。在繪制燈光時,每個燈光的輸出會混合在之前的燈光輸出之上。最后,渲染器通過執(zhí)行全屏四邊形或計(jì)算內(nèi)核將其他數(shù)據(jù)(例如陰影和定向照明)合成到場景上。
注意:
macOS GPU
具有立即模式渲染(IMR)
架構(gòu)。 在IMR GPU上,延遲照明渲染器只能在至少兩個渲染過程中實(shí)現(xiàn)。 因此,該示例為應(yīng)用程序的macOS版本實(shí)現(xiàn)了two-pass
延遲照明算法。
2. Single-Pass Deferred Lighting on iOS and tvOS GPUs - iOS和tvOS GPU上的單通道延遲照明
iOS和tvOS GPU具有基于圖塊的延遲渲染(TBDR)
架構(gòu),允許它們將數(shù)據(jù)渲染到GPU內(nèi)的tile
內(nèi)存。通過渲染到tile
內(nèi)存,該設(shè)備避免了GPU和系統(tǒng)存儲器之間潛在的昂貴的往返(通過帶寬受限的存儲器總線)。 GPU是否將tile
內(nèi)存寫入系統(tǒng)內(nèi)存取決于以下配置:
- 應(yīng)用程序渲染命令編碼器的存儲操作。
- 應(yīng)用程序紋理的存儲模式。
當(dāng)MTLStoreActionStore
設(shè)置為存儲操作時,渲染過程的渲染目標(biāo)的輸出數(shù)據(jù)將從tile內(nèi)存寫入系統(tǒng)內(nèi)存,其中渲染目標(biāo)由紋理支持。如果此數(shù)據(jù)隨后用于后續(xù)渲染過程,則將來自這些紋理的輸入數(shù)據(jù)從系統(tǒng)存儲器讀取到GPU中的紋理高速緩存中。因此,訪問系統(tǒng)存儲器的傳統(tǒng)延遲照明渲染器要求G緩沖區(qū)數(shù)據(jù)在第一和第二渲染通道之間存儲在系統(tǒng)存儲器中。
但是,由于其TBDR
架構(gòu),iOS和tvOS GPU也可以在任何給定時間從tile
內(nèi)存中讀取數(shù)據(jù)。這允許片段著色器在將此數(shù)據(jù)再次寫入tile內(nèi)存之前從tile內(nèi)存中的渲染目標(biāo)讀取并執(zhí)行計(jì)算。此功能允許樣本避免在第一次和第二次渲染過程之間將G緩沖區(qū)數(shù)據(jù)存儲在系統(tǒng)內(nèi)存中;因此,延遲的照明渲染器可以用單個渲染過程實(shí)現(xiàn)。
G緩沖區(qū)數(shù)據(jù)由單個渲染過程中的GPU(而不是CPU)專門生成和使用。因此,在渲染過程開始之前,不會從系統(tǒng)內(nèi)存加載此數(shù)據(jù),也不會在渲染過程完成后將其存儲在系統(tǒng)內(nèi)存中。光照片段不是從系統(tǒng)存儲器中的紋理讀取G緩沖區(qū)數(shù)據(jù),而是從G緩沖區(qū)讀取數(shù)據(jù),同時它仍然作為渲染目標(biāo)附加到渲染通道。因此,不需要為G緩沖區(qū)紋理分配系統(tǒng)內(nèi)存,并且可以使用MTLStorageModeMemoryless
存儲模式聲明這些紋理中的每一個。
注意:允許
TBDR GPU
從片段函數(shù)中的附加渲染目標(biāo)讀取的功能也稱為可編程混合(programmable blending)
。
3. Deferred Lighting with Raster Order Groups - 使用光柵順序組的延遲照明
默認(rèn)情況下,當(dāng)片段著色器將數(shù)據(jù)寫入像素時,GPU會等待著色器完全寫入該像素,然后再開始執(zhí)行該像素的另一個片段著色器。
光柵順序組允許應(yīng)用程序增加GPU片段著色器的并行化。 對于柵格順序組,片段函數(shù)可以將渲染目標(biāo)分成不同的執(zhí)行組。 這種分離允許GPU在片段著色器的前一個實(shí)例完成將數(shù)據(jù)寫入另一個組中的像素之前,從一個組中的渲染目標(biāo)讀取并執(zhí)行計(jì)算。
在此示例中,某些光照片段函數(shù)使用以下柵格順序組:
Raster order group 0 - 光柵順序組0。
AAPLLightingROG
用于包含光照計(jì)算結(jié)果的渲染目標(biāo)。Raster order group 1 - 光柵順序組1。
AAPLGBufferROG
用于照明函數(shù)中的G緩沖區(qū)數(shù)據(jù)。
這些柵格順序組允許GPU在片段著色器中讀取G緩沖區(qū)并執(zhí)行光照計(jì)算,然后從片段著色器的先前實(shí)例的光照計(jì)算完成寫入其輸出數(shù)據(jù)。
Render a Deferred Lighting Frame - 渲染延遲照明幀
該示例通過按以下順序呈現(xiàn)這些階段來呈現(xiàn)每個完整幀:
Shadow map
G-buffer
Directional light
Light mask
Point lights
Skybox
Fairy lights
示例的iOS和tvOS渲染器生成G緩沖區(qū),并在單個渲染過程中執(zhí)行所有后續(xù)階段。 由于iOS和tvOS GPU的TBDR
架構(gòu),這種單通道實(shí)現(xiàn)是可能的,它允許設(shè)備從tile
存儲器中的渲染目標(biāo)讀取G緩沖區(qū)數(shù)據(jù)。
id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:_viewRenderPassDescriptor];
[super drawGBuffer:renderEncoder];
[self drawDirectionalLight:renderEncoder];
[super drawPointLightMask:renderEncoder];
[self drawPointLights:renderEncoder];
[super drawSky:renderEncoder];
[super drawFairies:renderEncoder];
[renderEncoder endEncoding];
示例的macOS
渲染器在一個渲染過程中生成G緩沖區(qū),然后在另一個渲染過程中執(zhí)行所有后續(xù)階段。 由于macOS GPU
的IMR
架構(gòu),這種雙通道實(shí)現(xiàn)是必要的,這需要設(shè)備從視頻存儲器中的紋理中采樣G緩沖區(qū)數(shù)據(jù)。
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:_GBufferRenderPassDescriptor];
[super drawGBuffer:renderEncoder];
[renderEncoder endEncoding];
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:_finalRenderPassDescriptor];
[self drawDirectionalLight:renderEncoder];
[super drawPointLightMask:renderEncoder];
[self drawPointLights:renderEncoder];
[super drawSky:renderEncoder];
[super drawFairies:renderEncoder];
[renderEncoder endEncoding];
Render the Shadow Map - 渲染陰影貼圖
該示例通過從燈光的角度渲染模型,為場景中的單個方向光(太陽)渲染陰影貼圖。
陰影貼圖的渲染管道具有頂點(diǎn)函數(shù)但不具有片段函數(shù);因此,示例可以確定寫入陰影貼圖的屏幕空間深度值,而無需執(zhí)行渲染管線的其他階段。 (另外,因?yàn)殇秩竟艿罌]有片段函數(shù),所以它的執(zhí)行速度比沒有片段函數(shù)時要快得多。)
MTLRenderPipelineDescriptor *renderPipelineDescriptor = [MTLRenderPipelineDescriptor new];
renderPipelineDescriptor.label = @"Shadow Gen";
renderPipelineDescriptor.vertexDescriptor = nil;
renderPipelineDescriptor.vertexFunction = shadowVertexFunction;
renderPipelineDescriptor.fragmentFunction = nil;
renderPipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float;
_shadowGenPipelineState = [_device newRenderPipelineStateWithDescriptor:renderPipelineDescriptor
error:&error];
在為陰影貼圖繪制幾何圖形之前,示例會設(shè)置深度偏差值以減少陰影瑕疵。
[encoder setDepthBias:0.015 slopeScale:7 clamp:0.02];
然后,在G緩沖階段的片段函數(shù)中,示例測試片段是否被遮擋和陰影。
float shadow_sample = shadowMap.sample_compare(shadowSampler, in.shadow_coord.xy, in.shadow_coord.z);
該示例將sample_compare
函數(shù)的結(jié)果存儲在normal_shadow
渲染目標(biāo)的w
組件中。
gBuffer.normal_shadow = half4(eye_normal.xyz, shadow_sample);
在定向光和點(diǎn)光組成階段,示例從G緩沖區(qū)讀取陰影值并將其應(yīng)用于片段。
Render the G-Buffer - 渲染G緩沖區(qū)
示例的G緩沖區(qū)包含以下紋理:
-
albedo_specular_GBuffer
,用于存儲反照率和鏡面反射數(shù)據(jù)。 反照率數(shù)據(jù)存儲在x
,y
和z
分量中;鏡面數(shù)據(jù)存儲在w
組件中。 -
normal_shadow_GBuffer
,存儲正常和陰影數(shù)據(jù)。 普通數(shù)據(jù)存儲在x,y和z分量中;陰影數(shù)據(jù)存儲在w組件中。 -
depth_GBuffer
,用于存儲眼睛空間中的深度值。
當(dāng)示例呈現(xiàn)G緩沖區(qū)時,iOS和tvOS渲染器以及macOS渲染器將所有G緩沖區(qū)紋理附加為渲染過程的渲染目標(biāo)。 但是,由于iOS和tvOS設(shè)備都可以渲染G緩沖區(qū)并在單個渲染過程中從中讀取,因此該示例使用無記憶存儲模式創(chuàng)建iOS和tvOS紋理,這表明系統(tǒng)內(nèi)存未分配給這些紋理。 相反,在渲染過程的持續(xù)時間內(nèi),這些紋理僅在tile內(nèi)存中分配和填充。
該示例在常見的drawableSizeWillChange:withGBufferStorageMode:
方法中創(chuàng)建G緩沖區(qū)紋理,但iOS和tvOS渲染器將storageMode
變量設(shè)置為MTLStorageModeMemoryless
,而macOS
渲染器將其設(shè)置為MTLStorageModePrivate
。
GBufferTextureDesc.storageMode = storageMode;
GBufferTextureDesc.pixelFormat = _albedo_specular_GBufferFormat;
_albedo_specular_GBuffer = [_device newTextureWithDescriptor:GBufferTextureDesc];
GBufferTextureDesc.pixelFormat = _normal_shadow_GBufferFormat;
_normal_shadow_GBuffer = [_device newTextureWithDescriptor:GBufferTextureDesc];
GBufferTextureDesc.pixelFormat = _depth_GBufferFormat;
_depth_GBuffer = [_device newTextureWithDescriptor:GBufferTextureDesc];
對于macOS
渲染器,在示例完成將數(shù)據(jù)寫入G緩沖區(qū)紋理后,它會調(diào)用endEncoding方法來完成G緩沖區(qū)渲染過程。 由于渲染命令編碼器的存儲操作設(shè)置為MTLStoreActionStore
,因此當(dāng)編碼器完成執(zhí)行時,GPU會將每個渲染目標(biāo)紋理寫入視頻內(nèi)存。 這允許樣本在隨后的延遲光照和合成渲染過程中從視頻存儲器中讀取這些紋理。
對于iOS和tvOS渲染器,在示例完成將數(shù)據(jù)寫入G緩沖區(qū)紋理后,示例不會最終確定渲染命令編碼器,而是繼續(xù)將其用于后續(xù)階段。
Apply the Directional Lighting and Shadows - 應(yīng)用定向光照和陰影
該示例將定向光照和陰影應(yīng)用于指向顯示的drawable
。
macOS
渲染器從設(shè)置為片段函數(shù)參數(shù)的紋理中讀取G緩沖區(qū)數(shù)據(jù)。
fragment half4
deferred_directional_lighting_fragment(QuadInOut in [[ stage_in ]],
constant AAPLUniforms & uniforms [[ buffer(AAPLBufferIndexUniforms) ]],
texture2d<half> albedo_specular_GBuffer [[ texture(AAPLRenderTargetAlbedo) ]],
texture2d<half> normal_shadow_GBuffer [[ texture(AAPLRenderTargetNormal) ]],
texture2d<float> depth_GBuffer [[ texture(AAPLRenderTargetDepth) ]])
iOS和tvOS渲染器從附加到渲染過程的渲染目標(biāo)中讀取G緩沖區(qū)數(shù)據(jù)。
struct GBufferData
{
half4 lighting [[color(AAPLRenderTargetLighting), raster_order_group(AAPLLightingROG)]];
half4 albedo_specular [[color(AAPLRenderTargetAlbedo), raster_order_group(AAPLGBufferROG)]];
half4 normal_shadow [[color(AAPLRenderTargetNormal), raster_order_group(AAPLGBufferROG)]];
float depth [[color(AAPLRenderTargetDepth), raster_order_group(AAPLGBufferROG)]];
};
fragment AccumLightBuffer
deferred_directional_lighting_fragment(QuadInOut in [[ stage_in ]],
constant AAPLUniforms & uniforms [[ buffer(AAPLBufferIndexUniforms) ]],
GBufferData GBuffer)
雖然這些片段函數(shù)具有不同的輸入,但它們在deferred_directional_lighting_fragment_common
片段函數(shù)中共享一個共同的實(shí)現(xiàn)。 此函數(shù)執(zhí)行以下操作:
- 從G緩沖區(qū)正常數(shù)據(jù)重建法線以計(jì)算擴(kuò)散項(xiàng)。
- 從G緩沖深度數(shù)據(jù)重建眼睛空間位置以應(yīng)用鏡面高光。
- 使用G緩沖區(qū)陰影數(shù)據(jù)使片段變暗并將陰影應(yīng)用于場景。
因?yàn)檫@是渲染到drawable
的第一個階段,iOS和tvOS渲染器在早期G緩沖階段之前獲得一個drawable
,以便drawable
可以與后面階段的輸出合并。 然而,macOS
渲染器延遲獲得可繪制直到G緩沖階段完成之后和定向光階段開始之前。 此延遲減少了應(yīng)用程序保留在drawable
上的時間,從而提高了性能。
注意:由于
_directionLightDepthStencilState
的狀態(tài),deferred_directional_lighting_fragment
函數(shù)僅對應(yīng)該點(diǎn)亮的片段執(zhí)行。 此優(yōu)化很簡單但很重要,并且可以節(jié)省許多片段著色器執(zhí)行周期。
Cull the Light Volumes
該示例創(chuàng)建了一個模板掩碼,用于避免對許多片段執(zhí)行昂貴的光照計(jì)算。 它通過使用來自G緩沖區(qū)傳遞的深度緩沖區(qū)和模板緩沖區(qū)來創(chuàng)建此模板蒙版,以跟蹤光量是否與任何幾何體相交。 (如果沒有,那么它就不會對任何東西施光。)
在drawPointLightMask:
實(shí)現(xiàn)中,該示例設(shè)置_lightMaskPipelineState
渲染管道并對實(shí)例化繪制調(diào)用進(jìn)行編碼,以僅繪制二十面體的背面,其包含點(diǎn)光源的體積。 如果此繪制調(diào)用中的片段未通過深度測試,則此結(jié)果表明二十面體的背面位于某些幾何體后面。
[renderEncoder setRenderPipelineState:_lightMaskPipelineState];
[renderEncoder setDepthStencilState:_lightMaskDepthStencilState];
[renderEncoder setStencilReferenceValue:128];
[renderEncoder setCullMode:MTLCullModeFront];
[renderEncoder setVertexBuffer:self.uniformBuffers[self.currentBufferIndex] offset:0 atIndex:AAPLBufferIndexUniforms];
[renderEncoder setFragmentBuffer:self.uniformBuffers[self.currentBufferIndex] offset:0 atIndex:AAPLBufferIndexUniforms];
[renderEncoder setVertexBuffer:self.lightsData offset:0 atIndex:AAPLBufferIndexLightsData];
[renderEncoder setVertexBuffer:self.lightPositions[self.currentBufferIndex] offset:0 atIndex:AAPLBufferIndexLightsPosition];
MTKMeshBuffer *vertexBuffer = self.icosahedronMesh.vertexBuffers[AAPLBufferIndexMeshPositions];
[renderEncoder setVertexBuffer:vertexBuffer.buffer offset:vertexBuffer.offset atIndex:AAPLBufferIndexMeshPositions];
MTKSubmesh *icosahedronSubmesh = self.icosahedronMesh.submeshes[0];
[renderEncoder drawIndexedPrimitives:icosahedronSubmesh.primitiveType
indexCount:icosahedronSubmesh.indexCount
indexType:icosahedronSubmesh.indexType
indexBuffer:icosahedronSubmesh.indexBuffer.buffer
indexBufferOffset:icosahedronSubmesh.indexBuffer.offset
instanceCount:AAPLNumLights];
_lightMaskPipelineState
沒有片段函數(shù),因此不會從此渲染管道中寫入顏色數(shù)據(jù)。但是,由于set _lightMaskDepthStencilState
深度和模板狀態(tài),任何未通過深度測試的片段都會增加該片段的模板緩沖區(qū)。包含幾何體的片段的起始深度值為128,樣本在G緩沖階段中設(shè)置。因此,設(shè)置_lightMaskDepthStencilState
時未通過深度測試的任何片段會將深度值增加到大于128。(因?yàn)檎嫣蕹褑⒂茫赐ㄟ^深度測試并且值大于128的片段表示至少返回二十面體的一半落后于所有幾何體。)
在下一個繪制調(diào)用中,在drawPointLightsCommon
實(shí)現(xiàn)中,該示例將點(diǎn)光源的貢獻(xiàn)應(yīng)用于drawable
。該示例測試二十面體的前半部分是否在所有幾何體的前面,這確定體積是否與某些幾何體相交,因此是否應(yīng)該點(diǎn)亮該片段。如果片段的模板值大于參考值128,則為此繪制調(diào)用設(shè)置的深度和模板狀態(tài)_pointLightDepthStencilState
僅執(zhí)行片段函數(shù)。(因?yàn)槟0鍦y試值設(shè)置為MTLCompareFunctionLess
,所以示例通過測試僅當(dāng)參考值128小于模板緩沖區(qū)中的值時。)
[renderEncoder setDepthStencilState:_pointLightDepthStencilState];
[renderEncoder setStencilReferenceValue:128];
[renderEncoder setCullMode:MTLCullModeBack];
[renderEncoder setVertexBuffer:self.uniformBuffers[self.currentBufferIndex] offset:0 atIndex:AAPLBufferIndexUniforms];
[renderEncoder setVertexBuffer:self.lightsData offset:0 atIndex:AAPLBufferIndexLightsData];
[renderEncoder setVertexBuffer:self.lightPositions[self.currentBufferIndex] offset:0 atIndex:AAPLBufferIndexLightsPosition];
[renderEncoder setFragmentBuffer:self.uniformBuffers[self.currentBufferIndex] offset:0 atIndex:AAPLBufferIndexUniforms];
[renderEncoder setFragmentBuffer:self.lightsData offset:0 atIndex:AAPLBufferIndexLightsData];
[renderEncoder setFragmentBuffer:self.lightPositions[self.currentBufferIndex] offset:0 atIndex:AAPLBufferIndexLightsPosition];
MTKMeshBuffer *vertexBuffer = self.icosahedronMesh.vertexBuffers[AAPLBufferIndexMeshPositions];
[renderEncoder setVertexBuffer:vertexBuffer.buffer offset:vertexBuffer.offset atIndex:AAPLBufferIndexMeshPositions];
MTKSubmesh *icosahedronSubmesh = self.icosahedronMesh.submeshes[0];
[renderEncoder drawIndexedPrimitives:icosahedronSubmesh.primitiveType
indexCount:icosahedronSubmesh.indexCount
indexType:icosahedronSubmesh.indexType
indexBuffer:icosahedronSubmesh.indexBuffer.buffer
indexBufferOffset:icosahedronSubmesh.indexBuffer.offset
instanceCount:AAPLNumLights];
因?yàn)?code>drawPointLightMask中的繪制調(diào)用會增加任何幾何體后面的片段的模板值,所以樣本執(zhí)行片段函數(shù)的唯一片段是滿足以下兩個條件的片段:
- 正面通過深度測試并位于某些幾何體前面的碎片。
- 背面未通過深度測試且位于某些幾何體后面的碎片。
下圖顯示了使用此模板掩碼算法的渲染幀與不使用此模板掩碼算法的渲染幀之間的片段覆蓋范圍的差異。 當(dāng)啟用算法時,綠色像素是執(zhí)行點(diǎn)光片段功能的像素。
當(dāng)禁用該算法時,綠色和紅色的像素是執(zhí)行點(diǎn)光片段功能的像素。
Render the Skybox and Fairy Lights - 渲染Skybox 和 Fairy Lights
在最后的照明階段,示例將更簡單的照明技術(shù)應(yīng)用于場景。
該示例將深度測試應(yīng)用于天空盒,與模型的幾何體相對應(yīng),因此渲染器僅渲染到未被某些幾何體填充的可繪制區(qū)域。
[renderEncoder setRenderPipelineState:_skyboxPipelineState];
[renderEncoder setDepthStencilState:_dontWriteDepthStencilState];
[renderEncoder setCullMode:MTLCullModeFront];
[renderEncoder setVertexBuffer:_uniformBuffers[_currentBufferIndex] offset:0 atIndex:AAPLBufferIndexUniforms];
[renderEncoder setFragmentTexture:_skyMap atIndex:AAPLTextureIndexBaseColor];
// Set mesh's vertex buffers
for (NSUInteger bufferIndex = 0; bufferIndex < _skyMesh.vertexBuffers.count; bufferIndex++)
{
__unsafe_unretained MTKMeshBuffer *vertexBuffer = _skyMesh.vertexBuffers[bufferIndex];
if((NSNull*)vertexBuffer != [NSNull null])
{
[renderEncoder setVertexBuffer:vertexBuffer.buffer
offset:vertexBuffer.offset
atIndex:bufferIndex];
}
}
MTKSubmesh *sphereSubmesh = _skyMesh.submeshes[0];
[renderEncoder drawIndexedPrimitives:sphereSubmesh.primitiveType
indexCount:sphereSubmesh.indexCount
indexType:sphereSubmesh.indexType
indexBuffer:sphereSubmesh.indexBuffer.buffer
indexBufferOffset:sphereSubmesh.indexBuffer.offset];
該示例將fairy lights
渲染到drawable
上作為2D圓圈,并使用紋理來確定其片段的alpha混合因子。
half4 c = colorMap.sample(linearSampler, float2(in.tex_coord));
half3 fragColor = in.color * c.x;
return half4(fragColor, c.x);
后記
本篇主要講述了延遲照明,感興趣的給個贊或者關(guān)注~~~