本系列文章是对 http://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 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 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上.
下次见!