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

版本記錄

版本號 時間
V1.0 2018.10.10 星期三

前言

很多做視頻和圖像的楣责,相信對這個框架都不是很陌生鹃栽,它渲染高級3D圖形穿扳,并使用GPU執(zhí)行數(shù)據(jù)并行計算。接下來的幾篇我們就詳細的解析這個框架借尿。感興趣的看下面幾篇文章刨晴。
1. Metal框架詳細解析(一)—— 基本概覽
2. Metal框架詳細解析(二) —— 器件和命令(一)
3. Metal框架詳細解析(三) —— 渲染簡單的2D三角形(一)
4. Metal框架詳細解析(四) —— 關(guān)于GPU Family 4(一)
5. Metal框架詳細解析(五) —— 關(guān)于GPU Family 4之關(guān)于Imageblocks(二)
6. Metal框架詳細解析(六) —— 關(guān)于GPU Family 4之關(guān)于Tile Shading(三)
7. Metal框架詳細解析(七) —— 關(guān)于GPU Family 4之關(guān)于光柵順序組(四)
8. Metal框架詳細解析(八) —— 關(guān)于GPU Family 4之關(guān)于增強的MSAA和Imageblock采樣覆蓋控制(五)
9. Metal框架詳細解析(九) —— 關(guān)于GPU Family 4之關(guān)于線程組共享(六)
10. Metal框架詳細解析(十) —— 基本組件(一)
11. Metal框架詳細解析(十一) —— 基本組件之器件選擇 - 圖形渲染的器件選擇(二)
12. Metal框架詳細解析(十二) —— 基本組件之器件選擇 - 計算處理的設(shè)備選擇(三)
13. Metal框架詳細解析(十三) —— 計算處理(一)
14. Metal框架詳細解析(十四) —— 計算處理之你好,計算(二)
15. Metal框架詳細解析(十五) —— 計算處理之關(guān)于線程和線程組(三)
16. Metal框架詳細解析(十六) —— 計算處理之計算線程組和網(wǎng)格大新贩(四)
17. Metal框架詳細解析(十七) —— 工具狈癞、分析和調(diào)試(一)
18. Metal框架詳細解析(十八) —— 工具、分析和調(diào)試之Metal GPU Capture(二)
19. Metal框架詳細解析(十九) —— 工具茂契、分析和調(diào)試之GPU活動監(jiān)視器(三)
20. Metal框架詳細解析(二十) —— 工具蝶桶、分析和調(diào)試之關(guān)于Metal著色語言文件名擴展名、使用Metal的命令行工具構(gòu)建庫和標記Metal對象和命令(四)
21. Metal框架詳細解析(二十一) —— 基本課程之基本緩沖區(qū)(一)
22. Metal框架詳細解析(二十二) —— 基本課程之基本紋理(二)
23. Metal框架詳細解析(二十三) —— 基本課程之CPU和GPU同步(三)
24. Metal框架詳細解析(二十四) —— 基本課程之參數(shù)緩沖 - 基本參數(shù)緩沖(四)
25. Metal框架詳細解析(二十五) —— 基本課程之參數(shù)緩沖 - 帶有數(shù)組和資源堆的參數(shù)緩沖區(qū)(五)
26. Metal框架詳細解析(二十六) —— 基本課程之參數(shù)緩沖 - 具有GPU編碼的參數(shù)緩沖區(qū)(六)

Advanced Techniques - 高級技術(shù)

通過有效使用Metal功能掉冶,了解如何實現(xiàn)高級技術(shù)真竖。

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


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

演示如何使用圖層選擇來減少反射對象所需的渲染過程的數(shù)量获询。

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

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


Separate the Scene - 分離場景

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

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


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

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

在每個幀的開始處,對于每個立方體貼圖面玻蝌,計算視圖矩陣并且視圖的截錐體存儲在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測試actor和每個立方體貼圖面的視錐體之間的交叉點。 測試結(jié)果確定actor在反射過程中渲染到多少個面(instanceCount)俯树,以及它渲染到哪個面(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ù)它們相對于反射球的位置對actors進行的剔除測試的結(jié)果。 因為_actorData [0]actorData [1]將兩個視錐體平分许饿,所以它們的instanceCount屬性設(shè)置為2阳欲,并且它們的instanceParams數(shù)組中有兩個元素。 (此數(shù)組包含actors相交的視錐體的立方體貼圖面索引陋率。)


Configure Render Targets for the Reflection Pass - 配置Reflection Pass的渲染目標

