[MetalKit]18-Using-MetalKit-part-12使用MetalKit12

蘋果API搬運工發表於2017-12-25

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

MetalKit系統文章目錄


讓我們從上次第11部分 Part 11開始,繼續處理我們美麗的分形.還用上次我們工作的playground,我們將看到如何將它變成活的,也就是變成動畫.為此,我們又要使用uniforms了.我們是在第5部分 Part 5介紹過,如果你想再讀一遍為什麼它們在本例中有用的話.

首先,在MetalView.swift檔案的頂部,讓我們建立一個全域性變數命名為timer並用一個資料緩衝器來儲存它:

var timer: Float = 0
var timerBuffer: MTLBuffer!
複製程式碼

下一步,在registerShaders()裡面,我們要初始化這個緩衝器:

timerBuffer = device!.newBufferWithLength(sizeof(Float), options: [])
複製程式碼

然後,我們需要建立一個update() 函式,它將增加計時時間併傳送新的值到緩衝器:

func update() {
    timer += 0.01
    var bufferPointer = timerBuffer.contents()
    memcpy(bufferPointer, &timer, sizeof(Float))
}
複製程式碼

下一步,在drawRect()中,就在我們設定紋理到當前drawable的那行下面,我們需要設定計時器緩衝器到索引1.然後我們還需要呼叫update函式,因為drawRect()每幀都執行,所以計時器時間就會隨著幀數變化而增加:

commandEncoder.setBuffer(timerBuffer, offset: 0, atIndex: 1)
update()
複製程式碼

然後,在Shaders.metal中我們需要更新我們的核心簽名來包含計時器緩衝器:

kernel void compute(texture2d<float, access::write> output [[texture(0)]],
                    constant float &timer [[buffer(1)]],
                    uint2 gid [[thread_position_in_grid]])
複製程式碼

最有意思的部分來了!將下面這行:

float2 cc = 1.1*float2( 0.5*cos(0.1) - 0.25*cos(0.2), 0.5*sin(0.1) - 0.25*sin(0.2) );
複製程式碼

替換為:

float2 cc = 1.1*float2( 0.5*cos(0.1*timer) - 0.25*cos(0.2*timer), 0.5*sin(0.1*timer) - 0.25*sin(0.2*timer) );
複製程式碼

如果你現在執行playground,你會看到類似的東西:

chapter12_1.gif

是不是簡單又有趣?還可以新增一個重要又有用的特性,就是滑鼠互動.顯然,我們又要用到uniforms了.我們讓MetalView類遵守NSWindowDelegate協議,這樣我們就可以用它的mouse方法了.

public class MetalView: MTKView, NSWindowDelegate {
複製程式碼

下一步,參考計時器,我們再建立一個全域性變數命名為pos,來記錄滑鼠位置(座標)並用一個資料緩衝器來儲存它.我們現在可以重寫mouseDown() 方法並拿到座標了:

var mouseBuffer: MTLBuffer!
var pos: NSPoint!

override public func mouseDown(event: NSEvent) {
    pos = convertPointToLayer(convertPoint(event.locationInWindow, fromView: nil))
    let scale = layer!.contentsScale
    pos.x *= scale
    pos.y *= scale
}
複製程式碼

正如你看到的那樣,我們在縮放座標,從整個螢幕到只有MetalView大小,同時我們用從檢視的層那裡拿到的縮放比例來更新座標.下一步,在registerShaders()函式中我們初始化滑鼠緩衝器:

mouseBuffer = device!.newBufferWithLength(sizeof(NSPoint), options: [])
複製程式碼

現在回到update()函式,新增下面幾行到末尾,這樣我們就能傳送當前滑鼠座標到緩衝器了:

bufferPointer = mouseBuffer.contents()
memcpy(bufferPointer, &pos, sizeof(NSPoint))
複製程式碼

下一步,在drawRect()中我們設定滑鼠緩衝器到索引2:

commandEncoder.setBuffer(mouseBuffer, offset: 0, atIndex: 2)
複製程式碼

然後,在Shaders.metal中我們再升級核心簽名來包含滑鼠緩衝器:

kernel void compute(texture2d<float, access::write> output [[texture(0)]],
                    constant float &timer [[buffer(1)]],
                    constant float2 &mouse [[buffer(2)]],
                    uint2 gid [[thread_position_in_grid]])
複製程式碼

最後,我們將下面這行:

float3 color = float3( dmin.w );
複製程式碼

替換為這行:

float3 color = float3(mouse.x - mouse.y);
複製程式碼

我們這裡做的是改變顏色的計算方式,改用滑鼠座標傳遞到color變數.在playground中執行,並單擊可變檢視區,觀察效果.輸出的影像應該看起來像這樣:

chapter12_2.gif

在程式碼的不同位置引入滑鼠座標,可以使核心程式碼實現更漂亮的效果.還有一個需要關注的問題,事實上NSPoint可能並不完全對應核心中的float2型別.

原始碼source code 已釋出在Github上.

下次見!

相關文章