added shadows
This commit is contained in:
parent
8192c344e7
commit
c340f80664
|
|
@ -20,4 +20,5 @@ Repository to accompany the following blog posts:
|
||||||
- [Using MetalKit part 16](http://metalkit.org/2016/07/06/using-metalkit-part-16.html)
|
- [Using MetalKit part 16](http://metalkit.org/2016/07/06/using-metalkit-part-16.html)
|
||||||
- [Using MetalKit part 17](http://metalkit.org/2016/09/24/using-metalkit-part-17.html)
|
- [Using MetalKit part 17](http://metalkit.org/2016/09/24/using-metalkit-part-17.html)
|
||||||
- [Using MetalKit part 18](http://metalkit.org/2016/10/01/using-metalkit-part-2-3-2.html)
|
- [Using MetalKit part 18](http://metalkit.org/2016/10/01/using-metalkit-part-2-3-2.html)
|
||||||
- [Raymarching in Metal](http://metalkit.org/2016/12/30/raymarching-in-metal.html)
|
- [Raymarching in Metal](http://metalkit.org/2016/12/30/raymarching-in-metal.html)
|
||||||
|
- [Shadows in Metal part 1](http://metalkit.org/2017/01/31/shadows-in-metal-part-1.html)
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
import MetalKit
|
||||||
|
import PlaygroundSupport
|
||||||
|
|
||||||
|
let frame = NSRect(x: 0, y: 0, width: 300, height: 300)
|
||||||
|
let delegate = MetalView()
|
||||||
|
let view = MTKView(frame: frame, device: delegate.device)
|
||||||
|
view.delegate = delegate
|
||||||
|
PlaygroundPage.current.liveView = view
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
|
||||||
|
#include <metal_stdlib>
|
||||||
|
|
||||||
|
using namespace metal;
|
||||||
|
|
||||||
|
float differenceOp(float d0, float d1) {
|
||||||
|
return max(d0, -d1);
|
||||||
|
}
|
||||||
|
|
||||||
|
float distanceToRect( float2 point, float2 center, float2 size ) {
|
||||||
|
point -= center;
|
||||||
|
point = abs(point);
|
||||||
|
point -= size / 2.;
|
||||||
|
return max(point.x, point.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
float distanceToScene( float2 point ) {
|
||||||
|
float d2r1 = distanceToRect( point, float2(0.), float2(0.45, 0.85) );
|
||||||
|
float2 mod = point - 0.1 * floor(point / 0.1);
|
||||||
|
float d2r2 = distanceToRect( mod, float2( 0.05 ), float2(0.02, 0.04) );
|
||||||
|
float diff = differenceOp(d2r1, d2r2);
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getShadow(float2 point, float2 lightPos) {
|
||||||
|
float2 lightDir = normalize(lightPos - point);
|
||||||
|
float dist2light = length(lightDir);
|
||||||
|
float distAlongRay = 0.0;
|
||||||
|
for (float i=0.0; i < 80.; i++) {
|
||||||
|
float2 currentPoint = point + lightDir * distAlongRay;
|
||||||
|
float d2scene = distanceToScene(currentPoint);
|
||||||
|
if (d2scene <= 0.001) { return 0.0; }
|
||||||
|
distAlongRay += d2scene;
|
||||||
|
if (distAlongRay > dist2light) { break; }
|
||||||
|
}
|
||||||
|
return 1.;
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel void compute(texture2d<float, access::write> output [[texture(0)]],
|
||||||
|
constant float &timer [[buffer(0)]],
|
||||||
|
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 d2scene = distanceToScene(uv);
|
||||||
|
bool i = d2scene < 0.0;
|
||||||
|
float4 color = i ? float4( .1, .5, .5, 1. ) : float4( .7, .8, .8, 1. );
|
||||||
|
float2 lightPos = float2(1.3 * sin(timer), 1.3 * cos(timer));
|
||||||
|
float dist2light = length(lightPos - uv);
|
||||||
|
color *= max(0.0, 2. - dist2light );
|
||||||
|
float shadow = getShadow(uv, lightPos);
|
||||||
|
shadow = shadow * 0.5 + 0.5;
|
||||||
|
color *= shadow;
|
||||||
|
output.write(color, gid);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
|
||||||
|
import MetalKit
|
||||||
|
|
||||||
|
public class MetalView: NSObject, MTKViewDelegate {
|
||||||
|
|
||||||
|
public var device: MTLDevice! = nil
|
||||||
|
var queue: MTLCommandQueue! = nil
|
||||||
|
var cps: MTLComputePipelineState! = nil
|
||||||
|
var timerBuffer: MTLBuffer! = nil
|
||||||
|
var timer: Float = 0
|
||||||
|
|
||||||
|
override public init() {
|
||||||
|
super.init()
|
||||||
|
device = MTLCreateSystemDefaultDevice()
|
||||||
|
queue = device.makeCommandQueue()
|
||||||
|
registerShaders()
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerShaders() {
|
||||||
|
guard let path = Bundle.main.path(forResource: "Shaders", ofType: "metal") else { return }
|
||||||
|
do {
|
||||||
|
let input = try String(contentsOfFile: path, encoding: String.Encoding.utf8)
|
||||||
|
let library = try device.makeLibrary(source: input, options: nil)
|
||||||
|
guard let kernel = library.makeFunction(name: "compute") else { return }
|
||||||
|
cps = try device.makeComputePipelineState(function: kernel)
|
||||||
|
} catch let e {
|
||||||
|
Swift.print("\(e)")
|
||||||
|
}
|
||||||
|
timerBuffer = device.makeBuffer(length: MemoryLayout<Float>.size, options: [])
|
||||||
|
}
|
||||||
|
|
||||||
|
func update() {
|
||||||
|
timer += 0.01
|
||||||
|
let bufferPointer = timerBuffer.contents()
|
||||||
|
memcpy(bufferPointer, &timer, MemoryLayout<Float>.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}
|
||||||
|
|
||||||
|
public func draw(in view: MTKView) {
|
||||||
|
if let drawable = view.currentDrawable {
|
||||||
|
let commandBuffer = queue.makeCommandBuffer()
|
||||||
|
let commandEncoder = commandBuffer.makeComputeCommandEncoder()
|
||||||
|
commandEncoder.setComputePipelineState(cps)
|
||||||
|
commandEncoder.setTexture(drawable.texture, at: 0)
|
||||||
|
commandEncoder.setBuffer(timerBuffer, offset: 0, at: 0)
|
||||||
|
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.present(drawable)
|
||||||
|
commandBuffer.commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<playground version='5.0' target-platform='osx' executeOnSourceChanges='false'>
|
||||||
|
<timeline fileName='timeline.xctimeline'/>
|
||||||
|
</playground>
|
||||||
7
shadows/shadows.playground/playground.xcworkspace/contents.xcworkspacedata
generated
Executable file
7
shadows/shadows.playground/playground.xcworkspace/contents.xcworkspacedata
generated
Executable file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
BIN
shadows/shadows.playground/playground.xcworkspace/xcuserdata/marius.xcuserdatad/UserInterfaceState.xcuserstate
generated
Executable file
BIN
shadows/shadows.playground/playground.xcworkspace/xcuserdata/marius.xcuserdatad/UserInterfaceState.xcuserstate
generated
Executable file
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Timeline
|
||||||
|
version = "3.0">
|
||||||
|
<TimelineItems>
|
||||||
|
</TimelineItems>
|
||||||
|
</Timeline>
|
||||||
Loading…
Reference in New Issue