diff --git a/README.md b/README.md index 4104d77..4bf80c5 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,5 @@ Repository to accompany the following blog posts: - [Using MetalKit part 10](http://mhorga.org/2016/05/02/using-metalkit-part-10.html) - [Using MetalKit part 11](http://mhorga.org/2016/05/10/using-metalkit-part-11.html) - [Using MetalKit part 12](http://mhorga.org/2016/05/18/using-metalkit-part-12.html) -- [Using MetalKit part 13](http://mhorga.org/2016/05/25/using-metalkit-part-13.html) \ No newline at end of file +- [Using MetalKit part 13](http://mhorga.org/2016/05/25/using-metalkit-part-13.html) +- [Using MetalKit part 14](http://mhorga.org/2016/06/01/using-metalkit-part-14.html) \ No newline at end of file diff --git a/ch14/chapter14.playground/Contents.swift b/ch14/chapter14.playground/Contents.swift new file mode 100644 index 0000000..366d424 --- /dev/null +++ b/ch14/chapter14.playground/Contents.swift @@ -0,0 +1,8 @@ + +import Cocoa +import XCPlayground + +let device = MTLCreateSystemDefaultDevice()! +let frame = NSRect(x:0, y:0, width:400, height:400) +let view = MetalView(frame: frame, device: device) +XCPlaygroundPage.currentPage.liveView = view diff --git a/ch14/chapter14.playground/Resources/Shaders.metal b/ch14/chapter14.playground/Resources/Shaders.metal new file mode 100755 index 0000000..946c689 --- /dev/null +++ b/ch14/chapter14.playground/Resources/Shaders.metal @@ -0,0 +1,49 @@ + +#include + +using namespace metal; + +float random(float2 p) +{ + return fract(sin(dot(p, float2(15.79, 81.93)) * 45678.9123)); +} + +float noise(float2 p) +{ + float2 i = floor(p); + float2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + float bottom = mix(random(i + float2(0)), random(i + float2(1.0, 0.0)), f.x); + float top = mix(random(i + float2(0.0, 1.0)), random(i + float2(1)), f.x); + float t = mix(bottom, top, f.y); + return t; +} + +float fbm(float2 uv) +{ + float sum = 0; + float amp = 0.7; + for(int i = 0; i < 4; ++i) + { + sum += noise(uv) * amp; + uv += uv * 1.2; + amp *= 0.4; + } + return sum; +} + +kernel void compute(texture2d output [[texture(0)]], + constant float2 &mouse [[buffer(0)]], + constant float &timer [[buffer(1)]], + uint2 gid [[thread_position_in_grid]]) +{ + int width = output.get_width(); + int height = output.get_height(); + float2 uv = float2(gid) / float2(width, height); + uv = uv * 2.0 - 1.0; + float radius = 0.5; + float distance = length(uv) - radius; + uv = fmod(uv + float2(timer * 0.2, 0), float2(width, height)); + float t = fbm( uv * 3 ); + output.write(distance < 0 ? float4(float3(t), 1) : float4(0), gid); +} diff --git a/ch14/chapter14.playground/Resources/surface.png b/ch14/chapter14.playground/Resources/surface.png new file mode 100644 index 0000000..ab6d6a2 Binary files /dev/null and b/ch14/chapter14.playground/Resources/surface.png differ diff --git a/ch14/chapter14.playground/Sources/MetalView.swift b/ch14/chapter14.playground/Sources/MetalView.swift new file mode 100755 index 0000000..7120f0b --- /dev/null +++ b/ch14/chapter14.playground/Sources/MetalView.swift @@ -0,0 +1,70 @@ + +import MetalKit + +public class MetalView: MTKView, NSWindowDelegate { + + var queue: MTLCommandQueue! = nil + var cps: MTLComputePipelineState! = nil + var timer: Float = 0 + var timerBuffer: MTLBuffer! + 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 + } + + required public init(coder: NSCoder) { + super.init(coder: coder) + } + + override public init(frame frameRect: CGRect, device: MTLDevice?) { + super.init(frame: frameRect, device: device) + registerShaders() + } + + override public func drawRect(dirtyRect: NSRect) { + if let drawable = currentDrawable { + let commandBuffer = queue.commandBuffer() + let commandEncoder = commandBuffer.computeCommandEncoder() + commandEncoder.setComputePipelineState(cps) + commandEncoder.setTexture(drawable.texture, atIndex: 0) + commandEncoder.setBuffer(mouseBuffer, offset: 0, atIndex: 2) + commandEncoder.setBuffer(timerBuffer, offset: 0, atIndex: 1) + update() + let threadGroupCount = MTLSizeMake(8, 8, 1) + let threadGroups = MTLSizeMake(drawable.texture.width / threadGroupCount.width, drawable.texture.height / threadGroupCount.height, 1) + commandEncoder.dispatchThreadgroups(threadGroups, threadsPerThreadgroup: threadGroupCount) + commandEncoder.endEncoding() + commandBuffer.presentDrawable(drawable) + commandBuffer.commit() + } + + } + + func update() { + timer += 0.01 + var bufferPointer = timerBuffer.contents() + memcpy(bufferPointer, &timer, sizeof(Float)) + bufferPointer = mouseBuffer.contents() + memcpy(bufferPointer, &pos, sizeof(NSPoint)) + } + + func registerShaders() { + queue = device!.newCommandQueue() + let path = NSBundle.mainBundle().pathForResource("Shaders", ofType: "metal") + do { + let input = try String(contentsOfFile: path!, encoding: NSUTF8StringEncoding) + let library = try device!.newLibraryWithSource(input, options: nil) + let kernel = library.newFunctionWithName("compute")! + cps = try device!.newComputePipelineStateWithFunction(kernel) + } catch let e { + Swift.print("\(e)") + } + timerBuffer = device!.newBufferWithLength(sizeof(Float), options: []) + mouseBuffer = device!.newBufferWithLength(sizeof(NSPoint), options: []) + } +} diff --git a/ch14/chapter14.playground/contents.xcplayground b/ch14/chapter14.playground/contents.xcplayground new file mode 100644 index 0000000..06828af --- /dev/null +++ b/ch14/chapter14.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ch14/chapter14.playground/playground.xcworkspace/contents.xcworkspacedata b/ch14/chapter14.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100755 index 0000000..919434a --- /dev/null +++ b/ch14/chapter14.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ch14/chapter14.playground/playground.xcworkspace/xcuserdata/marius.xcuserdatad/UserInterfaceState.xcuserstate b/ch14/chapter14.playground/playground.xcworkspace/xcuserdata/marius.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100755 index 0000000..a985682 Binary files /dev/null and b/ch14/chapter14.playground/playground.xcworkspace/xcuserdata/marius.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ch14/chapter14.playground/timeline.xctimeline b/ch14/chapter14.playground/timeline.xctimeline new file mode 100644 index 0000000..bf468af --- /dev/null +++ b/ch14/chapter14.playground/timeline.xctimeline @@ -0,0 +1,6 @@ + + + + +