[MetalKit]3-Using-MetalKit-part-2使用MetalKit2

本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.c++

MetalKit系統文章目錄git


在本系列的第一部分中咱們介紹了MetalKit框架.讓咱們回到part1的項目中並繼續.再看一遍render() 函數,他應該看起來這樣:程序員

func render() {
    let device = MTLCreateSystemDefaultDevice()!
    self.device = device
    let rpd = MTLRenderPassDescriptor()
    let bleen = MTLClearColor(red: 0, green: 0.5, blue: 0.5, alpha: 1)
    rpd.colorAttachments[0].texture = currentDrawable!.texture
    rpd.colorAttachments[0].clearColor = bleen
    rpd.colorAttachments[0].loadAction = .Clear
    let commandQueue = device.newCommandQueue()
    let commandBuffer = commandQueue.commandBuffer()
    let encoder = commandBuffer.renderCommandEncoderWithDescriptor(rpd)
    encoder.endEncoding()
    commandBuffer.presentDrawable(currentDrawable!)
    commandBuffer.commit()
}
複製代碼

讓咱們改進一下這段代碼.首先,既然咱們的類已是MTKView的子類,它就已經有了本身的device,因此沒有必要再聲明一個.這就能夠把頭兩行變成一行:github

device = MTLCreateSystemDefaultDevice()
複製代碼

第二步,上週咱們說到咱們應該確保currentDrawablecurrentRenderPassDescriptor不爲空不然應用會崩潰.爲了簡單點,咱們在第1部分時沒作,因此如今來添加一下.這將讓咱們能再減小几行代碼.最終版看起來會像這樣:swift

func render() {
    device = MTLCreateSystemDefaultDevice()
    if let rpd = currentRenderPassDescriptor, drawable = currentDrawable {
        rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0.5, 0.5, 1.0)
        let command_buffer = device!.newCommandQueue().commandBuffer()
        let command_encoder = command_buffer.renderCommandEncoderWithDescriptor(rpd)
        command_encoder.endEncoding()
        command_buffer.presentDrawable(drawable)
        command_buffer.commit()
    }
}
複製代碼

你可能會好奇colorAttachments[0] 是什麼.爲了設置rendering pipeline state渲染管線狀態,Metal框架提供了3種類型的附件,來讓咱們寫入:數組

  • colorAttachments顏色附件
  • depthAttachmentPixelFormat像素格式的深度附件
  • stencilAttachmentPixelFormat像素格式的模板附件 咱們目前只關心如何儲存顏色數據,colorAttachments是一個紋理數組,裏面包含了繪製結果並將他們展現到屏幕上.咱們目前只有一個這樣的紋理-數組中的第一個元素(數組下標爲0). OK,如今是時候運行程序了,確保你仍然能看到像上次同樣背景顏色.很棒!只用9行代碼咱們就建立了一個安全運行在GPU上的Metal代碼.

接下來,讓咱們深刻研究一個新的Metal話題-在屏幕上繪製幾何體.全部的圖形學教程好比和OpenGL相關的都會以Hello,Triangle類型程序開始,由於三角形是能繪製在屏幕上幾何體中最簡單的一個.它是2D圖形學基本元素,圖形學中其餘全部對象都是三角形組成的,因此它是個很好的入門切入點.想象屏幕座標系統擁有本身的貫穿屏幕中心的座標軸,中心點座標爲 (0,0).相應的屏幕邊緣應該爲 -11 .讓咱們建立一組浮點數和一個緩衝器來保存三角形的頂點.在初始化device後插入下面幾行代碼:安全

let vertex_data:[Float] = [-1.0, -1.0, 0.0, 1.0,
                            1.0, -1.0, 0.0, 1.0,
                            0.0,  1.0, 0.0, 1.0]
let data_size = vertex_data.count * sizeof(Float)
let vertex_buffer = device!.newBufferWithBytes(vertex_data, length: data_size, options: [])
複製代碼

