本系列文章是對 metalkit.org 上面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,你會看到類似的東西:
是不是簡單又有趣?還可以新增一個重要又有用的特性,就是滑鼠互動.顯然,我們又要用到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中執行,並單擊可變檢視區,觀察效果.輸出的影像應該看起來像這樣:
在程式碼的不同位置引入滑鼠座標,可以使核心程式碼實現更漂亮的效果.還有一個需要關注的問題,事實上NSPoint
可能並不完全對應核心中的float2
型別.
原始碼source code 已釋出在Github上.
下次見!