Metal框架詳細(xì)解析(二十七) —— 高級(jí)技術(shù)之圖層選擇的反射(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2018.10.10 星期三

前言

很多做視頻和圖像的,相信對(duì)這個(gè)框架都不是很陌生,它渲染高級(jí)3D圖形,并使用GPU執(zhí)行數(shù)據(jù)并行計(jì)算。接下來(lái)的幾篇我們就詳細(xì)的解析這個(gè)框架。感興趣的看下面幾篇文章。
1. Metal框架詳細(xì)解析(一)—— 基本概覽
2. Metal框架詳細(xì)解析(二) —— 器件和命令(一)
3. Metal框架詳細(xì)解析(三) —— 渲染簡(jiǎn)單的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活動(dòng)監(jiān)視器(三)
20. Metal框架詳細(xì)解析(二十) —— 工具、分析和調(diào)試之關(guān)于Metal著色語(yǔ)言文件名擴(kuò)展名、使用Metal的命令行工具構(gòu)建庫(kù)和標(biāo)記Metal對(duì)象和命令(四)
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ū)(六)

Advanced Techniques - 高級(jí)技術(shù)

通過(guò)有效使用Metal功能,了解如何實(shí)現(xiàn)高級(jí)技術(shù)。

以下示例代碼項(xiàng)目演示了各種圖形和計(jì)算技術(shù),這些技術(shù)經(jīng)過(guò)專門(mén)優(yōu)化以利用Metal功能。


Reflections with Layer Selection - 圖層選擇的反射

演示如何使用圖層選擇來(lái)減少反射對(duì)象所需的渲染過(guò)程的數(shù)量。

此示例演示了鉻球上的動(dòng)態(tài)反射,使用圖層選擇以兩次渲染幀。 第一遍將環(huán)境渲染到立方體貼圖上。 第二遍將環(huán)境反射渲染到球體上;它渲染場(chǎng)景中的其他actors;它呈現(xiàn)環(huán)境本身。

您可以通過(guò)從環(huán)境的立方體貼圖中對(duì)其反射進(jìn)行采樣來(lái)實(shí)現(xiàn)反映其環(huán)境的對(duì)象。 立方體貼圖是由以立方體形狀排列的六個(gè)2D紋理層組成的單個(gè)紋理。 反射根據(jù)環(huán)境中其他對(duì)象的位置而變化,因此每個(gè)立方體貼圖的六個(gè)面必須在每個(gè)幀中動(dòng)態(tài)渲染。 這通常需要六個(gè)單獨(dú)的渲染過(guò)程,每個(gè)面一個(gè),但Metal允許您在一次過(guò)程中渲染整個(gè)立方體貼圖。


Separate the Scene - 分離場(chǎng)景

立方體貼圖表示為具有六個(gè)圖層的渲染目標(biāo)數(shù)組,每個(gè)圖層對(duì)應(yīng)一個(gè)面。 為頂點(diǎn)函數(shù)返回值的結(jié)構(gòu)成員指定的[[render_target_array_index]]屬性限定符分別標(biāo)識(shí)每個(gè)數(shù)組層。 此圖層選擇功能允許樣本決定將環(huán)境的哪個(gè)部分渲染到哪個(gè)立方體貼圖面。

AAPLActorData對(duì)象表示場(chǎng)景中的actor。 在此示例中,每個(gè)actor都是具有相同網(wǎng)格數(shù)據(jù)但具有不同漫反射顏色的模型。 這些actor坐在XZ平面上;它們總是相對(duì)于球體在X或Z方向上反射,并且可以渲染到立方體貼圖的任何+ X,-X,+ Z或-Z面。


Perform Culling Tests for the Reflection Pass - 執(zhí)行反射通過(guò)的剔除測(cè)試

在渲染到立方體貼圖之前,了解應(yīng)該渲染每個(gè)actor的面是很有用的。 確定此信息涉及稱為剔除測(cè)試的過(guò)程,并且針對(duì)每個(gè)立方體貼圖面對(duì)每個(gè)actor執(zhí)行該過(guò)程。

在每個(gè)幀的開(kāi)始處,對(duì)于每個(gè)立方體貼圖面,計(jì)算視圖矩陣并且視圖的截錐體存儲(chǔ)在culler_probe數(shù)組中。

// 1) Get the view matrix for the face given the sphere's updated position
viewMatrix[i] = _cameraReflection.GetViewMatrixForFace_LH (i);

// 2) Calculate the planes bounding the frustum using the updated view matrix
//    You use these planes later to test whether an actor's bounding sphere
//    intersects with the frustum, and is therefore visible in this face's viewport
culler_probe[i].Reset_LH (viewMatrix [i], _cameraReflection);

這些culler probes測(cè)試actor和每個(gè)立方體貼圖面的視錐體之間的交叉點(diǎn)。 測(cè)試結(jié)果確定actor在反射過(guò)程中渲染到多少個(gè)面(instanceCount),以及它渲染到哪個(gè)面(instanceParams)

