~connorbell/conemarching-ar

227ce465e0994c78ffe9891faf779f735cfea447 — Connor Bell 1 year, 9 months ago 561ccee
Added icons, fixed some ui bugs
34 files changed, 259 insertions(+), 140 deletions(-)

M README.md
M conemarch-ar.xcodeproj/project.pbxproj
M conemarch-ar.xcodeproj/project.xcworkspace/xcuserdata/connorbell.xcuserdatad/UserInterfaceState.xcuserstate
M conemarch-ar.xcodeproj/xcuserdata/connorbell.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/1024x1024.png
M conemarch-ar/Assets.xcassets/AppIcon.appiconset/Contents.json
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-60@2x-1.png
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-76.png
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-41.png
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
D conemarch-ar/Assets.xcassets/AppIcon.appiconset/cone.png
M conemarch-ar/DistanceFields/DistanceFields.plist
M conemarch-ar/Engine/ARManager.swift
M conemarch-ar/Engine/Conemarcher.swift
M conemarch-ar/Engine/MetalManager.swift
M conemarch-ar/Engine/MetalView.swift
M conemarch-ar/Engine/SceneKitRenderer.swift
M conemarch-ar/Shaders/Compositor.metal
M conemarch-ar/Shaders/Conemarcher.metal
M conemarch-ar/Shaders/DistanceFields.metal
M conemarch-ar/Shaders/PostFx.h
M conemarch-ar/Shaders/PostFx.metal
M conemarch-ar/Shaders/ShadowMaterial.metal
M conemarch-ar/UI/Base.lproj/Main.storyboard
M conemarch-ar/UI/TimerView.swift
M conemarch-ar/ViewController.swift
M README.md => README.md +21 -7
@@ 1,21 1,35 @@
#  Lidar+Conemarcher

A sandbox engine to render signed distance fields via a multi-pass "conemarch" algorithm.
Prototype engine to render signed distance fields in Augmented Reality defined by Metal Function Pointers via a multi-pass "conemarching" algorithm.


