~connorbell/ARSDFView

ef627342a2d64f9d5cd1b06fcee5d07beedba409 — Connor Bell 1 year, 2 months ago 1cf3e26 master
Updated some comments and added readme+license
M AR-SDF-Renderer/AR-SDF-Renderer.xcodeproj/project.pbxproj => AR-SDF-Renderer/AR-SDF-Renderer.xcodeproj/project.pbxproj +4 -0
@@ 66,6 66,8 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
		B9948946287F7369008B703D /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
		B9948949287F7369008B703D /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
		B9D8708F27C0185500775F31 /* AR-SDF-Renderer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AR-SDF-Renderer.app"; sourceTree = BUILT_PRODUCTS_DIR; };
		B9D8709227C0185500775F31 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
		B9D8709427C0185500775F31 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };


@@ 111,6 113,8 @@
		B9D8708627C0185500775F31 = {
			isa = PBXGroup;
			children = (
				B9948949287F7369008B703D /* LICENSE */,
				B9948946287F7369008B703D /* README.md */,
				B9D8714727C058CA00775F31 /* AR-SDF.xcodeproj */,
				B9D8709127C0185500775F31 /* AR-SDF-Renderer */,
				B9D870A827C0185800775F31 /* AR-SDF-RendererTests */,

M AR-SDF-Renderer/AR-SDF-Renderer.xcodeproj/project.xcworkspace/xcuserdata/connorbell.xcuserdatad/UserInterfaceState.xcuserstate => AR-SDF-Renderer/AR-SDF-Renderer.xcodeproj/project.xcworkspace/xcuserdata/connorbell.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
M AR-SDF-Renderer/AR-SDF-Renderer.xcodeproj/xcuserdata/connorbell.xcuserdatad/xcschemes/xcschememanagement.plist => AR-SDF-Renderer/AR-SDF-Renderer.xcodeproj/xcuserdata/connorbell.xcuserdatad/xcschemes/xcschememanagement.plist +1 -1
@@ 7,7 7,7 @@
		<key>AR-SDF-Renderer.xcscheme_^#shared#^_</key>
		<dict>
			<key>orderHint</key>
			<integer>0</integer>
			<integer>1</integer>
		</dict>
	</dict>
</dict>