if (_actorData[actorIdx].passFlags & EPassFlags::Reflection)
{
    int instanceCount = 0;
    for (int faceIdx = 0; faceIdx < 6; faceIdx++)
    {
        // Check if the actor is visible in the current probe face
        if (culler_probe [faceIdx].Intersects (_actorData[actorIdx].modelPosition.xyz, _actorData[actorIdx].bSphere))
        {
            // Add this face index to the the list of faces for this actor
            InstanceParams instanceParams = {(ushort)faceIdx};
            instanceParams_reflection [MaxVisibleFaces * actorIdx + instanceCount].viewportIndex = instanceParams.viewportIndex;
            instanceCount++;
        }
    }
    _actorData[actorIdx].instanceCountInReflection = instanceCount;
}

下圖顯示了根據(jù)它們相對(duì)于反射球的位置對(duì)actors進(jìn)行的剔除測(cè)試的結(jié)果。 因?yàn)?code>_actorData [0]和actorData [1]將兩個(gè)視錐體平分,所以它們的instanceCount屬性設(shè)置為2,并且它們的instanceParams數(shù)組中有兩個(gè)元素。 (此數(shù)組包含actors相交的視錐體的立方體貼圖面索引。)


Configure Render Targets for the Reflection Pass - 配置Reflection Pass的渲染目標(biāo)

反射過(guò)程的渲染目標(biāo)是立方體貼圖。 該示例通過(guò)使用具有顏色渲染目標(biāo),深度渲染目標(biāo)和六個(gè)圖層的MTLRenderPassDescriptor對(duì)象來(lái)配置渲染目標(biāo)。 renderTargetArrayLength屬性設(shè)置立方體貼圖面的數(shù)量,并允許渲染管道渲染到其中的任何一個(gè)或全部。

reflectionPassDesc.colorAttachments[0].texture    = _reflectionCubeMap;
reflectionPassDesc.depthAttachment.texture        = _reflectionCubeMapDepth;
reflectionPassDesc.renderTargetArrayLength        = 6;

Issue Draw Calls for the Reflection Pass - 發(fā)出反射過(guò)程的繪制調(diào)用

drawActors:pass:方法為每個(gè)actor設(shè)置圖形渲染狀態(tài)。 只有在六個(gè)立方體貼圖面中的任何一個(gè)中可見(jiàn)時(shí),才會(huì)繪制Actor,由visibleVpCount值(通過(guò)instanceCountInReflection屬性訪問(wèn))確定。 visibleVpCount的值確定實(shí)例化繪制調(diào)用的實(shí)例數(shù)。

[renderEncoder drawIndexedPrimitives: metalKitSubmesh.primitiveType
                          indexCount: metalKitSubmesh.indexCount
                           indexType: metalKitSubmesh.indexType
                         indexBuffer: metalKitSubmesh.indexBuffer.buffer
                   indexBufferOffset: metalKitSubmesh.indexBuffer.offset
                       instanceCount: visibleVpCount
                          baseVertex: 0
                        baseInstance: actorIdx * MaxVisibleFaces];

在此繪制調(diào)用中,示例將baseInstance參數(shù)設(shè)置為actorIdx * 5的值。此設(shè)置很重要,因?yàn)樗嬖V頂點(diǎn)函數(shù)如何為每個(gè)實(shí)例選擇適當(dāng)?shù)匿秩灸繕?biāo)圖層。


Render the Reflection Pass - 渲染反射Pass

vertexTransform頂點(diǎn)函數(shù)中,instanceParams參數(shù)指向包含每個(gè)actor應(yīng)渲染到的立方體貼圖面的緩沖區(qū)。 instanceId值索引到instanceParams數(shù)組中。

vertex ColorInOut vertexTransform (         Vertex          in               [[ stage_in ]],
                                            uint            instanceId       [[ instance_id ]],
                                   device   InstanceParams* instanceParams   [[ buffer (BufferIndexInstanceParams) ]],
                                   device   ActorParams&    actorParams      [[ buffer (BufferIndexActorParams)    ]],
                                   constant ViewportParams* viewportParams   [[ buffer (BufferIndexViewportParams) ]] )

頂點(diǎn)函數(shù)ColorInOut的輸出結(jié)構(gòu)包含使用[[render_target_array_index]]屬性限定符的face成員。 face的返回值確定渲染管道應(yīng)渲染到的立方體貼圖面。

typedef struct
{
    float4 position [[position]];
    float2 texCoord;

    half3  worldPos;
    half3  tangent;
    half3  bitangent;
    half3  normal;
    uint   face [[render_target_array_index]];
} ColorInOut;

因?yàn)?code>draw調(diào)用的baseInstance參數(shù)的值設(shè)置為actorIdx * 5,所以在draw調(diào)用中繪制的第一個(gè)實(shí)例的instanceId值等于此值。 實(shí)例的每個(gè)后續(xù)呈現(xiàn)都將instanceId值遞增1。instanceParams數(shù)組為每個(gè)actor提供五個(gè)槽,因?yàn)閍ctor最多可以在五個(gè)立方體貼圖面中可見(jiàn)。 因此,instanceParams [instanceId]元素始終包含actor中可見(jiàn)的面部索引之一。 因此,該示例使用此值來(lái)選擇有效的渲染目標(biāo)圖層。

