創(chuàng)建一個(gè)MetalKit視圖和一個(gè)渲染過程以繪制視圖的內(nèi)容。
縱覽
在這個(gè)示例中,你將學(xué)習(xí)使用Metal 渲染圖形內(nèi)容的基礎(chǔ)知識(shí)。你將使用MetalKit 框架創(chuàng)建一個(gè)視圖并用Metal 來繪制視圖的內(nèi)容。然后,你將編碼為視圖設(shè)置背景顏色的渲染過程中的命令。
注意
MetalKit自動(dòng)執(zhí)行窗口系統(tǒng)任務(wù),加載紋理并處理3D模型數(shù)據(jù)。閱讀MetalKit來了解更多信息。
準(zhǔn)備要繪制的MetalKit 視圖
metalKit 提供一個(gè)叫MTKView的類, 它是NSView(在macOS中)或者UIView(在iOS 和 tvOS中)的子類,MTKView處理許多與將用Metal繪制的內(nèi)容顯示在屏幕上相關(guān)的細(xì)節(jié)。
為了在內(nèi)部創(chuàng)建資源,MTKView需要對(duì)Metal設(shè)備對(duì)象的引用,因此,第一步是將視圖的device屬性設(shè)置為現(xiàn)有的MTLDevice。
_view.device = MTLCreateSystemDefaultDevice();
MTKView上的其他屬性允許你控制其行為,將視圖內(nèi)容擦除為純背景色,設(shè)置它的clearColor屬性。通過MTLClearColorMake()函數(shù)創(chuàng)建顏色,指定紅色、綠色、藍(lán)色和alpha值。
_view.clearColor = MTLClearColorMake(0.0, 0.5, 1.0, 1.0);
因?yàn)樵谶@個(gè)示例中不會(huì)繪制動(dòng)畫內(nèi)容,所以配置視圖,使其僅在需要更新內(nèi)容時(shí)繪制。
_view.enableSetNeedsDisplay = YES;
代理繪圖任務(wù)
MTKView依賴于您的應(yīng)用程序向Metal發(fā)出命令以生成可視內(nèi)容。MTKView使用代理模式通知應(yīng)用程序何時(shí)應(yīng)該繪制。為了接受代理的回調(diào),將視圖的delegate屬性設(shè)置為符合MTKViewDelegate協(xié)議的對(duì)象。
_view.delegate = _renderer;
代理實(shí)現(xiàn)了兩個(gè)方法:
1.每當(dāng)內(nèi)容大小發(fā)生變化時(shí),視圖將調(diào)用mtkView:drawableSizeWillChange:方法,這種變化在包含視圖的窗口調(diào)整大小時(shí),或者當(dāng)設(shè)備方向發(fā)生變化時(shí)(在iOS上)發(fā)生。這允許應(yīng)用程序根據(jù)視圖的大小調(diào)整渲染時(shí)的分辨率。
2.每當(dāng)?shù)剿⑿乱晥D內(nèi)容的時(shí)間時(shí),視圖會(huì)調(diào)用drawInMTKView:方法。在這個(gè)方法中創(chuàng)建一個(gè)命令緩沖區(qū),編碼命令告訴GPU畫什么和什么時(shí)候現(xiàn)在在屏幕上,然后在將要由GPU執(zhí)行的命令緩沖區(qū)中排隊(duì)。這個(gè)有時(shí)候稱為畫框。你可以把一個(gè)框架看作是產(chǎn)生一個(gè)在屏幕上顯示的單一圖像的所有工作。在一個(gè)交互式App中,比如游戲, 你可能每秒鐘畫很多框架。
在這個(gè)示例中,一個(gè)叫AAPLRenderer的類實(shí)現(xiàn)了代理方法并承擔(dān)繪圖的責(zé)任。視圖控制器創(chuàng)建這個(gè)類的一個(gè)實(shí)例并把它設(shè)置為視圖的代理。
創(chuàng)建渲染過程描述符
當(dāng)你繪圖,GPU會(huì)把結(jié)果存儲(chǔ)到紋理中,紋理是包含圖像數(shù)據(jù)的內(nèi)存塊,可被GPU訪問。在本示例中,MTKView將創(chuàng)建需要繪制到視圖中的所有紋理。它創(chuàng)建多個(gè)紋理,以便在渲染到另一個(gè)紋理時(shí)顯示一個(gè)紋理的內(nèi)容。
要進(jìn)行繪制,請(qǐng)創(chuàng)建一個(gè)渲染過程,該過程是一系列渲染命令,這些命令繪制成一組紋理。在渲染過程中使用時(shí),紋理也被稱為渲染目標(biāo)。若要?jiǎng)?chuàng)建渲染過程,需要一個(gè)渲染過程描述符,即MTLRenderPassDescriptor的實(shí)例。在這個(gè)示例中,與其配置自己的渲染過程描述符,不如讓MetalKit 給你創(chuàng)建一個(gè)。
MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
if (renderPassDescriptor == nil)
{
return;
}
一個(gè)渲染過程描述符描述一組渲染目標(biāo),以及在渲染過程開始和結(jié)束時(shí)應(yīng)如何處理這些目標(biāo)。渲染過程還定義了渲染的某些其他方面,這些方面不是此例的一部分。該視圖返回一個(gè)渲染過程描述符,該描述符具有指向該視圖的某個(gè)紋理的單色附件,并根據(jù)該視圖的屬性配置渲染過程。默認(rèn)情況下,這意味著渲染過程開始時(shí),渲染目標(biāo)將被擦除為純色來匹配視圖的clearColor屬性,并且在渲染過程結(jié)束時(shí)所有更改都存儲(chǔ)回紋理。
一個(gè)一個(gè)視圖的渲染過程描述符可能為nil, 在創(chuàng)建渲染過程之前, 應(yīng)該確保渲染過程描述符對(duì)象不為nil。
創(chuàng)建一個(gè)渲染過程
通過使用 MTLRenderCommandEncoder對(duì)象將其編碼到命令緩沖區(qū)來創(chuàng)建渲染過程。調(diào)用命令緩沖區(qū)的render>CommandEncoderWithDescriptor:方法并在渲染過程描述符中傳遞。
id<MTLRenderCommandEncoder> commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
在本示例中,您不編碼任何繪圖命令,因此渲染過程只會(huì)刪除紋理。調(diào)用編碼器的endEncoding方法以指示該過程已完成。
[commandEncoder endEncoding];
在屏幕上呈現(xiàn)一幅圖畫
繪制到紋理不會(huì)自動(dòng)在屏幕上顯示新內(nèi)容。實(shí)際上, 只有一些紋理可以顯示在屏幕上。Metal中,可以在屏幕上顯示的紋理由drawable對(duì)象管理,要顯示內(nèi)容,請(qǐng)顯示可繪制對(duì)象。
MTKView自動(dòng)創(chuàng)建可繪制對(duì)象來管理它的紋理,讀取currentDrawable
屬性以獲取擁有作為渲染過程目標(biāo)的紋理的drawable。視圖返回一個(gè)CAMetalDrawable
對(duì)象,一個(gè)連接到核心動(dòng)畫的對(duì)象。
id<MTLDrawable> drawable = view.currentDrawable;
在命令緩沖區(qū)中調(diào)用presentDrawable:方法,傳遞drawable。
[commandBuffer presentDrawable:drawable];
此方法告訴Metal,當(dāng)計(jì)劃執(zhí)行命令緩沖區(qū)時(shí),Metal應(yīng)與核心動(dòng)畫協(xié)調(diào)以在渲染完成后顯示紋理。當(dāng)核心動(dòng)畫呈現(xiàn)紋理時(shí),它將成為視圖的新內(nèi)容。在本示例中,這意味著刪除的紋理將成為視圖的新背景。這種變化與核心動(dòng)畫為屏幕用戶界面元素所做的任何其他視覺更新一起發(fā)生。
提交命令緩沖區(qū)
現(xiàn)在已經(jīng)發(fā)出了幀的所有命令,提交命令緩沖區(qū)。
[commandBuffer commit];