M AR-SDF-Renderer/AR-SDF-Renderer/ViewController.swift => AR-SDF-Renderer/AR-SDF-Renderer/ViewController.swift +1 -1
@@ 18,7 18,7 @@ class ViewController: UIViewController {
                                            range: CGSize(width: 0.1, height: 5.0),
                                            value: simd_float3(0.5, 0.5, 0.5))
        
        let distanceField = DistanceField(kernelName: "tunnel",
        let distanceField = DistanceField(kernelName: "cube",
                                          uniforms: [cubeSizeUniform])
        
        arSDFView = ARSDFView(frame: self.view.frame)

M AR-SDF/AR-SDF.xcodeproj/xcuserdata/connorbell.xcuserdatad/xcschemes/xcschememanagement.plist => AR-SDF/AR-SDF.xcodeproj/xcuserdata/connorbell.xcuserdatad/xcschemes/xcschememanagement.plist +1 -1
@@ 7,7 7,7 @@
		<key>AR-SDF.xcscheme_^#shared#^_</key>
		<dict>
			<key>orderHint</key>
			<integer>1</integer>
			<integer>0</integer>
		</dict>
	</dict>
</dict>

M AR-SDF/AR-SDF/ARSDFView.swift => AR-SDF/AR-SDF/ARSDFView.swift +6 -1
@@ 14,8 14,13 @@ public class ARSDFView: UIView {
    /// Access the last known AR Data
    public var lastARData: ARData?
    
    /// Manages ARSession and returns ARData each frame via ARManagerDelegate
    public var arManager: ARManager?
    
    /// Multi-pass raymarcher
    public var conemarcher: Conemarcher?
    
    /// The target view
    public var metalView: MetalView?
    
    /// Update this to render a distance field (class must have already called start() with the assigned distance field included.


@@ 38,7 43,7 @@ public class ARSDFView: UIView {
        super.init(coder: coder)
    }
    
    /// Begin rendering distance fields. The first one will be by default. Any distance fields intended to render should be passed as well in order to create the function table.
    /// Begin rendering distance fields. distanceFields[0] will be the default. Any distance fields intended to render should be passed as well in order to create the function table.
    public func start(distanceFields: [DistanceField]) {
        lastKnownOrientation = lastKnownOrientation.interfaceOrientationFromCurrentDevice
        

M AR-SDF/AR-SDF/Conemarcher.swift => AR-SDF/AR-SDF/Conemarcher.swift +1 -1
@@ 111,7 111,7 @@ public class Conemarcher {
        registerExecutionTimeCallbacks()
    }
    
    /// Returns a texture with the rendered SDF composed with the AR Data
    /// Returns a texture with the rendered SDF composed with the ARData
    func step(data: ARData, field: DistanceField, functionIndex: Int) -> MTLTexture? {
        
        guard let time = rendererDelegate?.rendererDidRequestTime() else { return nil }

M AR-SDF/AR-SDF/Shaders/DistanceFields.metal => AR-SDF/AR-SDF/Shaders/DistanceFields.metal +1 -1
@@ 74,7 74,7 @@ float gasket(float3 p, float time, DistanceFieldParams params) {
}

[[visible]]
float tunnel(float3 pos, float time, DistanceFieldParams params) {
float cube(float3 pos, float time, DistanceFieldParams params) {
    float3 spaceSize = float3(0.25);
    //pos.xy = pR(pos.xy, pos.z*0.1);
    float3 p = mod(pos, spaceSize*2.0) - spaceSize;

A LICENSE => LICENSE +1 -0
@@ 0,0 1,1 @@
Attribution-NonCommercial 3.0 Unported (CC BY-NC 3.0)

A README.md => README.md +60 -0
@@ 0,0 1,60 @@
#  ARSDFView 

iOS Framework that renders signed distance fields in Augmented Reality defined by Metal Function Pointers via a multi-pass "conemarching" algorithm.

Sample project included. An earlier prototype with lots of fun fractal stuff exists [at this repo](https://git.sr.ht/~connorbell/conemarching-ar) before I refactored into a framework. 

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

##  Example

```swift
    var arSDFView: ARSDFView?
    
    override func viewDidLoad() {        
        let cubeSizeUniform = Float3Uniform(name: "Cube Size",
                                            range: CGSize(width: 0.1, height: 5.0),
                                            value: simd_float3(0.5, 0.5, 0.5))
        
        let distanceField = DistanceField(kernelName: "cube",
                                          uniforms: [cubeSizeUniform])
        
        arSDFView = ARSDFView(frame: self.view.frame)
        
        self.view.addSubview(arSDFView!)
        arSDFView?.start(distanceFields: [distanceField])

        super.viewDidLoad()
    }
```

##  Creating a New Distance Field

Define a Metal function with the [[visible]] attribute that returns a float and has input parameters: (float3 position, float time, DistanceFieldParams params). 

Optional: In Swift, define uniforms. Available types are FloatUniform, Float2Uniform, Float3Uniform and Float4Uniform.

Create a DistanceField object with the kernel name and an optional array of uniforms (max 4 at the moment). 

##  Device Support

The device compatibility with Metal Function Pointers is unclear. It runs on iPad Pro (3rd gen) but not on iPhone 12, so I suspect it's limited to M1 devices. This limitation can be removed by making the function tables optional (future work).

##  Engine Architecture

ARSDFView conforms to ARManagerDelegate which ARManager fufuils each frame with ARData containing rgb color, smoothed depth, and the view+projection matrices. This data is passed 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.

Internally, ARSDFView uses Metal Function Pointers, which allows the ability to write a single raymarch algorithm that is agnostic to the distance field it renders. The advantages of abstracting the renderer over writing in each shader is invaluable to code duplication. This does create restrictions however with input uniforms being a fixed struct.

##  Acknowledgements

Conemarching technique developed by fulcrum demo team
http://www.fulcrum-demo.org/wp-content/uploads/2012/04/Cone_Marching_Mandelbox_by_Seven_Fulcrum_LongVersion.pdf

## License

Attribution-NonCommercial 3.0 Unported (CC BY-NC 3.0)