![image](http://connorbell.ca/images/conemarching.jpg)
![image](http://connorbell.ca/images/conemarcher-readme/conemarching.jpg)

The conemarch first pass projects rays from a 3x2 texture and tests against a cone of a width equal to the region of the pixel. The following passes double the texture size and begins marching from where the previous lower-resolution pass left off. The idea is to march as far as you can in low resolutions and fill in the details as you need. The final pass is a standard raymarch with ray-relaxation which over-estimates the raymarch step and verifies it didn't miss any data by comparing adjacent bounding spheres. The ray step also uses the world depth as a max distance to save computation where we don't need to render.
##  Engine Architecture

The resulting render is then composed with ARSession's camera and lidar data. 
ARSession provides color, smoothed depth, and the view+projection matrices to the conemarcher.

The conemarcher renders the distance field progressively over a number of passes, doubling the resolution each pass. Rays are advanced as far as the cone guarantees there won't be an intersection, filling in the details at higher resolutions.

The final pass removes the cone constraint and performs regular raymarching. The depth texture from ARSession is tested against the depth of the projected ray in order to rule out unnecessary computation. Rays are advanced by overestimating the minimum distance to a surface by a factor of 2 while validating that the bounding distance spheres between steps overlap (Ray-relaxing 😎).

##  Distance Fields

Distance field functions are defined as visible functions in DistanceFields.metal. In order to display them in the app, they require an entry in DistanceFields.plist. 
![image](http://connorbell.ca/images/conemarcher-readme/distancefields.jpg)

Distance fields are defined as visible functions in DistanceFields.metal.

Each field requires an entry in DistanceFields.plist, referencing the function name, variables, and display name.

Four float4 uniforms can be defined for each entry, with their own name and range to be displayed in the UI.

##  Post Fx

Post Fx functions are defined as visible functions in PostFx.metal.
Similar to distance fields, the post fx functions are defined in PostFx.metal and referenced by an entry in PostFx.plist.

##  Future

Raymarched shadows via planes detected from ARKit with Metal.

##  Acknowledgements



@@ 24,7 38,7 @@ MetalViewRecorder: Warren Moore
Ray-relaxing: Keinert et al.
https://erleuchtet.org/~cupe/permanent/enhanced_sphere_tracing.pdf

SDF primitives by Inigo Quilez
Torus, Cube SDF primitives by Inigo Quilez
https://iquilezles.org/www/articles/distfunctions/distfunctions.htm

Conemarching technique developed by fulcrum demo team

M conemarch-ar.xcodeproj/project.pbxproj => conemarch-ar.xcodeproj/project.pbxproj +2 -0
@@ 574,6 574,7 @@
				IPHONEOS_DEPLOYMENT_TARGET = 14.4;
				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
				MTL_FAST_MATH = YES;
				MTL_IGNORE_WARNINGS = NO;
				MTL_LANGUAGE_REVISION = Metal24;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = iphoneos;


@@ 630,6 631,7 @@
				IPHONEOS_DEPLOYMENT_TARGET = 14.4;
				MTL_ENABLE_DEBUG_INFO = NO;
				MTL_FAST_MATH = YES;
				MTL_IGNORE_WARNINGS = NO;
				MTL_LANGUAGE_REVISION = Metal24;
				SDKROOT = iphoneos;
				SWIFT_COMPILATION_MODE = wholemodule;

M conemarch-ar.xcodeproj/project.xcworkspace/xcuserdata/connorbell.xcuserdatad/UserInterfaceState.xcuserstate => conemarch-ar.xcodeproj/project.xcworkspace/xcuserdata/connorbell.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
M conemarch-ar.xcodeproj/xcuserdata/connorbell.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist => conemarch-ar.xcodeproj/xcuserdata/connorbell.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +2 -2
@@ 14,8 14,8 @@
            filePath = "conemarch-ar/Engine/MetalManager.swift"
            startingColumnNumber = "9223372036854775807"
            endingColumnNumber = "9223372036854775807"
            startingLineNumber = "63"
            endingLineNumber = "63"
            startingLineNumber = "62"
            endingLineNumber = "62"
            landmarkName = "createComputePipelineState(name:linkedFunctions:)"
            landmarkType = "7">
         </BreakpointContent>

A conemarch-ar/Assets.xcassets/AppIcon.appiconset/1024x1024.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/1024x1024.png +0 -0
M conemarch-ar/Assets.xcassets/AppIcon.appiconset/Contents.json => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Contents.json +13 -1
@@ 1,6 1,7 @@
{
  "images" : [
    {
      "filename" : "Icon-Small-40.png",
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "20x20"


@@ 11,26 12,31 @@
      "size" : "20x20"
    },
    {
      "filename" : "Icon-Small@2x.png",
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "29x29"
    },
    {
      "filename" : "Icon-Small@3x.png",
      "idiom" : "iphone",
      "scale" : "3x",
      "size" : "29x29"
    },
    {
      "filename" : "Icon-Small-40@2x-1.png",
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "40x40"
    },
    {
      "filename" : "Icon-60@2x-1.png",
      "idiom" : "iphone",
      "scale" : "3x",
      "size" : "40x40"
    },
    {
      "filename" : "Icon-60@2x.png",
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "60x60"


@@ 41,6 47,7 @@
      "size" : "60x60"
    },
    {
      "filename" : "Icon-Notification.png",
      "idiom" : "ipad",
      "scale" : "1x",
      "size" : "20x20"


@@ 61,32 68,37 @@
      "size" : "29x29"
    },
    {
      "filename" : "Icon-Small-41.png",
      "idiom" : "ipad",
      "scale" : "1x",
      "size" : "40x40"
    },
    {
      "filename" : "Icon-Small-40@2x.png",
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "40x40"
    },
    {
      "filename" : "Icon-76.png",
      "idiom" : "ipad",
      "scale" : "1x",
      "size" : "76x76"
    },
    {
      "filename" : "Icon-76@2x.png",
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "76x76"
    },
    {
      "filename" : "Icon-83.5@2x.png",
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "83.5x83.5"
    },
    {
      "filename" : "cone.png",
      "filename" : "1024x1024.png",
      "idiom" : "ios-marketing",
      "scale" : "1x",
      "size" : "1024x1024"

A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-60@2x-1.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-60@2x-1.png +0 -0
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png +0 -0
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-76.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-76.png +0 -0
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png +0 -0
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png +0 -0
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png +0 -0
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png +0 -0
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png +0 -0
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png +0 -0
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-41.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small-41.png +0 -0
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png +0 -0
A conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png +0 -0
D conemarch-ar/Assets.xcassets/AppIcon.appiconset/cone.png => conemarch-ar/Assets.xcassets/AppIcon.appiconset/cone.png +0 -0
M conemarch-ar/DistanceFields/DistanceFields.plist => conemarch-ar/DistanceFields/DistanceFields.plist +2 -11
@@ 195,6 195,8 @@
	</dict>
	<key>GASKET</key>
	<dict>
		<key>fudge</key>
		<real>0.65</real>
		<key>index</key>
		<integer>0</integer>
		<key>steps</key>


@@ 238,19 240,8 @@
	</dict>
	<key>OCTA</key>
	<dict>
		<key>index</key>
		<integer>0</integer>
		<key>steps</key>
		<integer>6</integer>
		<key>position</key>
		<dict>
			<key>x</key>
			<integer>0</integer>
			<key>y</key>
			<integer>0</integer>
			<key>z</key>
			<real>-1</real>
		</dict>
		<key>colorKernel</key>
		<string></string>
		<key>visibleFunction</key>

M conemarch-ar/Engine/ARManager.swift => conemarch-ar/Engine/ARManager.swift +11 -0
@@ 54,6 54,8 @@ final class ARManager: NSObject {
    }
    
    let arSession: ARSession
    var anchor: ARAnchor?

    var delegate: ARManagerDelegate?
    var rendererDelegate: RendererDelegate?



@@ 71,6 73,7 @@ final class ARManager: NSObject {
    var cameraType: CameraType = .back
    
    var YCyCbPass: ComputePass
    var lastRaycastTransform = simd_float4x4()
    
    init(metalProvider: MetalManager, resolution: CGSize) {
        arSession = ARSession()


@@ 88,6 91,7 @@ final class ARManager: NSObject {
        if cameraType == .back {
            let config = ARWorldTrackingConfiguration()
            config.isLightEstimationEnabled = true
            config.planeDetection = [.horizontal]
            config.frameSemantics = [.sceneDepth, .smoothedSceneDepth]
            arSession.run(config)
        } else {


@@ 170,12 174,19 @@ extension ARManager: ARSessionDelegate {
                                                     (result.worldTransform.columns.3.y),
                                                     (result.worldTransform.columns.3.z))
                arData.distanceToRaycast = simd_distance(camPos, arData.raycastPosition)
                lastRaycastTransform = result.worldTransform
            }
        }
        
        delegate?.ARManagerDidReadyFrame(arData: arData)
    }
    
    func addAnchor() {
        let anchor = ARAnchor(transform: lastRaycastTransform)
        arSession.add(anchor: anchor)
        self.anchor = anchor
    }
    
    func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
        for anchor in anchors {
            let identifier = anchor.identifier.uuidString

M conemarch-ar/Engine/Conemarcher.swift => conemarch-ar/Engine/Conemarcher.swift +1 -1
@@ 36,7 36,7 @@ class Conemarcher {
    var startHeight = 2
    
    let coneWidthScale = 2.0
    let maxDist = 20.0
    let maxDist = 50.0
    let minDist = 0.001
    let startTime = CFAbsoluteTimeGetCurrent()
    

M conemarch-ar/Engine/MetalManager.swift => conemarch-ar/Engine/MetalManager.swift +0 -1
@@ 48,7 48,6 @@ final class MetalManager {
            if let linkedFunctions = linkedFunctions {
                computeDescriptor.linkedFunctions = linkedFunctions
            }

            do {
                let state = try metalDevice.makeComputePipelineState(descriptor: computeDescriptor, options: [], reflection: nil)
                pipelineStateCache[name] = state

M conemarch-ar/Engine/MetalView.swift => conemarch-ar/Engine/MetalView.swift +1 -1
@@ 99,7 99,7 @@ final class MetalView: MTKView  {
        guard let arData = self.arData else { return }
        let drawableTextureUniform = TextureUniform(texture: drawable.texture, index: 1)

        if arData.distanceToRaycast < 0.25 {
        if arData.distanceToRaycast < 0.25 && !debug {
            guard let rgbTexture = arData.rgbTexture else { return }
            renderLightPass(mtlFriend: mtlProvider!, inputTexture: rgbTexture, drawable: drawable, outputTexture: drawableTextureUniform)
            return

M conemarch-ar/Engine/SceneKitRenderer.swift => conemarch-ar/Engine/SceneKitRenderer.swift +55 -10
@@ 16,25 16,54 @@ class SceneKitRenderer {
    var shaderProgram: SCNProgram
    var camera: SCNCamera
    let cameraNode = SCNNode()

    
    var mtlFriend: MetalManager
    var renderTexture: MTLTexture
    var depthTexture: MTLTexture
    var lightNode = SCNNode()
    
    init(mtlFriend: MetalManager, resolution: CGSize) {
        renderer = SCNRenderer(device: mtlFriend.metalDevice, options: [:])
        renderTexture = mtlFriend.allocTexture(pixelFormat: .rgba32Float, width: Int(resolution.width), height: Int(resolution.height), usage: [.renderTarget, .shaderRead])
        renderTexture = mtlFriend.allocTexture(pixelFormat: .bgra8Unorm, width: Int(resolution.width), height: Int(resolution.height), usage: [.renderTarget, .shaderRead])
        depthTexture = mtlFriend.allocTexture(pixelFormat: .depth32Float, width: Int(resolution.width), height: Int(resolution.height), usage: [.renderTarget, .shaderRead])
        
        scene = SCNScene()
        shaderProgram = SCNProgram()
        shaderProgram.vertexFunctionName = "vertexShader"
        shaderProgram.fragmentFunctionName = "shadowFragment"
        
        camera = SCNCamera()
        camera.zNear = 0.001;
        camera.zFar = 1000;
        cameraNode.camera = camera
        scene.rootNode.camera = camera
        scene.rootNode.addChildNode(cameraNode)
        
        material = SCNMaterial()
        material.program = shaderProgram
        shaderProgram.handleBinding(ofBufferNamed: "", frequency: .perShadable) { bufferStream, _, _, _ in
            
        }
        renderer.scene = scene
        renderer.autoenablesDefaultLighting = true
        renderer.usesReverseZ = false

        lightNode.light = SCNLight()
        lightNode.light!.type = SCNLight.LightType.omni
        lightNode.light!.color = UIColor(white: 1, alpha: 1.0)
        lightNode.light!.intensity = 40
        lightNode.position = SCNVector3Make(0, 5, 0)
        scene.rootNode.addChildNode(lightNode)
        
        let cubeGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)
        let cubeNode = SCNNode(geometry: cubeGeometry)
        cubeNode.position = SCNVector3(0.0, 0.0, -1.5)
        let redMaterial = SCNMaterial()
        redMaterial.diffuse.contents = UIColor.blue
        redMaterial.lightingModel = .physicallyBased

        cubeGeometry.materials = [redMaterial]
        
        scene.rootNode.addChildNode(cubeNode)
        
        self.mtlFriend = mtlFriend
    }
    


@@ 43,10 72,11 @@ class SceneKitRenderer {
        if let plane = planes[uuid] {
            plane.update(from: planeGeometry)
        } else {
            let plane = ARSCNPlaneGeometry()
            guard let plane = ARSCNPlaneGeometry(device: mtlFriend.metalDevice) else { return }
            
            plane.update(from: planeGeometry)
            plane.insertMaterial(material, at: 0)
            plane.firstMaterial = material
            plane.firstMaterial?.lightingModel = .physicallyBased
            
            let node = SCNNode(geometry: plane)
            scene.rootNode.addChildNode(node)


@@ 57,14 87,16 @@ class SceneKitRenderer {
    
    func render(time: CFTimeInterval,
                cameraTransform: simd_float4x4,
                projectionMatrix: simd_float4x4,
                viewport: CGRect,
                distanceFieldIndex: Int,
                distanceFieldFunctionTable: MTLVisibleFunctionTable,
                distanceFieldParameters: DistanceFieldParameters) -> MTLTexture? {
                distanceFieldParameters: DistanceFieldParameters) -> (MTLTexture?, MTLTexture?) {
        
        guard let commandBuffer = mtlFriend.commandQueue.makeCommandBuffer() else { return nil }
        guard let commandBuffer = mtlFriend.commandQueue.makeCommandBuffer() else { return (nil, nil) }
    
        cameraNode.simdTransform = cameraTransform
        camera.projectionTransform = SCNMatrix4(projectionMatrix)
        
        var t = time
        let timeData = Data(bytes: &t, count: MemoryLayout<Float>.size)


@@ 75,17 107,30 @@ class SceneKitRenderer {
        var params = distanceFieldParameters
        let distanceFieldData = Data(bytes: &params, count: MemoryLayout<DistanceFieldParameters>.stride)
        
        var functionTable = distanceFieldFunctionTable
        let functionTableData = Data(bytes: &functionTable, count: functionTable.allocatedSize)
        
        planes.first?.value.setValue(functionTableData, forKey: "distanceFieldFunctions")
        material.setValue(timeData, forKey: "time")
        material.setValue(distanceFieldFunctionTable, forKey: "distanceFieldFunctions")
        material.setValue(functionTableData, forKey: "distanceFieldFunctions")
        material.setValue(distanceFieldIndexData, forKey: "distanceFieldIndex")
        material.setValue(distanceFieldData, forKey: "distanceFieldParameters")
        
        let passDescriptor = MTLRenderPassDescriptor()
        passDescriptor.colorAttachments[0].texture = renderTexture
        passDescriptor.colorAttachments[0].loadAction = .clear
        passDescriptor.colorAttachments[0].storeAction = .store
        passDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1.0, green: 0, blue: 0, alpha: 1.0)
        
        passDescriptor.depthAttachment.texture = depthTexture
        passDescriptor.depthAttachment.loadAction = .clear
        passDescriptor.depthAttachment.clearDepth = 1.0
        passDescriptor.depthAttachment.storeAction = .store
        
        
        renderer.render(atTime: time, viewport: viewport, commandBuffer: commandBuffer, passDescriptor: passDescriptor)
        commandBuffer.commit()
        commandBuffer.waitUntilCompleted()
        return renderTexture
        return (renderTexture, depthTexture)
    }
}

M conemarch-ar/Shaders/Compositor.metal => conemarch-ar/Shaders/Compositor.metal +2 -28
@@ 26,44 26,18 @@ kernel void CompositorPass(texture2d<float, access::sample> worldTexture [[textu
                           device const float4 &uniforms [[buffer(4)]],
                           uint2 gid [[ thread_position_in_grid ]]) {
 
    constexpr sampler colorSampler(coord::normalized,
                                   address::repeat,
                                   filter::linear);
    
    PostFxFunction *postFxFunction = postFxFunctions[postFxFunctionIndex];
    
    float2 outSize = float2(outTexture.get_width(), outTexture.get_height());
    float2 uv = float2(gid) / outSize;
//    float2 transformedUv = transformedTo(orientation, uv);`
//    float4 worldColor = worldTexture.sample(colorSampler, uv);
    
    float4 col = conemarchTexture.sample(colorSampler, uv);

    /*
    float minDist = worldColor.a;

    float depthWorld = depthTexture.sample(colorSampler, transformedUv).r;
    uint2 bSamplePos = uint2(gid.x*2 % blueNoiseTexture.get_width(), gid.y*2 % blueNoiseTexture.get_height());
    float blueNoiseSample = blueNoiseTexture.read(bSamplePos).r;
    const float c_goldenRatioConjugate = 0.61803398875f;

    blueNoiseSample = fract(blueNoiseSample + float(time) * c_goldenRatioConjugate);

    minDist = min(min(conemarchColor.a,10.), depthWorld);
    float smoothDistance = saturate(minDist / 1.) * (0.1 + blueNoiseSample*0.005);
    
    col.rgb = mix(conemarchColor.rgb, worldColor.rgb, saturate((conemarchColor.a - depthWorld)/smoothDistance));
    col.rgb *= 0.5 + smoothstep(smoothDistance*0.9, smoothDistance, abs(conemarchColor.a - depthWorld)) * 0.5;
     
     */
   // col.rgb = mix(col.rgb, worldColor.rgb, smoothstep(9.5, 10.0, minDist));

    PostFxParameters params;
    params.uv = uv;
    params.col = col;
    params.rgbTexture = conemarchTexture;
    params.lastTexture = lastTexture;
    params.time = time;
    params.uniforms = uniforms;
    float4 col;
    col.rgb = postFxFunction(params);
    col.a = 1.0;
    outTexture.write(col, gid);

M conemarch-ar/Shaders/Conemarcher.metal => conemarch-ar/Shaders/Conemarcher.metal +17 -3
@@ 182,6 182,16 @@ float3 refractSample(float3 pos, float3 rayDir, float3 nor, float ior, float4x4 
    return rgbTexture.sample(s, screenSpaceUv).rgb;
}

constant float4x4 ditherTable(-4.0, 0.0, -3.0, 1.0,
                               2.0, -2.0, 3.0, -1.0,
                              -3.0, 1.0, -4.0, 0.0,
                               3.0, -1.0, 2.0, -2.0);

//    <https://www.shadertoy.com/view/4dS3Wd>
//    By Morgan McGuire @morgan3d, http://graphicscodex.com
//
float hash(float2 p) { return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * (0.1 + abs(sin(p.y * 13.0 + p.x)))); }

float4 color(device ConemarchParameters &conemarchParams,
             device DistanceFieldParams &distanceFieldParams,
             DistanceFieldFunction distanceFieldFunction,


@@ 210,11 220,16 @@ float4 color(device ConemarchParameters &conemarchParams,
        color.rgb *= saturate(0.25+0.5*dot(normal, normalize(float3(0.05, 1.0, 0.1)-res.position)));
        color.rgb *= colorTemperatureToRGB(conemarchParams.lightTemperature);
        
        float smoothDistance = 0.0125;
        float smoothDistance = 0.125;
        
        color.rgb = mix(color.rgb, worldColor.rgb, saturate((depth - worldColor.a)/smoothDistance));
        color.rgb *= 0.5 + smoothstep(smoothDistance*0.25, smoothDistance, abs(depth - worldColor.a)) * 0.5;
        color = mix(color, worldColor, saturate((depth - 15.0)/5.0));
        color.rgb = mix(color.rgb, float3(0.), saturate((depth - 45.)/5.0));
        color.a = min(50., color.a);
        float ditherAmount = saturate((res.distToSurface - 0.125) / 0.125);
        
        color.rgb = mix(color.rgb, worldColor.rgb, hash(uv) * ditherAmount);
        
    } else {
        color = worldColor;
    }


@@ 270,6 285,5 @@ kernel void conemarchKernel(texture2d<float, access::sample> previousTexture [[t
                          lastDepth,
                          rgbTexture);
    
    
    outTexture.write(result, gid);
}

M conemarch-ar/Shaders/DistanceFields.metal => conemarch-ar/Shaders/DistanceFields.metal +7 -3
@@ 14,6 14,10 @@ float2 pR(float2 p, float a) {
    return cos(a)*p + sin(a)*float2(p.y, -p.x);
}

float2 mod(float2 x, float2 y) {
    return x - y * floor(x/y);
}

float3 mod(float3 x, float3 y) {
    return x - y * floor(x/y);
}


@@ 128,7 132,7 @@ float gasket(float3 p, float time, DistanceFieldParams params) {

    float radius = params.param1.x;
    float4 pos = float4(p / s, 1.0);
    pos.xz += time*0.5;
    pos.xyz += float3(time * 0.125, time*0.5, 0.);

    pos.xyz = mod(pos.xyz, 2.)-1.;



@@ 146,7 150,7 @@ float octa(float3 pos, float time, DistanceFieldParams params) {
    float3 spacesize = float3(1.,1.5,1.) * params.param4.x;
    float distFromCam = length(pos)*0.15;
    
    pos.xyz = mod(pos.xyz, spacesize) - spacesize*0.5;
    pos.xz = mod(pos.xz, spacesize.xz) - spacesize.xz*0.5;

    float res = 1e10;
    float3 displacement = -params.param2.xyz;


@@ 169,7 173,7 @@ float octa(float3 pos, float time, DistanceFieldParams params) {
[[visible]]
float tunnel(float3 pos, float time, DistanceFieldParams params) {
    float3 spaceSize = float3(params.param1.xyz);
    pos.xy = pR(pos.xy, pos.z*0.1);
    //pos.xy = pR(pos.xy, pos.z*0.1);
    float3 p = mod(pos, spaceSize*2.0) - spaceSize;

    float dist = sdBox(p, spaceSize * params.param2.xyz);

M conemarch-ar/Shaders/PostFx.h => conemarch-ar/Shaders/PostFx.h +1 -1
@@ 11,10 11,10 @@
#define PostFx_h

struct PostFxParameters {
    float4 col;
    float4 uniforms;
    float2 uv;
    float time;
    metal::texture2d<float, metal::access::sample> rgbTexture;
    metal::texture2d<float, metal::access::sample> lastTexture;
};


M conemarch-ar/Shaders/PostFx.metal => conemarch-ar/Shaders/PostFx.metal +33 -12
@@ 9,17 9,40 @@

using namespace metal;

constexpr sampler colorSampler(coord::normalized,
                               address::clamp_to_border,
                               filter::linear);

[[visible]]
float3 postFx_blackAndWhite(PostFxParameters parameters) {
    float3 output = mix(parameters.col.rgb, sqrt(dot(parameters.col.rgb*parameters.col.rgb, float3(.299,  .587, .114))), 0.5);
    float2 uv = (parameters.uv - 0.5) * 2.0;
    uv *= 1.0 + pow(abs(float2(uv.y, uv.x)) / float2(5.0, 4.0),2.0);
    
    float2 ratio = float2(parameters.rgbTexture.get_width() / parameters.rgbTexture.get_height(), 1.0);
    float vignette = 1.-smoothstep(0.75, 2.5, length(uv * ratio) * length(uv * ratio));

    uv = uv / 2.0 + 0.5;
    float4 col = parameters.rgbTexture.sample(colorSampler, uv);

    float3 output = mix(col.rgb, sqrt(dot(col.rgb*col.rgb, float3(.299,  .587, .114))), 0.5);
    float strength = 16.0;
    
    float x = (uv.x + 4.0 ) * (uv.y + 4.0 ) * (parameters.time * 10.0);
    float grain = (fmod((fmod(x, 13.0) + 1.0) * (fmod(x, 123.0) + 1.0), 0.01)-0.005) * strength;
    output.rgb *= 1.0 - grain;
    
    
    //output = mix(output, float3(0.), saturate((parameters.col.a-2.5) / 5.0));
    output.rgb *= .85 + sin(parameters.uv.y*parameters.uniforms.x)*0.15;
    output.rgb *= .85 + sin(uv.y*parameters.uniforms.x)*0.15;
    output.rgb *= vignette;
    return output;
}

[[visible]]
float3 postFx_rgbRotate(PostFxParameters parameters) {
    return sin(parameters.col.rgb * float3(0.33, 0.66, 1.0) * 8.0 + parameters.col.a + parameters.time*0.5);
    float4 col = parameters.rgbTexture.sample(colorSampler, parameters.uv);

    return sin(col.rgb * float3(0.33, 0.66, 1.0) * 8.0 +col.a + parameters.time*0.5);
}

float3 hue(float3 col, float shift)


@@ 35,10 58,7 @@ float3 hue(float3 col, float shift)
// w: phase
[[visible]]
float3 postFx_feedback(PostFxParameters parameters) {
    constexpr sampler colorSampler(coord::normalized,
                                   address::mirrored_repeat,
                                   filter::linear);
    float4 col = parameters.col;
    float4 col = parameters.rgbTexture.sample(colorSampler, parameters.uv);
    col.rgb *= .85 + sin(parameters.uv.y*1800.)*0.15;
    float2 n = float2(sin(length(col)*4. + parameters.time + parameters.uv.x*parameters.uniforms.z), cos(length(col)*4. + parameters.time + parameters.uv.y*parameters.uniforms.z));
    float2 offsetUv = parameters.uv + n*0.01;


@@ 49,13 69,14 @@ float3 postFx_feedback(PostFxParameters parameters) {

[[visible]]
float3 postFx_hueShift(PostFxParameters parameters) {
    float l = length(parameters.col.rgb);
    float3 c = parameters.col.rgb;
    parameters.col.rg *= 0.0;
    return mix(pow(hue(parameters.col.rgb, parameters.col.a*2.0 + l*4.), 0.5), float3(0.), saturate(parameters.col.a/5.0));
    float4 col = parameters.rgbTexture.sample(colorSampler, parameters.uv);

    float l = length(col.rgb);
    return mix(pow(hue(col.rgb, col.a*2.0 + l*4.), 0.5), float3(0.), saturate(col.a/5.0));
}

[[visible]]
float3 postFx_nothing(PostFxParameters parameters) {
    return parameters.col.rgb;
    float4 col = parameters.rgbTexture.sample(colorSampler, parameters.uv);
    return col.rgb;
}

M conemarch-ar/Shaders/ShadowMaterial.metal => conemarch-ar/Shaders/ShadowMaterial.metal +7 -7
@@ 11,31 11,30 @@ using namespace metal;
#include "DistanceFields.h"

struct Node {
    float4x4 modelTransform;
    float4x4 modelViewProjectionTransform;
};

struct VertexIn {
    float4 position [[attribute(SCNVertexSemanticPosition)]];
    float4 normal [[attribute(SCNVertexSemanticNormal)]];
   // float4 normal [[attribute(SCNVertexSemanticNormal)]];
};

struct VertexOut {
    float4 position [[position]];
    float2 uv;
    float4 normal;
    float3 worldPosition;
};

vertex VertexOut vertexShader(VertexIn in [[stage_in]],
                              constant Node& scn_node [[buffer(1)]]) {
    VertexOut out;
    out.position = in.position;
    out.position = scn_node.modelViewProjectionTransform * float4(in.position.xyz, 1.0);
    
    out.uv = float2(
                    (in.position.x + 1.0) * 0.5,
                    1.0 - (in.position.y + 1.0) * 0.5
                    );
    out.worldPosition = (scn_node.modelTransform * out.position).xyz;
    out.normal = in.normal;
    //out.normal = in.normal;
    return out;
};



@@ 58,13 57,14 @@ float softShadow(float3 pos, float3 norm, float minDist, float maxDist, float ti

fragment float4 shadowFragment(VertexOut in [[stage_in]],
                              device SCNSceneBuffer& scn_frame [[buffer(0)]],
                              visible_function_table<DistanceFieldFunction> distanceFieldFunctions [[buffer(2)]],
                              visible_function_table<DistanceFieldFunction> distanceFieldFunctions [[buffer(8)]],
                              device DistanceFieldParams *distanceFieldParameters [[buffer(3)]],
                              device const int &distanceFieldIndex [[buffer(4)]],
                              device const float &time [[buffer(5)]]) {
    DistanceFieldFunction *distFunction = distanceFieldFunctions[distanceFieldIndex];
    float4 color = softShadow(in.position.xyz, normalize(float3(0.2, 1.0, 0.3)), 0.01, 5.0, time, distFunction, *distanceFieldParameters);
    return float4(color);
    return float4(0.0, 1.0, 0.0, 1.0);
};



M conemarch-ar/UI/Base.lproj/Main.storyboard => conemarch-ar/UI/Base.lproj/Main.storyboard +66 -47
@@ 1,8 1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
    <device id="ipad9_7" orientation="portrait" layout="fullscreen" appearance="light"/>
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="collection view cell content view" minToolsVersion="11.0"/>


@@ 63,10 64,11 @@
                                    <stackView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="gVL-1T-xdB">
                                        <rect key="frame" x="0.0" y="0.0" width="220" height="650"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
                                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                        <edgeInsets key="layoutMargins" top="10" left="10" bottom="10" right="10"/>
                                    </stackView>
                                </subviews>
                                <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.20107264234530214" colorSpace="custom" customColorSpace="sRGB"/>
                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                <viewLayoutGuide key="contentLayoutGuide" id="6NO-zt-DRn"/>
                                <viewLayoutGuide key="frameLayoutGuide" id="bvn-m7-OiA"/>
                            </scrollView>


@@ 89,46 91,55 @@
                                </connections>
                            </button>
                            <view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dEf-EU-FGt" userLabel="ControlsView">
                                <rect key="frame" x="678" y="621" width="70" height="194"/>
                                <rect key="frame" x="678" y="621" width="70" height="185"/>
                                <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
                                <subviews>
                                    <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="YBg-V6-5Ge">
                                        <rect key="frame" x="12" y="8" width="46" height="50"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                        <color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                        <state key="normal" title="move"/>
                                        <fontDescription key="fontDescription" name="Avenir-Book" family="Avenir" pointSize="13"/>
                                        <state key="normal" title="move">
                                            <color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                        </state>
                                        <connections>
                                            <action selector="tapTranslateWithSender:" destination="BYZ-38-t0r" eventType="touchUpInside" id="358-Jj-rBD"/>
                                        </connections>
                                    </button>
                                    <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FJk-X2-g4V">
                                        <rect key="frame" x="12" y="66" width="46" height="50"/>
                                    <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sE8-nx-AUz">
                                        <rect key="frame" x="12" y="124" width="46" height="50"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                        <color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                        <state key="normal" title="rotate"/>
                                        <fontDescription key="fontDescription" name="Avenir-Book" family="Avenir" pointSize="13"/>
                                        <state key="normal" title="scale">
                                            <color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                        </state>
                                        <connections>
                                            <action selector="tapRotateWithSender:" destination="BYZ-38-t0r" eventType="touchUpInside" id="zgl-wu-xhE"/>
                                            <action selector="tapScaleWithSender:" destination="BYZ-38-t0r" eventType="touchUpInside" id="aSm-fj-ua1"/>
                                        </connections>
                                    </button>
                                    <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sE8-nx-AUz">
                                        <rect key="frame" x="12" y="124" width="46" height="50"/>
                                    <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FJk-X2-g4V">
                                        <rect key="frame" x="12" y="67" width="46" height="50"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                        <color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                        <state key="normal" title="scale"/>
                                        <fontDescription key="fontDescription" name="Avenir-Book" family="Avenir" pointSize="13"/>
                                        <state key="normal" title="rotate">
                                            <color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                        </state>
                                        <connections>
                                            <action selector="tapScaleWithSender:" destination="BYZ-38-t0r" eventType="touchUpInside" id="aSm-fj-ua1"/>
                                            <action selector="tapRotateWithSender:" destination="BYZ-38-t0r" eventType="touchUpInside" id="zgl-wu-xhE"/>
                                        </connections>
                                    </button>
                                </subviews>
                                <color key="backgroundColor" red="1" green="1" blue="1" alpha="0.10985640974234272" colorSpace="custom" customColorSpace="sRGB"/>
                                <color key="backgroundColor" red="0.19463895547269572" green="0.19463895547269572" blue="0.19463895547269572" alpha="0.10985640974234272" colorSpace="custom" customColorSpace="sRGB"/>
                            </view>
                            <tableView clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="Dgs-6t-2g8">
                            <tableView hidden="YES" clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="Dgs-6t-2g8">
                                <rect key="frame" x="507" y="20" width="240" height="166"/>
                                <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
                                <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                                <prototypes>
                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" id="fcW-Fu-mM1">
                                        <rect key="frame" x="0.0" y="24.5" width="240" height="43.5"/>
                                        <rect key="frame" x="0.0" y="44.5" width="240" height="43.5"/>
                                        <autoresizingMask key="autoresizingMask"/>
                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fcW-Fu-mM1" id="hNI-fd-oGU">
                                            <rect key="frame" x="0.0" y="0.0" width="240" height="43.5"/>


@@ 144,7 155,7 @@
                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" showsVerticalScrollIndicator="NO" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="aLa-b3-elT" userLabel="Distance Field Collection View">
                                <rect key="frame" x="20" y="912" width="728" height="92"/>
                                <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
                                <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.1352938216" colorSpace="custom" customColorSpace="sRGB"/>
                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="5" minimumInteritemSpacing="5" id="IaI-XG-Hd6">
                                    <size key="itemSize" width="70" height="70"/>
                                    <size key="estimatedItemSize" width="70" height="70"/>


@@ 181,40 192,14 @@
                                    <outlet property="delegate" destination="BYZ-38-t0r" id="D18-4B-ozz"/>
                                </connections>
                            </collectionView>
                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jgV-md-fVw">
                                <rect key="frame" x="231.5" y="420" width="305" height="184"/>
                                <subviews>
                                    <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rkc-6o-xgi">
                                        <rect key="frame" x="41" y="99" width="231" height="64"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                        <color key="backgroundColor" white="1" alpha="0.46688307195516349" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                        <state key="normal" title="Place"/>
                                        <connections>
                                            <action selector="didTapPlaceObject" destination="BYZ-38-t0r" eventType="touchUpInside" id="1FP-u3-zET"/>
                                        </connections>
                                    </button>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Point device at target placement" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="V3G-6b-XWf">
                                        <rect key="frame" x="8" y="45" width="297" height="21"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                        <fontDescription key="fontDescription" name="NotoSansKannada-Regular" family="Noto Sans Kannada" pointSize="16"/>
                                        <nil key="textColor"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                </subviews>
                                <color key="backgroundColor" white="1" alpha="0.20107264234530214" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                <constraints>
                                    <constraint firstAttribute="width" constant="305" id="fLg-VG-LWb"/>
                                    <constraint firstAttribute="height" constant="184" id="qCs-kz-7Af"/>
                                </constraints>
                            </view>
                            <view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Zlz-xE-rwN" customClass="TimerView" customModule="conemarch_ar" customModuleProvider="target">
                                <rect key="frame" x="590" y="867" width="70" height="37"/>
                                <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
                                <subviews>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="00:00" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="54b-ge-3j5">
                                        <rect key="frame" x="12" y="8" width="46" height="21"/>
                                        <rect key="frame" x="12" y="8" width="48" height="26"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                        <fontDescription key="fontDescription" name="Avenir-Book" family="Avenir" pointSize="19"/>
                                        <nil key="textColor"/>
                                        <nil key="highlightedColor"/>
                                    </label>


@@ 224,15 209,46 @@
                                    <outlet property="label" destination="54b-ge-3j5" id="JGo-Kx-B0H"/>
                                </connections>
                            </view>
                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="djy-mG-A8w">
                            <button hidden="YES" opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="djy-mG-A8w">
                                <rect key="frame" x="690" y="571" width="47" height="42"/>
                                <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
                                <color key="backgroundColor" red="0.35860558712121215" green="0.35860558712121215" blue="0.35860558712121215" alpha="0.46688307195516349" colorSpace="custom" customColorSpace="sRGB"/>
                                <state key="normal" title="CAM"/>
                                <state key="normal" title="CAM">
                                    <color key="titleColor" systemColor="labelColor"/>
                                </state>
                                <connections>
                                    <action selector="tapSwitchCameraWithSender:" destination="BYZ-38-t0r" eventType="touchUpInside" id="2aS-Y8-cG8"/>
                                </connections>
                            </button>
                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jgV-md-fVw">
                                <rect key="frame" x="231.5" y="430.5" width="305" height="163"/>
                                <subviews>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Point device at target" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="V3G-6b-XWf">
                                        <rect key="frame" x="8" y="40" width="297" height="22"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                        <fontDescription key="fontDescription" name="Avenir-Book" family="Avenir" pointSize="16"/>
                                        <nil key="textColor"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rkc-6o-xgi">
                                        <rect key="frame" x="41" y="91" width="231" height="64"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                        <color key="backgroundColor" red="0.98479846992877995" green="0.98479846992877995" blue="0.98479846992877995" alpha="0.46688307195516349" colorSpace="custom" customColorSpace="sRGB"/>
                                        <fontDescription key="fontDescription" name="Avenir-Book" family="Avenir" pointSize="14"/>
                                        <state key="normal" title="Place">
                                            <color key="titleColor" systemColor="labelColor"/>
                                        </state>
                                        <connections>
                                            <action selector="didTapPlaceObject" destination="BYZ-38-t0r" eventType="touchUpInside" id="1FP-u3-zET"/>
                                        </connections>
                                    </button>
                                </subviews>
                                <color key="backgroundColor" white="1" alpha="0.20107264234530214" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                <constraints>
                                    <constraint firstAttribute="width" constant="305" id="fLg-VG-LWb"/>
                                    <constraint firstAttribute="height" constant="163" id="qCs-kz-7Af"/>
                                </constraints>
                            </view>
                        </subviews>
                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>


@@ 261,6 277,9 @@
        </scene>
    </scenes>
    <resources>
        <systemColor name="labelColor">
            <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>

M conemarch-ar/UI/TimerView.swift => conemarch-ar/UI/TimerView.swift +1 -0
@@ 18,6 18,7 @@ class TimerView: UIView {
    
    func show(animated: Bool) {
        seconds = 0
        label?.text = "00:00"
        UIView.animate(withDuration: animated ? 0.5 : 0.0) {
            self.alpha = 1.0
        }

M conemarch-ar/ViewController.swift => conemarch-ar/ViewController.swift +17 -5
@@ 170,17 170,23 @@ extension ViewController: ARManagerDelegate {
        let distanceField = distanceFields[distanceFieldIndex]
        lastKnownOrientation = lastKnownOrientation.interfaceOrientationFromCurrentDevice
        
        guard let conemarchRender = conemarcher?.step(data: data, field: distanceField, functionIndex: distanceFieldIndex),
              let functionTable = conemarcher?.computePasses.last?.visibleFunctionTable else { return }
        
        guard let conemarchRender = conemarcher?.step(data: data, field: distanceField, functionIndex: distanceFieldIndex) else { return }
         
          // guard  let functionTable = conemarcher?.computePasses.first?.visibleFunctionTable else { return }
       
        /*
        let sceneKitTexture = sceneKitRenderer?.render(time: 0.0,
        let result = sceneKitRenderer?.render(time: 0.0,
                                                       cameraTransform: data.camera,
                                                       projectionMatrix: data.projectionMatrix,
                                                       viewport: self.view.bounds,
                                                       distanceFieldIndex: distanceFieldIndex,
                                                       distanceFieldFunctionTable: functionTable,
                                                       distanceFieldParameters: distanceField.parameters)
        */
        
        metalView?.debug = true
        metalView?.blit(arData: data, conemarchRender: result?.0!)
        return
         */
        
        if selectedDebugTextureIndex == -1 {
            metalView?.debug = false


@@ 257,6 263,7 @@ extension ViewController {
    @IBAction
    func didTapPlaceObject() {
        arManager?.processingCenterPixels = false
        arManager?.addAnchor()
        UIView.animate(withDuration: 0.5) {
            self.placeObjectView?.alpha = 0.0
        }


@@ 269,10 276,15 @@ extension ViewController {
            setView(view: postFxCollectionView!, hidden: true)
            distanceFieldCollectionView?.reloadData()
            distanceFieldCollectionView?.isHidden = false
            let distanceField = distanceFields[distanceFieldIndex]
            updateUniformStackView(uniforms: distanceField.uniforms)
        } else {
            setView(view: distanceFieldCollectionView!, hidden: true)
            setView(view: postFxCollectionView!, hidden: false)
            postFxCollectionView?.reloadData()
            if let uniform = metalView?.postEffects[metalView!.postFxFunctionIndex].uniform {
                updateUniformStackView(postFxUniform: uniform)
            }
        }
    }