out.face = instanceParams[instanceId].viewportIndex;

總之,為了將每個(gè)actor渲染到反射立方體貼圖,該示例為actor發(fā)出一個(gè)實(shí)例化繪制調(diào)用。 頂點(diǎn)函數(shù)使用內(nèi)置的instanceId變量來(lái)索引instanceParams數(shù)組,該數(shù)組包含應(yīng)該渲染實(shí)例的立方體貼圖面的索引。 因此,頂點(diǎn)函數(shù)在面返回值成員中設(shè)置此面的索引,該成員使用[[render_target_array_index]]屬性限定符。 這可確保每個(gè)actor都呈現(xiàn)給它應(yīng)該出現(xiàn)的每個(gè)立方體貼圖面。


Perform Culling Tests for the Final Pass - 執(zhí)行最終過(guò)程的剔除測(cè)試

該示例在最后過(guò)程中對(duì)主攝像機(jī)執(zhí)行類似的視圖更新。 在每個(gè)幀的開(kāi)始處,計(jì)算視圖矩陣,并且視圖的平截頭體存儲(chǔ)在culler_final變量中。

_cameraFinal.target   = SceneCenter;

_cameraFinal.rotation = fmod ((_cameraFinal.rotation + CameraRotationSpeed), M_PI*2.f);
matrix_float3x3 rotationMatrix = matrix3x3_rotation (_cameraFinal.rotation,  CameraRotationAxis);

_cameraFinal.position = SceneCenter;
_cameraFinal.position += matrix_multiply (rotationMatrix, CameraDistanceFromCenter);

const matrix_float4x4 viewMatrix       = _cameraFinal.GetViewMatrix();
const matrix_float4x4 projectionMatrix = _cameraFinal.GetProjectionMatrix_LH();

culler_final.Reset_LH (viewMatrix, _cameraFinal);

ViewportParams *viewportBuffer = (ViewportParams *)_viewportsParamsBuffers_final[_uniformBufferIndex].contents;
viewportBuffer[0].cameraPos            = _cameraFinal.position;
viewportBuffer[0].viewProjectionMatrix = matrix_multiply (projectionMatrix, viewMatrix);

這個(gè)最終的culler probe用于測(cè)試actor和攝像機(jī)的視錐體之間的交叉點(diǎn)。 測(cè)試結(jié)果僅確定每個(gè)actor在最后一遍中是否可見(jiàn)。

if (culler_final.Intersects (_actorData[actorIdx].modelPosition.xyz, _actorData[actorIdx].bSphere))
{
    _actorData[actorIdx].visibleInFinal = YES;
}
else
{
    _actorData[actorIdx].visibleInFinal = NO;
}

Configure Render Targets for the Final Pass - 配置最終過(guò)程的渲染目標(biāo)

最終過(guò)程的渲染目標(biāo)是視圖的drawable,它是通過(guò)訪問(wèn)視圖的currentRenderPassDescriptor屬性獲得的可顯示資源。 但是,您不能過(guò)早訪問(wèn)此屬性,因?yàn)樗[式檢索drawableDrawable是由Core Animation框架創(chuàng)建和維護(hù)的昂貴系統(tǒng)資源。 你應(yīng)該盡可能短暫地持有一個(gè)drawable,以避免資源停滯。 在此示例中,在編碼最終渲染過(guò)程之前獲取drawable

MTLRenderPassDescriptor* finalPassDescriptor = view.currentRenderPassDescriptor;

finalPassDescriptor.renderTargetArrayLength = 1;
id<MTLRenderCommandEncoder> renderEncoder =
    [commandBuffer renderCommandEncoderWithDescriptor:finalPassDescriptor];
renderEncoder.label = @"FinalPass";

[self drawActors: renderEncoder pass: EPassFlags::Final];

[renderEncoder endEncoding];

Issue Draw Calls for the Final Pass - 發(fā)出最終過(guò)程的繪制調(diào)用

drawActors:pass:方法為每個(gè)actor設(shè)置圖形渲染狀態(tài)。 只有在主攝像機(jī)可見(jiàn)的情況下才會(huì)繪制Actor,由visibleVpCount值(通過(guò)visibleInFinal屬性訪問(wèn))確定。

因?yàn)槊總€(gè)actor在最后一次傳遞中只繪制一次,所以instanceCount參數(shù)始終設(shè)置為1,baseInstance參數(shù)始終設(shè)置為0。


Render the Final Pass - 渲染最終過(guò)程

最后過(guò)程將最終幀直接渲染到視圖的drawable,然后在屏幕上顯示。

[commandBuffer presentDrawable:view.currentDrawable];

后記

本篇主要講述了圖層選擇的反射,感興趣的給個(gè)贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容