git.iko.soy/metal-metaballs
on master help sign in
Refactored shader and renderer to accept graph data.
commit7fdd9e12f1b00db34da84f289c75962d5f845e37 authorvinivendra <vinivendra@gmail.com> dateThu 11 Feb 2016, 17:49 (11 years ago) parenta46f437
4 files changed+48−24
PixelImage/Matrix.swift+10−4 PixelImage/MetalMetaballRenderer.swift+10−4 PixelImage/ViewController.swift+7−1 PixelImage/shaders.metal+21−15
PixelImage/Matrix.swift+10−4
@@ -5,6 +5,12 @@ class Graph<T> {
55 var adjacencyMatrix: Matrix
66 var vertices: [T]
77
8+ var size: Int {
9+ get {
10+ return vertices.count
11+ }
12+ }
13+
814 init(vertices: [T]) {
915 let size = vertices.count
1016
@@ -24,20 +30,20 @@ class Graph<T> {
2430}
2531
2632struct Matrix: CustomStringConvertible {
27− private var buffer: [CGFloat]
33+ var buffer: [Float]
2834
2935 let size: Int
3036
3137 init(size: Int) {
32− buffer = [CGFloat](count: size * size, repeatedValue: 0.0)
38+ buffer = [Float](count: size * size, repeatedValue: 0.0)
3339 self.size = size
3440 }
3541
36− func get(i: Int, j: Int) -> CGFloat {
42+ func get(i: Int, j: Int) -> Float {
3743 return buffer[index(i, j)]
3844 }
3945
40− mutating func set(i: Int, _ j: Int, value: CGFloat = 1.0) {
46+ mutating func set(i: Int, _ j: Int, value: Float = 1.0) {
4147 buffer[index(i, j)] = value
4248 }
4349
PixelImage/MetalMetaballRenderer.swift+10−4
@@ -1,8 +1,6 @@
11import Metal
22import UIKit
33
4−var i = 0
5−
64class MetalMetaballRenderer {
75
86 typealias TargetView = UIImageView
@@ -54,8 +52,6 @@ class MetalMetaballRenderer {
5452 let timeout = dispatch_time(DISPATCH_TIME_NOW, 1000000000)
5553 dispatch_semaphore_wait(semaphore, timeout)
5654
57− print("Update \(i++)!")
58−
5955 state = .Ending
6056
6157 dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.rawValue), 0)) { () -> Void in
@@ -108,6 +104,8 @@ class MetalMetaballRenderer {
108104 commandEncoder.setTexture(computeContext.texture, atIndex: 0)
109105 let metaballInfoBuffer = metaballBuffer()
110106 commandEncoder.setBuffer(metaballInfoBuffer, offset: 0, atIndex: 0)
107+ let edgesBuffer = metaballEdgesBuffer()
108+ commandEncoder.setBuffer(edgesBuffer, offset: 0, atIndex: 1)
111109 commandEncoder.dispatchThreadgroups(threadGroups,
112110 threadsPerThreadgroup: threadGroupCounts)
113111 commandEncoder.endEncoding()
@@ -163,6 +161,14 @@ class MetalMetaballRenderer {
163161 return image
164162 }
165163
164+ func metaballEdgesBuffer() -> MTLBuffer {
165+ let floats = dataSource.metaballGraph.adjacencyMatrix.buffer
166+
167+ let buffer = context.device.newBufferWithBytes(floats, length: floats.count * sizeof(Float), options: .StorageModeShared)
168+
169+ return buffer
170+ }
171+
166172 func metaballBuffer() -> MTLBuffer {
167173 // Create Float array for buffer
168174 // Exclude metaballs far from the view's bounds
PixelImage/ViewController.swift+7−1
@@ -20,10 +20,16 @@ class ViewController: UIViewController, MetaballDataSource {
2020 let recognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
2121 view.addGestureRecognizer(recognizer)
2222
23− let metaballs = [CGPoint(x: 70, y: 70), CGPoint(x: 270, y: 470), CGPoint(x: 270, y: 70), CGPoint(x: 70, y: 470)].map {
23+ let metaballs = [CGPoint(x: 70, y: 70), CGPoint(x: 270, y: 70), CGPoint(x: 270, y: 470), CGPoint(x: 70, y: 470)].map {
2424 Metaball(position: $0)
2525 }
2626 metaballGraph = Graph(vertices: metaballs)
27+ metaballGraph.addEdge(0, 1)
28+ metaballGraph.addEdge(0, 3)
29+ metaballGraph.addEdge(1, 2)
30+ metaballGraph.addEdge(2, 3)
31+ metaballGraph.removeEdge(2, 3)
32+ print(metaballGraph.adjacencyMatrix)
2733
2834 let border = 20
2935 let metaballViewFrame = CGRect(x: border/2, y: border/2, width: width, height: height)
PixelImage/shaders.metal+21−15
@@ -5,40 +5,46 @@ using namespace metal;
55
66kernel void
77 drawMetaballs(texture2d<float, access::write> outTexture[[texture(0)]],
8− constant float *metaballBuffer [[buffer(0)]],
8+ constant float *edgesBuffer[[buffer(1)]],
9+ constant float *metaballBuffer[[buffer(0)]],
910 uint2 gid[[thread_position_in_grid]]) {
1011
11− char numberOfMetaballs = metaballBuffer[0];
12− char metaballArraySize = numberOfMetaballs * 2 + 1;
12+ uint numberOfMetaballs = metaballBuffer[0];
13+ uint metaballArraySize = numberOfMetaballs * 2 + 1;
1314
1415 float sum = 0;
1516
16− for (char i = 1; i < metaballArraySize; i += 2) {
17− for (char j = i + 2; j < metaballArraySize; j += 2) {
18− float2 metaball1 = float2(metaballBuffer[i],
19− metaballBuffer[i + 1]);
17+ uint i, j, x, y;
2018
21− float2 metaball2 = float2(metaballBuffer[j],
22− metaballBuffer[j + 1]);
19+ for (i = 1, x = 0; i < metaballArraySize; i += 2, x += 1) {
20+ for (j = i + 2, y = x + 1; j < metaballArraySize; j += 2, y += 1) {
21+ float2 metaball1 = float2(metaballBuffer[i], metaballBuffer[i + 1]);
22+
23+ float2 metaball2 = float2(metaballBuffer[j], metaballBuffer[j + 1]);
2324
2425 float2 metaball1Vector
2526 = float2(metaball1.x - gid.x, metaball1.y - gid.y);
2627 float2 metaball2Vector
2728 = float2(metaball2.x - gid.x, metaball2.y - gid.y);
2829
30+ float value1 = 2048 / (dot(metaball1Vector, metaball1Vector) + 1);
31+ float value2 = 2048 / (dot(metaball2Vector, metaball2Vector) + 1);
32+
33+ float v = value1 + value2;
34+
35+ float weightedValue = 0.5 * v;
36+
37+ char edgeIndex = y * numberOfMetaballs + x;
38+ float edgeWeight = edgesBuffer[edgeIndex];
2939 float2 direction1 = normalize(metaball1Vector);
3040 float2 direction2 = normalize(metaball2Vector);
3141
3242 float cosine = dot(direction1, direction2);
3343
34− float value1 = 2048 / (dot(metaball1Vector, metaball1Vector) + 1);
35− float value2 = 2048 / (dot(metaball2Vector, metaball2Vector) + 1);
36−
37− float v = value1 + value2;
3844 float link = pow(((1 - cosine) * 0.5), 100);
3945
40− float weightedLink = 0.6 * link;
41− float weightedValue = 0.5 * v;
46+ float weightedLink = 0.6 * link * edgeWeight;
47+
4248
4349 sum += mix(0.0, 1.0, step(0.5, weightedValue + weightedLink));
4450 }