反射過程的渲染目標是立方體貼圖球化。 該示例通過使用具有顏色渲染目標,深度渲染目標和六個圖層的MTLRenderPassDescriptor對象來配置渲染目標瓦糟。 renderTargetArrayLength屬性設(shè)置立方體貼圖面的數(shù)量筒愚,并允許渲染管道渲染到其中的任何一個或全部。

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

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

drawActors:pass:方法為每個actor設(shè)置圖形渲染狀態(tài)菩浙。 只有在六個立方體貼圖面中的任何一個中可見時巢掺,才會繪制Actor,由visibleVpCount值(通過instanceCountInReflection屬性訪問)確定劲蜻。 visibleVpCount的值確定實例化繪制調(diào)用的實例數(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è)置很重要倔约,因為它告訴頂點函數(shù)如何為每個實例選擇適當?shù)匿秩灸繕藞D層。


Render the Reflection Pass - 渲染反射Pass

vertexTransform頂點函數(shù)中坝初,instanceParams參數(shù)指向包含每個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) ]] )

頂點函數(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;

因為draw調(diào)用的baseInstance參數(shù)的值設(shè)置為actorIdx * 5吏恭,所以在draw調(diào)用中繪制的第一個實例的instanceId值等于此值。 實例的每個后續(xù)呈現(xiàn)都將instanceId值遞增1重罪。instanceParams數(shù)組為每個actor提供五個槽樱哼,因為actor最多可以在五個立方體貼圖面中可見。 因此剿配,instanceParams [instanceId]元素始終包含actor中可見的面部索引之一搅幅。 因此,該示例使用此值來選擇有效的渲染目標圖層呼胚。

out.face = instanceParams[instanceId].viewportIndex;

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


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

該示例在最后過程中對主攝像機執(zhí)行類似的視圖更新相嵌。 在每個幀的開始處,計算視圖矩陣克胳,并且視圖的平截頭體存儲在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);

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

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 - 配置最終過程的渲染目標

最終過程的渲染目標是視圖的drawable,它是通過訪問視圖的currentRenderPassDescriptor屬性獲得的可顯示資源跃赚。 但是笆搓,您不能過早訪問此屬性,因為它隱式檢索drawable纬傲。Drawable是由Core Animation框架創(chuàng)建和維護的昂貴系統(tǒng)資源满败。 你應(yīng)該盡可能短暫地持有一個drawable,以避免資源停滯叹括。 在此示例中算墨,在編碼最終渲染過程之前獲取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ā)出最終過程的繪制調(diào)用

drawActors:pass:方法為每個actor設(shè)置圖形渲染狀態(tài)车摄。 只有在主攝像機可見的情況下才會繪制Actor介时,由visibleVpCount值(通過visibleInFinal屬性訪問)確定椎咧。

因為每個actor在最后一次傳遞中只繪制一次,所以instanceCount參數(shù)始終設(shè)置為1挖藏,baseInstance參數(shù)始終設(shè)置為0暑刃。


Render the Final Pass - 渲染最終過程

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

[commandBuffer presentDrawable:view.currentDrawable];

后記

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市宵膨,隨后出現(xiàn)的幾起案子架谎,更是在濱河造成了極大的恐慌,老刑警劉巖辟躏,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狐树,死亡現(xiàn)場離奇詭異,居然都是意外死亡鸿脓,警方通過查閱死者的電腦和手機抑钟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來野哭,“玉大人在塔,你說我怎么就攤上這事〔η” “怎么了蛔溃?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長篱蝇。 經(jīng)常有香客問我贺待,道長,這世上最難降的妖魔是什么零截? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任麸塞,我火速辦了婚禮,結(jié)果婚禮上涧衙,老公的妹妹穿的比我還像新娘哪工。我一直安慰自己,他們只是感情好弧哎,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布雁比。 她就那樣靜靜地躺著,像睡著了一般撤嫩。 火紅的嫁衣襯著肌膚如雪偎捎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音茴她,去河邊找鬼寻拂。 笑死,一個胖子當著我的面吹牛败京,可吹牛的內(nèi)容都是我干的兜喻。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼赡麦,長吁一口氣:“原來是場噩夢啊……” “哼朴皆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起泛粹,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤遂铡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后晶姊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扒接,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年们衙,在試婚紗的時候發(fā)現(xiàn)自己被綠了钾怔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡蒙挑,死狀恐怖宗侦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忆蚀,我是刑警寧澤矾利,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站馋袜,受9級特大地震影響男旗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜欣鳖,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一察皇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧观堂,春花似錦让网、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽而账。三九已至胰坟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背笔横。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工竞滓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吹缔。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓商佑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親厢塘。 傳聞我的和親對象是個殘疾皇子茶没,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

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