上面的頂點數據是按順序排列的:左下,右下,中上.咱們注意到每一個頂點使用4個浮點數來表示座標.前兩個是xy軸.本次用不到的浮點數是:第三個深度(z軸)和第四個w座標用來使座標系齊次化.咱們將在下一節討論他們.而後咱們計算這個數組的size大小爲12個浮點數長度,最後咱們用數組及其長度來建立一個緩衝器.如今咱們已經儲存好了咱們的頂點,還須要找個辦法將他們發送到GPU以便能在屏幕上顯示他們.讓咱們看看繪製到屏幕的整個處理過程(即管線):框架

chapter03_1.png

咱們目前已經完成了第1階段,儲存頂點.下一步要求咱們有一個新的構件稱爲shader着色器.一個shader就是程序員可以用自定義函數來干預圖形管線的地方.Metal提供了幾種類型的着色器,但今天咱們只看其中兩種:vertex shader頂點着色器負責點的位置,fragment shader片斷着色器負責點的顏色.函數

Metal框架提供了一個函數,咱們能夠在device中調用,來建立一個函數(shader)組成的Library庫,以下:post

let library = device!.newDefaultLibrary()!
let vertex_func = library.newFunctionWithName("vertex_func")
let frag_func = library.newFunctionWithName("fragment_func")
複製代碼

咱們建立兩個新的函數,將其指向對應的着色器(稍後會建立).下一步是建立一個Render Pipeline Descriptor渲染管線描述符來使用着色器:

let rpld = MTLRenderPipelineDescriptor()
rpld.vertexFunction = vertex_func
rpld.fragmentFunction = frag_func
rpld.colorAttachments[0].pixelFormat = .BGRA8Unorm
複製代碼

你可能會好奇 .BGRA8Unorm是什麼意思.它設置了像素格式,因此渲染管線中出來的全部東西的顏色組件都會是同一順序(本例中按Blue,Green,Red,Alpha順序),同時大小也會一致(本例中是8-bit的顏色值,範圍從0255).最後一步是根據上面的descriptor描述符建立一個Render Pipeline State渲染管線狀態:

let rps = try! device!.newRenderPipelineStateWithDescriptor(rpld)
複製代碼

最後,咱們只須要讓命令編碼器獲取到咱們的三角形就能夠了,因此添加下面幾行代碼到建立encoder編碼器以後:

command_encoder.setRenderPipelineState(rps)
command_encoder.setVertexBuffer(vertex_buffer, offset: 0, atIndex: 0)
command_encoder.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: 3, instanceCount: 1) 
複製代碼

如今咱們回到兩個shaders那裏,咱們在建立Library庫時說過須要建立的.爲了建立shader,咱們須要建立一個Xcode中的新文件.選擇Metal File類型,命名爲Shaders.metal或者其餘相似名字,單擊Create.你將看到代碼彷佛不是Swift的,由於Metal shading language着色語言實際上是基於C++的.添加下面的代碼:

#include <metal_stdlib>

using namespace metal;

struct Vertex {
    float4 position [[position]];
};

vertex Vertex vertex_func(constant Vertex *vertices [[buffer(0)]], uint vid [[vertex_id]]) {
    return vertices[vid];
}

fragment float4 fragment_func(Vertex vert [[stage_in]]) {
    return float4(0.7, 1, 1, 1);
}
複製代碼

代碼至關直白.咱們首先建立一個struct結構體命名爲Vertex,裏面只有一個成員-一個包含位置數組的數組.咱們注意到數組是float4類型,在着色語言中該類型和咱們前面建立頂點時的4個浮點數是同樣的.咱們如今先不解釋 [[...]] 語法的意思.而後咱們看到vertex_func着色器返回當前頂點的location位置,fragment_func着色器返回當前頂點的color顏色.咱們硬編碼了一個特殊的顏色值,可是咱們能夠將color結構體成員添加到Vertex上,這樣將每一個頂點設置不一樣的顏色. 若是你運行應用,將會看到像這樣的三角形:

chapter03_2.png

下一部分咱們將學習Metal shading language也就是3D圖形是怎樣渲染到GPUs的.源代碼source code 已發佈在Github上.

下次見!

相關文章
相關標籤/搜索