SlideShare ist ein Scribd-Unternehmen logo
1 von 76
Downloaden Sie, um offline zu lesen
Enhance your world
with ARKit
an introduction
1
!
2
Marius Constantinescu
@marius_const
Greener Pastures
iOS Goodies
3
!
4
https://www.facebook.com/nixcraft/photos/a.431194973560553.114666.126000117413375/1856451711034865/?type=3&theater 5
What is Augmented Reality?
6
https://developer.apple.com/documentation/arkit/about_augmented_reality_and_arkit 7
8
What is ARKit?
9
Why I think it's huge?
10
from the WWDC 2017 Keynote 11
What does ARKit do?
• tracking
• scene understanding
• plane detection
• hit test
• light estimate
• rendering support for SpriteKit, SceneKit and Metal
12
How does it do it?
• Visual Inertial Odometry system
• AVFoundation
• CoreMotion
13
How do we start?
14
15
16
17
Let's look at the code a bit
18
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
// Create a new scene
let scene = SCNScene(named: "art.scnassets/ship.scn")!
// Set the scene to the view
sceneView.scene = scene
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
}
}
19
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
// Create a new scene
let scene = SCNScene(named: "art.scnassets/ship.scn")!
// Set the scene to the view
sceneView.scene = scene
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
}
}
20
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
// Create a new scene
let scene = SCNScene(named: "art.scnassets/ship.scn")!
// Set the scene to the view
sceneView.scene = scene
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
}
}
21
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
// Show statistics such as fps and timing information
sceneView.showsStatistics = true
// Create a new scene
let scene = SCNScene(named: "art.scnassets/ship.scn")!
// Set the scene to the view
sceneView.scene = scene
}
22
// Implement to create and configure nodes for anchors added to the view's session.
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode?
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor)
func renderer(_ renderer: SCNSceneRenderer, willUpdate node: SCNNode, for anchor: ARAnchor)
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor)
func renderer(_ renderer: SCNSceneRenderer, didRemove node: SCNNode, for anchor: ARAnchor)
23
Let's take some snapshots
24
//...
let tgr = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(gestureRecognizer:)))
view.addGestureRecognizer(tgr)
//...
@objc func handleTap(gestureRecognizer: UITapGestureRecognizer) {
guard let currentFrame = sceneView.session.currentFrame else { return }
// Take a snapshot of the view
let imagePlane = SCNPlane(width: sceneView.bounds.width/6000, height: sceneView.bounds.height/6000)
imagePlane.firstMaterial?.diffuse.contents = sceneView.snapshot()
imagePlane.firstMaterial?.lightingModel = .constant
// Add the snapshot to the scene
let planeNode = SCNNode(geometry: imagePlane)
sceneView.scene.rootNode.addChildNode(planeNode)
// Move the snapshot 10 cm in fromt of the user
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.1
planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)
}
25
//...
let tgr = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(gestureRecognizer:)))
view.addGestureRecognizer(tgr)
//...
@objc func handleTap(gestureRecognizer: UITapGestureRecognizer) {
guard let currentFrame = sceneView.session.currentFrame else { return }
// Take a snapshot of the view
let imagePlane = SCNPlane(width: sceneView.bounds.width/6000, height: sceneView.bounds.height/6000)
imagePlane.firstMaterial?.diffuse.contents = sceneView.snapshot()
imagePlane.firstMaterial?.lightingModel = .constant
// Add the snapshot to the scene
let planeNode = SCNNode(geometry: imagePlane)
sceneView.scene.rootNode.addChildNode(planeNode)
// Move the snapshot 10 cm in fromt of the user
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.1
planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)
}
26
//...
let tgr = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(gestureRecognizer:)))
view.addGestureRecognizer(tgr)
//...
@objc func handleTap(gestureRecognizer: UITapGestureRecognizer) {
guard let currentFrame = sceneView.session.currentFrame else { return }
// Take a snapshot of the view
let imagePlane = SCNPlane(width: sceneView.bounds.width/6000, height: sceneView.bounds.height/6000)
imagePlane.firstMaterial?.diffuse.contents = sceneView.snapshot()
imagePlane.firstMaterial?.lightingModel = .constant
// Add the snapshot to the scene
let planeNode = SCNNode(geometry: imagePlane)
sceneView.scene.rootNode.addChildNode(planeNode)
// Move the snapshot 10 cm in fromt of the user
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.1
planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)
}
27
//...
let tgr = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(gestureRecognizer:)))
view.addGestureRecognizer(tgr)
//...
@objc func handleTap(gestureRecognizer: UITapGestureRecognizer) {
guard let currentFrame = sceneView.session.currentFrame else { return }
// Take a snapshot of the view
let imagePlane = SCNPlane(width: sceneView.bounds.width/6000, height: sceneView.bounds.height/6000)
imagePlane.firstMaterial?.diffuse.contents = sceneView.snapshot()
imagePlane.firstMaterial?.lightingModel = .constant
// Add the snapshot to the scene
let planeNode = SCNNode(geometry: imagePlane)
sceneView.scene.rootNode.addChildNode(planeNode)
// Move the snapshot 10 cm in fromt of the user
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.1
planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)
}
28
//...
let tgr = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(gestureRecognizer:)))
view.addGestureRecognizer(tgr)
//...
@objc func handleTap(gestureRecognizer: UITapGestureRecognizer) {
guard let currentFrame = sceneView.session.currentFrame else { return }
// Take a snapshot of the view
let imagePlane = SCNPlane(width: sceneView.bounds.width/6000, height: sceneView.bounds.height/6000)
imagePlane.firstMaterial?.diffuse.contents = sceneView.snapshot()
imagePlane.firstMaterial?.lightingModel = .constant
// Add the snapshot to the scene
let planeNode = SCNNode(geometry: imagePlane)
sceneView.scene.rootNode.addChildNode(planeNode)
// Move the snapshot 10 cm in fromt of the user
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.1
planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)
}
29
30
Let's see how it detects planes
31
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
sceneView.session.run(configuration)
32
33
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Place content only for anchors found by plane detection.
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// Create a SceneKit plane to visualize the plane anchor using its position and extent.
let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)
/*
`SCNPlane` is vertically oriented in its local coordinate space, so
rotate the plane to match the horizontal orientation of `ARPlaneAnchor`.
*/
planeNode.eulerAngles.x = -.pi / 2
// Make the plane visualization semitransparent to clearly show real-world placement.
planeNode.opacity = 0.55
plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles")
/*
Add the plane visualization to the ARKit-managed node so that it tracks
changes in the plane anchor as plane estimation continues.
*/
node.addChildNode(planeNode)
}
34
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Place content only for anchors found by plane detection.
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// Create a SceneKit plane to visualize the plane anchor using its position and extent.
let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)
/*
`SCNPlane` is vertically oriented in its local coordinate space, so
rotate the plane to match the horizontal orientation of `ARPlaneAnchor`.
*/
planeNode.eulerAngles.x = -.pi / 2
// Make the plane visualization semitransparent to clearly show real-world placement.
planeNode.opacity = 0.55
plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles")
/*
Add the plane visualization to the ARKit-managed node so that it tracks
changes in the plane anchor as plane estimation continues.
*/
node.addChildNode(planeNode)
}
35
https://developer.apple.com/documentation/scenekit/organizing_a_scene_with_nodes 36
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Place content only for anchors found by plane detection.
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// Create a SceneKit plane to visualize the plane anchor using its position and extent.
let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)
/*
`SCNPlane` is vertically oriented in its local coordinate space, so
rotate the plane to match the horizontal orientation of `ARPlaneAnchor`.
*/
planeNode.eulerAngles.x = -.pi / 2
// Make the plane visualization semitransparent to clearly show real-world placement.
planeNode.opacity = 0.55
plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles")
/*
Add the plane visualization to the ARKit-managed node so that it tracks
changes in the plane anchor as plane estimation continues.
*/
node.addChildNode(planeNode)
}
37
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Place content only for anchors found by plane detection.
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// Create a SceneKit plane to visualize the plane anchor using its position and extent.
let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)
/*
`SCNPlane` is vertically oriented in its local coordinate space, so
rotate the plane to match the horizontal orientation of `ARPlaneAnchor`.
*/
planeNode.eulerAngles.x = -.pi / 2
// Make the plane visualization semitransparent to clearly show real-world placement.
planeNode.opacity = 0.55
plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles")
/*
Add the plane visualization to the ARKit-managed node so that it tracks
changes in the plane anchor as plane estimation continues.
*/
node.addChildNode(planeNode)
}
38
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Place content only for anchors found by plane detection.
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// Create a SceneKit plane to visualize the plane anchor using its position and extent.
let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)
/*
`SCNPlane` is vertically oriented in its local coordinate space, so
rotate the plane to match the horizontal orientation of `ARPlaneAnchor`.
*/
planeNode.eulerAngles.x = -.pi / 2
// Make the plane visualization semitransparent to clearly show real-world placement.
planeNode.opacity = 0.55
plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles")
/*
Add the plane visualization to the ARKit-managed node so that it tracks
changes in the plane anchor as plane estimation continues.
*/
node.addChildNode(planeNode)
}
39
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Place content only for anchors found by plane detection.
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// Create a SceneKit plane to visualize the plane anchor using its position and extent.
let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)
/*
`SCNPlane` is vertically oriented in its local coordinate space, so
rotate the plane to match the horizontal orientation of `ARPlaneAnchor`.
*/
planeNode.eulerAngles.x = -.pi / 2
// Make the plane visualization semitransparent to clearly show real-world placement.
planeNode.opacity = 0.55
plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles")
/*
Add the plane visualization to the ARKit-managed node so that it tracks
changes in the plane anchor as plane estimation continues.
*/
node.addChildNode(planeNode)
}
40
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
// Update content only for plane anchors and nodes matching the setup created in `renderer(_:didAdd:for:)`.
guard let planeAnchor = anchor as? ARPlaneAnchor,
let planeNode = node.childNodes.first,
let plane = planeNode.geometry as? SCNPlane
else { return }
// Plane estimation may shift the center of a plane relative to its anchor's transform.
planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)
/*
Plane estimation may extend the size of the plane, or combine previously detected
planes into a larger one. In the latter case, `ARSCNView` automatically deletes the
corresponding node for one plane, then calls this method to update the size of
the remaining plane.
*/
plane.width = CGFloat(planeAnchor.extent.x)
plane.height = CGFloat(planeAnchor.extent.z)
}
41
42
Let's add an object on a plane
43
class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate {
@IBOutlet weak var sceneView: ARSCNView!
var lampScene = SCNScene()
var lamp: SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene = lampScene
let node = SCNReferenceNode(url: Bundle.main.url(forResource: "Models.scnassets/lamp/lamp.scn", withExtension: nil)!)!
node.load()
lampScene.rootNode.addChildNode(node)
node.isHidden = true
lamp = node
let tgr = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
view.addGestureRecognizer(tgr)
}
44
class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate {
@IBOutlet weak var sceneView: ARSCNView!
var lampScene = SCNScene()
var lamp: SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene = lampScene
let node = SCNReferenceNode(url: Bundle.main.url(forResource: "Models.scnassets/lamp/lamp.scn", withExtension: nil)!)!
node.load()
lampScene.rootNode.addChildNode(node)
node.isHidden = true
lamp = node
let tgr = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
view.addGestureRecognizer(tgr)
}
45
class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate {
@IBOutlet weak var sceneView: ARSCNView!
var lampScene = SCNScene()
var lamp: SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene = lampScene
let node = SCNReferenceNode(url: Bundle.main.url(forResource: "Models.scnassets/lamp/lamp.scn", withExtension: nil)!)!
node.load()
lampScene.rootNode.addChildNode(node)
node.isHidden = true
lamp = node
let tgr = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
view.addGestureRecognizer(tgr)
}
46
class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate {
@IBOutlet weak var sceneView: ARSCNView!
var lampScene = SCNScene()
var lamp: SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene = lampScene
let node = SCNReferenceNode(url: Bundle.main.url(forResource: "Models.scnassets/lamp/lamp.scn", withExtension: nil)!)!
node.load()
lampScene.rootNode.addChildNode(node)
node.isHidden = true
lamp = node
let tgr = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
view.addGestureRecognizer(tgr)
}
47
class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate {
@IBOutlet weak var sceneView: ARSCNView!
var lampScene = SCNScene()
var lamp: SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene = lampScene
let node = SCNReferenceNode(url: Bundle.main.url(forResource: "Models.scnassets/lamp/lamp.scn", withExtension: nil)!)!
node.load()
lampScene.rootNode.addChildNode(node)
node.isHidden = true
lamp = node
let tgr = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
view.addGestureRecognizer(tgr)
}
48
@IBAction func didTap(_ recognizer: UITapGestureRecognizer) {
let location = recognizer.location(in: sceneView)
// When tapped on a plane, reposition the content
let arHitTestResult = sceneView.hitTest(location, types: .existingPlane)
if !arHitTestResult.isEmpty {
let hit = arHitTestResult.first!
lamp.simdTransform = hit.worldTransform
if lamp.isHidden {
lamp.isHidden = false
}
}
}
49
@IBAction func didTap(_ recognizer: UITapGestureRecognizer) {
let location = recognizer.location(in: sceneView)
// When tapped on a plane, reposition the content
let arHitTestResult = sceneView.hitTest(location, types: .existingPlane)
if !arHitTestResult.isEmpty {
let hit = arHitTestResult.first!
lamp.simdTransform = hit.worldTransform
if lamp.isHidden {
lamp.isHidden = false
}
}
}
50
@IBAction func didTap(_ recognizer: UITapGestureRecognizer) {
let location = recognizer.location(in: sceneView)
// When tapped on a plane, reposition the content
let arHitTestResult = sceneView.hitTest(location, types: .existingPlane)
if !arHitTestResult.isEmpty {
let hit = arHitTestResult.first!
lamp.simdTransform = hit.worldTransform
if lamp.isHidden {
lamp.isHidden = false
}
}
}
51
@IBAction func didTap(_ recognizer: UITapGestureRecognizer) {
let location = recognizer.location(in: sceneView)
// When tapped on a plane, reposition the content
let arHitTestResult = sceneView.hitTest(location, types: .existingPlane)
if !arHitTestResult.isEmpty {
let hit = arHitTestResult.first!
lamp.simdTransform = hit.worldTransform
if lamp.isHidden {
lamp.isHidden = false
}
}
}
52
@IBAction func didTap(_ recognizer: UITapGestureRecognizer) {
let location = recognizer.location(in: sceneView)
// When tapped on a plane, reposition the content
let arHitTestResult = sceneView.hitTest(location, types: .existingPlane)
if !arHitTestResult.isEmpty {
let hit = arHitTestResult.first!
lamp.simdTransform = hit.worldTransform
if lamp.isHidden {
lamp.isHidden = false
}
}
}
53
@IBAction func didTap(_ recognizer: UITapGestureRecognizer) {
let location = recognizer.location(in: sceneView)
// When tapped on a plane, reposition the content
let arHitTestResult = sceneView.hitTest(location, types: .existingPlane)
if !arHitTestResult.isEmpty {
let hit = arHitTestResult.first!
lamp.simdTransform = hit.worldTransform
if lamp.isHidden {
lamp.isHidden = false
}
}
}
54
55
Let's move it around
56
let pgr = UIPanGestureRecognizer(target: self, action: #selector(didPan(_:)))
view.addGestureRecognizer(pgr)
57
@IBAction func didPan(_ recognizer: UIPanGestureRecognizer) {
let location = recognizer.location(in: sceneView)
// Drag the object on an infinite plane
let arHitTestResult = sceneView.hitTest(location, types: .existingPlane)
if !arHitTestResult.isEmpty {
let hit = arHitTestResult.first!
lamp.simdTransform = hit.worldTransform
}
}
58
59
Usecases
60
IKEA Place
61
Conduct AR
62
ARKit-CoreLocation
https://github.com/ProjectDent/ARKit-CoreLocation
63
Measurements
64
Starting from the basic plane detection example
var measurementNodes: [SCNNode] = []
65
@IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) {
//make a hit test to see where we should place it
let point = gestureRecognizer.location(in: self.view)
if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first {
let radius: CGFloat = 0.01
let sphere = SCNSphere(radius:radius)
sphere.firstMaterial?.diffuse.contents = UIColor.green
let newNode = SCNNode(geometry: sphere)
//if we already have 2 or more nodes, we start a new measurement
if measurementNodes.count >= 2 {
clearMeasurement()
}
//we add the measuring point to the scene
measurementNodes.append(newNode)
self.sceneView.scene.rootNode.addChildNode(newNode)
newNode.simdTransform = hit.worldTransform
//if we have two measuring nodes here, we draw a line and show the measuring result
if measurementNodes.count == 2 {
computeDistance()
}
}
}
66
@IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) {
//make a hit test to see where we should place it
let point = gestureRecognizer.location(in: self.view)
if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first {
let radius: CGFloat = 0.01
let sphere = SCNSphere(radius:radius)
sphere.firstMaterial?.diffuse.contents = UIColor.green
let newNode = SCNNode(geometry: sphere)
//if we already have 2 or more nodes, we start a new measurement
if measurementNodes.count >= 2 {
clearMeasurement()
}
//we add the measuring point to the scene
measurementNodes.append(newNode)
self.sceneView.scene.rootNode.addChildNode(newNode)
newNode.simdTransform = hit.worldTransform
//if we have two measuring nodes here, we draw a line and show the measuring result
if measurementNodes.count == 2 {
computeDistance()
}
}
}
67
@IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) {
//make a hit test to see where we should place it
let point = gestureRecognizer.location(in: self.view)
if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first {
let radius: CGFloat = 0.01
let sphere = SCNSphere(radius:radius)
sphere.firstMaterial?.diffuse.contents = UIColor.green
let newNode = SCNNode(geometry: sphere)
//if we already have 2 or more nodes, we start a new measurement
if measurementNodes.count >= 2 {
clearMeasurement()
}
//we add the measuring point to the scene
measurementNodes.append(newNode)
self.sceneView.scene.rootNode.addChildNode(newNode)
newNode.simdTransform = hit.worldTransform
//if we have two measuring nodes here, we draw a line and show the measuring result
if measurementNodes.count == 2 {
computeDistance()
}
}
}
68
@IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) {
//make a hit test to see where we should place it
let point = gestureRecognizer.location(in: self.view)
if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first {
let radius: CGFloat = 0.01
let sphere = SCNSphere(radius:radius)
sphere.firstMaterial?.diffuse.contents = UIColor.green
let newNode = SCNNode(geometry: sphere)
//if we already have 2 or more nodes, we start a new measurement
if measurementNodes.count >= 2 {
clearMeasurement()
}
//we add the measuring point to the scene
measurementNodes.append(newNode)
self.sceneView.scene.rootNode.addChildNode(newNode)
newNode.simdTransform = hit.worldTransform
//if we have two measuring nodes here, we draw a line and show the measuring result
if measurementNodes.count == 2 {
computeDistance()
}
}
}
69
@IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) {
//make a hit test to see where we should place it
let point = gestureRecognizer.location(in: self.view)
if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first {
let radius: CGFloat = 0.01
let sphere = SCNSphere(radius:radius)
sphere.firstMaterial?.diffuse.contents = UIColor.green
let newNode = SCNNode(geometry: sphere)
//if we already have 2 or more nodes, we start a new measurement
if measurementNodes.count >= 2 {
clearMeasurement()
}
//we add the measuring point to the scene
measurementNodes.append(newNode)
self.sceneView.scene.rootNode.addChildNode(newNode)
newNode.simdTransform = hit.worldTransform
//if we have two measuring nodes here, we draw a line and show the measuring result
if measurementNodes.count == 2 {
computeDistance()
}
}
}
70
@IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) {
//make a hit test to see where we should place it
let point = gestureRecognizer.location(in: self.view)
if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first {
let radius: CGFloat = 0.01
let sphere = SCNSphere(radius:radius)
sphere.firstMaterial?.diffuse.contents = UIColor.green
let newNode = SCNNode(geometry: sphere)
//if we already have 2 or more nodes, we start a new measurement
if measurementNodes.count >= 2 {
clearMeasurement()
}
//we add the measuring point to the scene
measurementNodes.append(newNode)
self.sceneView.scene.rootNode.addChildNode(newNode)
newNode.simdTransform = hit.worldTransform
//if we have two measuring nodes here, we draw a line and show the measuring result
if measurementNodes.count == 2 {
computeDistance()
}
}
}
71
72
Things you should know
• Plane detection only for horizontal planes
• Take care of your users
• Add arkit to UIRequiredDeviceCapabilities
73
What I learned
• Lighting is super important
• Being familiar with SceneKit is a huge advantage
• Don't expect the same result every time
74
!
75
Questions?
76

Weitere ähnliche Inhalte

Was ist angesagt?

アプリを弄ってみる #2 #antama_ws
アプリを弄ってみる #2 #antama_wsアプリを弄ってみる #2 #antama_ws
アプリを弄ってみる #2 #antama_ws
Takahiro Yoshimura
 

Was ist angesagt? (20)

libGDX: Scene2D
libGDX: Scene2DlibGDX: Scene2D
libGDX: Scene2D
 
Shootting Game
Shootting GameShootting Game
Shootting Game
 
アプリを弄ってみる #2 #antama_ws
アプリを弄ってみる #2 #antama_wsアプリを弄ってみる #2 #antama_ws
アプリを弄ってみる #2 #antama_ws
 
Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...
Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...
Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...
 
Converting Scene Data to DOTS – Unite Copenhagen 2019
Converting Scene Data to DOTS – Unite Copenhagen 2019Converting Scene Data to DOTS – Unite Copenhagen 2019
Converting Scene Data to DOTS – Unite Copenhagen 2019
 
Using Android Things to Detect & Exterminate Reptilians
Using Android Things to Detect & Exterminate ReptiliansUsing Android Things to Detect & Exterminate Reptilians
Using Android Things to Detect & Exterminate Reptilians
 
Keeping Track of Moving Things: MapKit and CoreLocation in Depth
Keeping Track of Moving Things: MapKit and CoreLocation in DepthKeeping Track of Moving Things: MapKit and CoreLocation in Depth
Keeping Track of Moving Things: MapKit and CoreLocation in Depth
 
Technical Deep Dive into the New Prefab System
Technical Deep Dive into the New Prefab SystemTechnical Deep Dive into the New Prefab System
Technical Deep Dive into the New Prefab System
 
node.js and the AR.Drone: building a real-time dashboard using socket.io
node.js and the AR.Drone: building a real-time dashboard using socket.ionode.js and the AR.Drone: building a real-time dashboard using socket.io
node.js and the AR.Drone: building a real-time dashboard using socket.io
 
Advanced programming with #nodecopter
Advanced programming with #nodecopterAdvanced programming with #nodecopter
Advanced programming with #nodecopter
 
Drones, Flying robots and Javascript
Drones, Flying robots and JavascriptDrones, Flying robots and Javascript
Drones, Flying robots and Javascript
 
HoloLens Programming Tutorial: AirTap & Spatial Mapping
HoloLens Programming Tutorial: AirTap & Spatial MappingHoloLens Programming Tutorial: AirTap & Spatial Mapping
HoloLens Programming Tutorial: AirTap & Spatial Mapping
 
Ujug07presentation
Ujug07presentationUjug07presentation
Ujug07presentation
 
Augmented Reality in JavaScript
Augmented Reality in JavaScriptAugmented Reality in JavaScript
Augmented Reality in JavaScript
 
A split screen-viable UI event system - Unite Copenhagen 2019
A split screen-viable UI event system - Unite Copenhagen 2019A split screen-viable UI event system - Unite Copenhagen 2019
A split screen-viable UI event system - Unite Copenhagen 2019
 
Introduction to Game Programming Tutorial
Introduction to Game Programming TutorialIntroduction to Game Programming Tutorial
Introduction to Game Programming Tutorial
 
iOS Training Session-3
iOS Training Session-3iOS Training Session-3
iOS Training Session-3
 
The Ring programming language version 1.8 book - Part 65 of 202
The Ring programming language version 1.8 book - Part 65 of 202The Ring programming language version 1.8 book - Part 65 of 202
The Ring programming language version 1.8 book - Part 65 of 202
 
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureSOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean Architecture
 
Getting Oriented with MapKit: Everything you need to get started with the new...
Getting Oriented with MapKit: Everything you need to get started with the new...Getting Oriented with MapKit: Everything you need to get started with the new...
Getting Oriented with MapKit: Everything you need to get started with the new...
 

Ähnlich wie Enhance your world with ARKit. UA Mobile 2017.

Webgl para JavaScripters
Webgl para JavaScriptersWebgl para JavaScripters
Webgl para JavaScripters
gerbille
 
Rotoscope inthebrowserppt billy
Rotoscope inthebrowserppt billyRotoscope inthebrowserppt billy
Rotoscope inthebrowserppt billy
nimbleltd
 
Introduction to open gl in android droidcon - slides
Introduction to open gl in android   droidcon - slidesIntroduction to open gl in android   droidcon - slides
Introduction to open gl in android droidcon - slides
tamillarasan
 
Breizhcamp Rennes 2011
Breizhcamp Rennes 2011Breizhcamp Rennes 2011
Breizhcamp Rennes 2011
sekond0
 
ArcGIS Server Tips and Tricks, MAC-URISA2010
ArcGIS Server Tips and Tricks, MAC-URISA2010ArcGIS Server Tips and Tricks, MAC-URISA2010
ArcGIS Server Tips and Tricks, MAC-URISA2010
Brian
 

Ähnlich wie Enhance your world with ARKit. UA Mobile 2017. (20)

Useful Tools for Making Video Games - XNA (2008)
Useful Tools for Making Video Games - XNA (2008)Useful Tools for Making Video Games - XNA (2008)
Useful Tools for Making Video Games - XNA (2008)
 
Webgl para JavaScripters
Webgl para JavaScriptersWebgl para JavaScripters
Webgl para JavaScripters
 
Leaving Flatland: getting started with WebGL
Leaving Flatland: getting started with WebGLLeaving Flatland: getting started with WebGL
Leaving Flatland: getting started with WebGL
 
Augmented reality in web rtc browser
Augmented reality in web rtc browserAugmented reality in web rtc browser
Augmented reality in web rtc browser
 
Game development with Cocos2d
Game development with Cocos2dGame development with Cocos2d
Game development with Cocos2d
 
Rotoscope inthebrowserppt billy
Rotoscope inthebrowserppt billyRotoscope inthebrowserppt billy
Rotoscope inthebrowserppt billy
 
Custom SRP and graphics workflows - Unite Copenhagen 2019
Custom SRP and graphics workflows - Unite Copenhagen 2019Custom SRP and graphics workflows - Unite Copenhagen 2019
Custom SRP and graphics workflows - Unite Copenhagen 2019
 
Synchronizing without internet - Multipeer Connectivity (iOS)
Synchronizing without internet - Multipeer Connectivity (iOS)Synchronizing without internet - Multipeer Connectivity (iOS)
Synchronizing without internet - Multipeer Connectivity (iOS)
 
Introduction to open gl in android droidcon - slides
Introduction to open gl in android   droidcon - slidesIntroduction to open gl in android   droidcon - slides
Introduction to open gl in android droidcon - slides
 
Enhancing UI/UX using Java animations
Enhancing UI/UX using Java animationsEnhancing UI/UX using Java animations
Enhancing UI/UX using Java animations
 
Breizhcamp Rennes 2011
Breizhcamp Rennes 2011Breizhcamp Rennes 2011
Breizhcamp Rennes 2011
 
Universal JavaScript - Frontend United Athens 2017
Universal JavaScript - Frontend United Athens 2017Universal JavaScript - Frontend United Athens 2017
Universal JavaScript - Frontend United Athens 2017
 
Giovanni Laquidara - Hello ARCore - Codemotion Milan 2017
Giovanni Laquidara - Hello ARCore - Codemotion Milan 2017Giovanni Laquidara - Hello ARCore - Codemotion Milan 2017
Giovanni Laquidara - Hello ARCore - Codemotion Milan 2017
 
Workshop 25: React Native - Components
Workshop 25: React Native - ComponentsWorkshop 25: React Native - Components
Workshop 25: React Native - Components
 
Migrating Objective-C to Swift
Migrating Objective-C to SwiftMigrating Objective-C to Swift
Migrating Objective-C to Swift
 
Getting Started in VR with JS
Getting Started in VR with JSGetting Started in VR with JS
Getting Started in VR with JS
 
ArcGIS Server Tips and Tricks, MAC-URISA2010
ArcGIS Server Tips and Tricks, MAC-URISA2010ArcGIS Server Tips and Tricks, MAC-URISA2010
ArcGIS Server Tips and Tricks, MAC-URISA2010
 
Games 3 dl4-example
Games 3 dl4-exampleGames 3 dl4-example
Games 3 dl4-example
 
Philipp Nagele (CTO, Wikitude) An Insider Deep-Dive into the Wikitude SDK
Philipp Nagele (CTO, Wikitude) An Insider Deep-Dive into the Wikitude SDK Philipp Nagele (CTO, Wikitude) An Insider Deep-Dive into the Wikitude SDK
Philipp Nagele (CTO, Wikitude) An Insider Deep-Dive into the Wikitude SDK
 
Side effects-con-redux
Side effects-con-reduxSide effects-con-redux
Side effects-con-redux
 

Mehr von UA Mobile

Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
Декларативное программирование клиент-серверных приложений на андроид - UA Mo...Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
UA Mobile
 
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
UA Mobile
 
Історія декількох проектів та що в них пішло не так - UA Mobile 2019
Історія декількох проектів та що в них пішло не так - UA Mobile 2019Історія декількох проектів та що в них пішло не так - UA Mobile 2019
Історія декількох проектів та що в них пішло не так - UA Mobile 2019
UA Mobile
 
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
UA Mobile
 
До чого прикладати Docker в Android? - UA Mobile 2019
До чого прикладати Docker в Android? - UA Mobile 2019До чого прикладати Docker в Android? - UA Mobile 2019
До чого прикладати Docker в Android? - UA Mobile 2019
UA Mobile
 
Optional. Tips and Tricks - UA Mobile 2019
Optional. Tips and Tricks - UA Mobile 2019Optional. Tips and Tricks - UA Mobile 2019
Optional. Tips and Tricks - UA Mobile 2019
UA Mobile
 
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
UA Mobile
 
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
UA Mobile
 

Mehr von UA Mobile (20)

Designing iOS+Android project without using multiplatform frameworks - UA Mob...
Designing iOS+Android project without using multiplatform frameworks - UA Mob...Designing iOS+Android project without using multiplatform frameworks - UA Mob...
Designing iOS+Android project without using multiplatform frameworks - UA Mob...
 
Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
Декларативное программирование клиент-серверных приложений на андроид - UA Mo...Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
 
Leave your Room behind - UA Mobile 2019
Leave your Room behind - UA Mobile 2019Leave your Room behind - UA Mobile 2019
Leave your Room behind - UA Mobile 2019
 
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
 
Google Wear OS watch faces and applications development - UA Mobile 2019
Google Wear OS watch faces and applications development - UA Mobile 2019Google Wear OS watch faces and applications development - UA Mobile 2019
Google Wear OS watch faces and applications development - UA Mobile 2019
 
Історія декількох проектів та що в них пішло не так - UA Mobile 2019
Історія декількох проектів та що в них пішло не так - UA Mobile 2019Історія декількох проектів та що в них пішло не так - UA Mobile 2019
Історія декількох проектів та що в них пішло не так - UA Mobile 2019
 
Working effectively with ViewModels and TDD - UA Mobile 2019
Working effectively with ViewModels and TDD - UA Mobile 2019Working effectively with ViewModels and TDD - UA Mobile 2019
Working effectively with ViewModels and TDD - UA Mobile 2019
 
Managing State in Reactive applications - UA Mobile 2019
Managing State in Reactive applications - UA Mobile 2019Managing State in Reactive applications - UA Mobile 2019
Managing State in Reactive applications - UA Mobile 2019
 
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
 
Актуальні практики дизайну мобільних додатків - UA Mobile 2019
Актуальні практики дизайну мобільних додатків - UA Mobile 2019Актуальні практики дизайну мобільних додатків - UA Mobile 2019
Актуальні практики дизайну мобільних додатків - UA Mobile 2019
 
До чого прикладати Docker в Android? - UA Mobile 2019
До чого прикладати Docker в Android? - UA Mobile 2019До чого прикладати Docker в Android? - UA Mobile 2019
До чого прикладати Docker в Android? - UA Mobile 2019
 
Building your Flutter apps using Redux - UA Mobile 2019
Building your Flutter apps using Redux - UA Mobile 2019Building your Flutter apps using Redux - UA Mobile 2019
Building your Flutter apps using Redux - UA Mobile 2019
 
Optional. Tips and Tricks - UA Mobile 2019
Optional. Tips and Tricks - UA Mobile 2019Optional. Tips and Tricks - UA Mobile 2019
Optional. Tips and Tricks - UA Mobile 2019
 
Designing iOS+Android project without using multiplatform frameworks - UA Mob...
Designing iOS+Android project without using multiplatform frameworks - UA Mob...Designing iOS+Android project without using multiplatform frameworks - UA Mob...
Designing iOS+Android project without using multiplatform frameworks - UA Mob...
 
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
 
Flutter: No more boring apps! - UA Mobile 2019
Flutter: No more boring apps! - UA Mobile 2019Flutter: No more boring apps! - UA Mobile 2019
Flutter: No more boring apps! - UA Mobile 2019
 
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
 
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
 
Sceneform SDK на практиці - UA Mobile 2019
Sceneform SDK на практиці - UA Mobile 2019Sceneform SDK на практиці - UA Mobile 2019
Sceneform SDK на практиці - UA Mobile 2019
 
Coroutines in Kotlin. UA Mobile 2017.
Coroutines in Kotlin. UA Mobile 2017.Coroutines in Kotlin. UA Mobile 2017.
Coroutines in Kotlin. UA Mobile 2017.
 

Enhance your world with ARKit. UA Mobile 2017.

  • 1. Enhance your world with ARKit an introduction 1
  • 2. ! 2
  • 4. ! 4
  • 6. What is Augmented Reality? 6
  • 8. 8
  • 10. Why I think it's huge? 10
  • 11. from the WWDC 2017 Keynote 11
  • 12. What does ARKit do? • tracking • scene understanding • plane detection • hit test • light estimate • rendering support for SpriteKit, SceneKit and Metal 12
  • 13. How does it do it? • Visual Inertial Odometry system • AVFoundation • CoreMotion 13
  • 14. How do we start? 14
  • 15. 15
  • 16. 16
  • 17. 17
  • 18. Let's look at the code a bit 18
  • 19. class ViewController: UIViewController, ARSCNViewDelegate { @IBOutlet var sceneView: ARSCNView! override func viewDidLoad() { super.viewDidLoad() // Create a new scene let scene = SCNScene(named: "art.scnassets/ship.scn")! // Set the scene to the view sceneView.scene = scene } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // Create a session configuration let configuration = ARWorldTrackingConfiguration() // Run the view's session sceneView.session.run(configuration) } } 19
  • 20. class ViewController: UIViewController, ARSCNViewDelegate { @IBOutlet var sceneView: ARSCNView! override func viewDidLoad() { super.viewDidLoad() // Create a new scene let scene = SCNScene(named: "art.scnassets/ship.scn")! // Set the scene to the view sceneView.scene = scene } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // Create a session configuration let configuration = ARWorldTrackingConfiguration() // Run the view's session sceneView.session.run(configuration) } } 20
  • 21. class ViewController: UIViewController, ARSCNViewDelegate { @IBOutlet var sceneView: ARSCNView! override func viewDidLoad() { super.viewDidLoad() // Create a new scene let scene = SCNScene(named: "art.scnassets/ship.scn")! // Set the scene to the view sceneView.scene = scene } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // Create a session configuration let configuration = ARWorldTrackingConfiguration() // Run the view's session sceneView.session.run(configuration) } } 21
  • 22. override func viewDidLoad() { super.viewDidLoad() // Set the view's delegate sceneView.delegate = self // Show statistics such as fps and timing information sceneView.showsStatistics = true // Create a new scene let scene = SCNScene(named: "art.scnassets/ship.scn")! // Set the scene to the view sceneView.scene = scene } 22
  • 23. // Implement to create and configure nodes for anchors added to the view's session. func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) func renderer(_ renderer: SCNSceneRenderer, willUpdate node: SCNNode, for anchor: ARAnchor) func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) func renderer(_ renderer: SCNSceneRenderer, didRemove node: SCNNode, for anchor: ARAnchor) 23
  • 24. Let's take some snapshots 24
  • 25. //... let tgr = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(gestureRecognizer:))) view.addGestureRecognizer(tgr) //... @objc func handleTap(gestureRecognizer: UITapGestureRecognizer) { guard let currentFrame = sceneView.session.currentFrame else { return } // Take a snapshot of the view let imagePlane = SCNPlane(width: sceneView.bounds.width/6000, height: sceneView.bounds.height/6000) imagePlane.firstMaterial?.diffuse.contents = sceneView.snapshot() imagePlane.firstMaterial?.lightingModel = .constant // Add the snapshot to the scene let planeNode = SCNNode(geometry: imagePlane) sceneView.scene.rootNode.addChildNode(planeNode) // Move the snapshot 10 cm in fromt of the user var translation = matrix_identity_float4x4 translation.columns.3.z = -0.1 planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation) } 25
  • 26. //... let tgr = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(gestureRecognizer:))) view.addGestureRecognizer(tgr) //... @objc func handleTap(gestureRecognizer: UITapGestureRecognizer) { guard let currentFrame = sceneView.session.currentFrame else { return } // Take a snapshot of the view let imagePlane = SCNPlane(width: sceneView.bounds.width/6000, height: sceneView.bounds.height/6000) imagePlane.firstMaterial?.diffuse.contents = sceneView.snapshot() imagePlane.firstMaterial?.lightingModel = .constant // Add the snapshot to the scene let planeNode = SCNNode(geometry: imagePlane) sceneView.scene.rootNode.addChildNode(planeNode) // Move the snapshot 10 cm in fromt of the user var translation = matrix_identity_float4x4 translation.columns.3.z = -0.1 planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation) } 26
  • 27. //... let tgr = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(gestureRecognizer:))) view.addGestureRecognizer(tgr) //... @objc func handleTap(gestureRecognizer: UITapGestureRecognizer) { guard let currentFrame = sceneView.session.currentFrame else { return } // Take a snapshot of the view let imagePlane = SCNPlane(width: sceneView.bounds.width/6000, height: sceneView.bounds.height/6000) imagePlane.firstMaterial?.diffuse.contents = sceneView.snapshot() imagePlane.firstMaterial?.lightingModel = .constant // Add the snapshot to the scene let planeNode = SCNNode(geometry: imagePlane) sceneView.scene.rootNode.addChildNode(planeNode) // Move the snapshot 10 cm in fromt of the user var translation = matrix_identity_float4x4 translation.columns.3.z = -0.1 planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation) } 27
  • 28. //... let tgr = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(gestureRecognizer:))) view.addGestureRecognizer(tgr) //... @objc func handleTap(gestureRecognizer: UITapGestureRecognizer) { guard let currentFrame = sceneView.session.currentFrame else { return } // Take a snapshot of the view let imagePlane = SCNPlane(width: sceneView.bounds.width/6000, height: sceneView.bounds.height/6000) imagePlane.firstMaterial?.diffuse.contents = sceneView.snapshot() imagePlane.firstMaterial?.lightingModel = .constant // Add the snapshot to the scene let planeNode = SCNNode(geometry: imagePlane) sceneView.scene.rootNode.addChildNode(planeNode) // Move the snapshot 10 cm in fromt of the user var translation = matrix_identity_float4x4 translation.columns.3.z = -0.1 planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation) } 28
  • 29. //... let tgr = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(gestureRecognizer:))) view.addGestureRecognizer(tgr) //... @objc func handleTap(gestureRecognizer: UITapGestureRecognizer) { guard let currentFrame = sceneView.session.currentFrame else { return } // Take a snapshot of the view let imagePlane = SCNPlane(width: sceneView.bounds.width/6000, height: sceneView.bounds.height/6000) imagePlane.firstMaterial?.diffuse.contents = sceneView.snapshot() imagePlane.firstMaterial?.lightingModel = .constant // Add the snapshot to the scene let planeNode = SCNNode(geometry: imagePlane) sceneView.scene.rootNode.addChildNode(planeNode) // Move the snapshot 10 cm in fromt of the user var translation = matrix_identity_float4x4 translation.columns.3.z = -0.1 planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation) } 29
  • 30. 30
  • 31. Let's see how it detects planes 31
  • 32. let configuration = ARWorldTrackingConfiguration() configuration.planeDetection = .horizontal sceneView.session.run(configuration) 32
  • 33. 33
  • 34. func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { // Place content only for anchors found by plane detection. guard let planeAnchor = anchor as? ARPlaneAnchor else { return } // Create a SceneKit plane to visualize the plane anchor using its position and extent. let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z)) let planeNode = SCNNode(geometry: plane) planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z) /* `SCNPlane` is vertically oriented in its local coordinate space, so rotate the plane to match the horizontal orientation of `ARPlaneAnchor`. */ planeNode.eulerAngles.x = -.pi / 2 // Make the plane visualization semitransparent to clearly show real-world placement. planeNode.opacity = 0.55 plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles") /* Add the plane visualization to the ARKit-managed node so that it tracks changes in the plane anchor as plane estimation continues. */ node.addChildNode(planeNode) } 34
  • 35. func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { // Place content only for anchors found by plane detection. guard let planeAnchor = anchor as? ARPlaneAnchor else { return } // Create a SceneKit plane to visualize the plane anchor using its position and extent. let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z)) let planeNode = SCNNode(geometry: plane) planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z) /* `SCNPlane` is vertically oriented in its local coordinate space, so rotate the plane to match the horizontal orientation of `ARPlaneAnchor`. */ planeNode.eulerAngles.x = -.pi / 2 // Make the plane visualization semitransparent to clearly show real-world placement. planeNode.opacity = 0.55 plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles") /* Add the plane visualization to the ARKit-managed node so that it tracks changes in the plane anchor as plane estimation continues. */ node.addChildNode(planeNode) } 35
  • 37. func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { // Place content only for anchors found by plane detection. guard let planeAnchor = anchor as? ARPlaneAnchor else { return } // Create a SceneKit plane to visualize the plane anchor using its position and extent. let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z)) let planeNode = SCNNode(geometry: plane) planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z) /* `SCNPlane` is vertically oriented in its local coordinate space, so rotate the plane to match the horizontal orientation of `ARPlaneAnchor`. */ planeNode.eulerAngles.x = -.pi / 2 // Make the plane visualization semitransparent to clearly show real-world placement. planeNode.opacity = 0.55 plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles") /* Add the plane visualization to the ARKit-managed node so that it tracks changes in the plane anchor as plane estimation continues. */ node.addChildNode(planeNode) } 37
  • 38. func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { // Place content only for anchors found by plane detection. guard let planeAnchor = anchor as? ARPlaneAnchor else { return } // Create a SceneKit plane to visualize the plane anchor using its position and extent. let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z)) let planeNode = SCNNode(geometry: plane) planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z) /* `SCNPlane` is vertically oriented in its local coordinate space, so rotate the plane to match the horizontal orientation of `ARPlaneAnchor`. */ planeNode.eulerAngles.x = -.pi / 2 // Make the plane visualization semitransparent to clearly show real-world placement. planeNode.opacity = 0.55 plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles") /* Add the plane visualization to the ARKit-managed node so that it tracks changes in the plane anchor as plane estimation continues. */ node.addChildNode(planeNode) } 38
  • 39. func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { // Place content only for anchors found by plane detection. guard let planeAnchor = anchor as? ARPlaneAnchor else { return } // Create a SceneKit plane to visualize the plane anchor using its position and extent. let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z)) let planeNode = SCNNode(geometry: plane) planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z) /* `SCNPlane` is vertically oriented in its local coordinate space, so rotate the plane to match the horizontal orientation of `ARPlaneAnchor`. */ planeNode.eulerAngles.x = -.pi / 2 // Make the plane visualization semitransparent to clearly show real-world placement. planeNode.opacity = 0.55 plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles") /* Add the plane visualization to the ARKit-managed node so that it tracks changes in the plane anchor as plane estimation continues. */ node.addChildNode(planeNode) } 39
  • 40. func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { // Place content only for anchors found by plane detection. guard let planeAnchor = anchor as? ARPlaneAnchor else { return } // Create a SceneKit plane to visualize the plane anchor using its position and extent. let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z)) let planeNode = SCNNode(geometry: plane) planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z) /* `SCNPlane` is vertically oriented in its local coordinate space, so rotate the plane to match the horizontal orientation of `ARPlaneAnchor`. */ planeNode.eulerAngles.x = -.pi / 2 // Make the plane visualization semitransparent to clearly show real-world placement. planeNode.opacity = 0.55 plane.firstMaterial?.diffuse.contents = #imageLiteral(resourceName: "tiles") /* Add the plane visualization to the ARKit-managed node so that it tracks changes in the plane anchor as plane estimation continues. */ node.addChildNode(planeNode) } 40
  • 41. func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { // Update content only for plane anchors and nodes matching the setup created in `renderer(_:didAdd:for:)`. guard let planeAnchor = anchor as? ARPlaneAnchor, let planeNode = node.childNodes.first, let plane = planeNode.geometry as? SCNPlane else { return } // Plane estimation may shift the center of a plane relative to its anchor's transform. planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z) /* Plane estimation may extend the size of the plane, or combine previously detected planes into a larger one. In the latter case, `ARSCNView` automatically deletes the corresponding node for one plane, then calls this method to update the size of the remaining plane. */ plane.width = CGFloat(planeAnchor.extent.x) plane.height = CGFloat(planeAnchor.extent.z) } 41
  • 42. 42
  • 43. Let's add an object on a plane 43
  • 44. class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate { @IBOutlet weak var sceneView: ARSCNView! var lampScene = SCNScene() var lamp: SCNNode! override func viewDidLoad() { super.viewDidLoad() sceneView.scene = lampScene let node = SCNReferenceNode(url: Bundle.main.url(forResource: "Models.scnassets/lamp/lamp.scn", withExtension: nil)!)! node.load() lampScene.rootNode.addChildNode(node) node.isHidden = true lamp = node let tgr = UITapGestureRecognizer(target: self, action: #selector(didTap(_:))) view.addGestureRecognizer(tgr) } 44
  • 45. class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate { @IBOutlet weak var sceneView: ARSCNView! var lampScene = SCNScene() var lamp: SCNNode! override func viewDidLoad() { super.viewDidLoad() sceneView.scene = lampScene let node = SCNReferenceNode(url: Bundle.main.url(forResource: "Models.scnassets/lamp/lamp.scn", withExtension: nil)!)! node.load() lampScene.rootNode.addChildNode(node) node.isHidden = true lamp = node let tgr = UITapGestureRecognizer(target: self, action: #selector(didTap(_:))) view.addGestureRecognizer(tgr) } 45
  • 46. class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate { @IBOutlet weak var sceneView: ARSCNView! var lampScene = SCNScene() var lamp: SCNNode! override func viewDidLoad() { super.viewDidLoad() sceneView.scene = lampScene let node = SCNReferenceNode(url: Bundle.main.url(forResource: "Models.scnassets/lamp/lamp.scn", withExtension: nil)!)! node.load() lampScene.rootNode.addChildNode(node) node.isHidden = true lamp = node let tgr = UITapGestureRecognizer(target: self, action: #selector(didTap(_:))) view.addGestureRecognizer(tgr) } 46
  • 47. class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate { @IBOutlet weak var sceneView: ARSCNView! var lampScene = SCNScene() var lamp: SCNNode! override func viewDidLoad() { super.viewDidLoad() sceneView.scene = lampScene let node = SCNReferenceNode(url: Bundle.main.url(forResource: "Models.scnassets/lamp/lamp.scn", withExtension: nil)!)! node.load() lampScene.rootNode.addChildNode(node) node.isHidden = true lamp = node let tgr = UITapGestureRecognizer(target: self, action: #selector(didTap(_:))) view.addGestureRecognizer(tgr) } 47
  • 48. class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate { @IBOutlet weak var sceneView: ARSCNView! var lampScene = SCNScene() var lamp: SCNNode! override func viewDidLoad() { super.viewDidLoad() sceneView.scene = lampScene let node = SCNReferenceNode(url: Bundle.main.url(forResource: "Models.scnassets/lamp/lamp.scn", withExtension: nil)!)! node.load() lampScene.rootNode.addChildNode(node) node.isHidden = true lamp = node let tgr = UITapGestureRecognizer(target: self, action: #selector(didTap(_:))) view.addGestureRecognizer(tgr) } 48
  • 49. @IBAction func didTap(_ recognizer: UITapGestureRecognizer) { let location = recognizer.location(in: sceneView) // When tapped on a plane, reposition the content let arHitTestResult = sceneView.hitTest(location, types: .existingPlane) if !arHitTestResult.isEmpty { let hit = arHitTestResult.first! lamp.simdTransform = hit.worldTransform if lamp.isHidden { lamp.isHidden = false } } } 49
  • 50. @IBAction func didTap(_ recognizer: UITapGestureRecognizer) { let location = recognizer.location(in: sceneView) // When tapped on a plane, reposition the content let arHitTestResult = sceneView.hitTest(location, types: .existingPlane) if !arHitTestResult.isEmpty { let hit = arHitTestResult.first! lamp.simdTransform = hit.worldTransform if lamp.isHidden { lamp.isHidden = false } } } 50
  • 51. @IBAction func didTap(_ recognizer: UITapGestureRecognizer) { let location = recognizer.location(in: sceneView) // When tapped on a plane, reposition the content let arHitTestResult = sceneView.hitTest(location, types: .existingPlane) if !arHitTestResult.isEmpty { let hit = arHitTestResult.first! lamp.simdTransform = hit.worldTransform if lamp.isHidden { lamp.isHidden = false } } } 51
  • 52. @IBAction func didTap(_ recognizer: UITapGestureRecognizer) { let location = recognizer.location(in: sceneView) // When tapped on a plane, reposition the content let arHitTestResult = sceneView.hitTest(location, types: .existingPlane) if !arHitTestResult.isEmpty { let hit = arHitTestResult.first! lamp.simdTransform = hit.worldTransform if lamp.isHidden { lamp.isHidden = false } } } 52
  • 53. @IBAction func didTap(_ recognizer: UITapGestureRecognizer) { let location = recognizer.location(in: sceneView) // When tapped on a plane, reposition the content let arHitTestResult = sceneView.hitTest(location, types: .existingPlane) if !arHitTestResult.isEmpty { let hit = arHitTestResult.first! lamp.simdTransform = hit.worldTransform if lamp.isHidden { lamp.isHidden = false } } } 53
  • 54. @IBAction func didTap(_ recognizer: UITapGestureRecognizer) { let location = recognizer.location(in: sceneView) // When tapped on a plane, reposition the content let arHitTestResult = sceneView.hitTest(location, types: .existingPlane) if !arHitTestResult.isEmpty { let hit = arHitTestResult.first! lamp.simdTransform = hit.worldTransform if lamp.isHidden { lamp.isHidden = false } } } 54
  • 55. 55
  • 56. Let's move it around 56
  • 57. let pgr = UIPanGestureRecognizer(target: self, action: #selector(didPan(_:))) view.addGestureRecognizer(pgr) 57
  • 58. @IBAction func didPan(_ recognizer: UIPanGestureRecognizer) { let location = recognizer.location(in: sceneView) // Drag the object on an infinite plane let arHitTestResult = sceneView.hitTest(location, types: .existingPlane) if !arHitTestResult.isEmpty { let hit = arHitTestResult.first! lamp.simdTransform = hit.worldTransform } } 58
  • 59. 59
  • 65. Starting from the basic plane detection example var measurementNodes: [SCNNode] = [] 65
  • 66. @IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) { //make a hit test to see where we should place it let point = gestureRecognizer.location(in: self.view) if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first { let radius: CGFloat = 0.01 let sphere = SCNSphere(radius:radius) sphere.firstMaterial?.diffuse.contents = UIColor.green let newNode = SCNNode(geometry: sphere) //if we already have 2 or more nodes, we start a new measurement if measurementNodes.count >= 2 { clearMeasurement() } //we add the measuring point to the scene measurementNodes.append(newNode) self.sceneView.scene.rootNode.addChildNode(newNode) newNode.simdTransform = hit.worldTransform //if we have two measuring nodes here, we draw a line and show the measuring result if measurementNodes.count == 2 { computeDistance() } } } 66
  • 67. @IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) { //make a hit test to see where we should place it let point = gestureRecognizer.location(in: self.view) if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first { let radius: CGFloat = 0.01 let sphere = SCNSphere(radius:radius) sphere.firstMaterial?.diffuse.contents = UIColor.green let newNode = SCNNode(geometry: sphere) //if we already have 2 or more nodes, we start a new measurement if measurementNodes.count >= 2 { clearMeasurement() } //we add the measuring point to the scene measurementNodes.append(newNode) self.sceneView.scene.rootNode.addChildNode(newNode) newNode.simdTransform = hit.worldTransform //if we have two measuring nodes here, we draw a line and show the measuring result if measurementNodes.count == 2 { computeDistance() } } } 67
  • 68. @IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) { //make a hit test to see where we should place it let point = gestureRecognizer.location(in: self.view) if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first { let radius: CGFloat = 0.01 let sphere = SCNSphere(radius:radius) sphere.firstMaterial?.diffuse.contents = UIColor.green let newNode = SCNNode(geometry: sphere) //if we already have 2 or more nodes, we start a new measurement if measurementNodes.count >= 2 { clearMeasurement() } //we add the measuring point to the scene measurementNodes.append(newNode) self.sceneView.scene.rootNode.addChildNode(newNode) newNode.simdTransform = hit.worldTransform //if we have two measuring nodes here, we draw a line and show the measuring result if measurementNodes.count == 2 { computeDistance() } } } 68
  • 69. @IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) { //make a hit test to see where we should place it let point = gestureRecognizer.location(in: self.view) if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first { let radius: CGFloat = 0.01 let sphere = SCNSphere(radius:radius) sphere.firstMaterial?.diffuse.contents = UIColor.green let newNode = SCNNode(geometry: sphere) //if we already have 2 or more nodes, we start a new measurement if measurementNodes.count >= 2 { clearMeasurement() } //we add the measuring point to the scene measurementNodes.append(newNode) self.sceneView.scene.rootNode.addChildNode(newNode) newNode.simdTransform = hit.worldTransform //if we have two measuring nodes here, we draw a line and show the measuring result if measurementNodes.count == 2 { computeDistance() } } } 69
  • 70. @IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) { //make a hit test to see where we should place it let point = gestureRecognizer.location(in: self.view) if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first { let radius: CGFloat = 0.01 let sphere = SCNSphere(radius:radius) sphere.firstMaterial?.diffuse.contents = UIColor.green let newNode = SCNNode(geometry: sphere) //if we already have 2 or more nodes, we start a new measurement if measurementNodes.count >= 2 { clearMeasurement() } //we add the measuring point to the scene measurementNodes.append(newNode) self.sceneView.scene.rootNode.addChildNode(newNode) newNode.simdTransform = hit.worldTransform //if we have two measuring nodes here, we draw a line and show the measuring result if measurementNodes.count == 2 { computeDistance() } } } 70
  • 71. @IBAction func didTap(gestureRecognizer: UITapGestureRecognizer) { //make a hit test to see where we should place it let point = gestureRecognizer.location(in: self.view) if let hit = sceneView.hitTest(point, types: [.existingPlaneUsingExtent]).first { let radius: CGFloat = 0.01 let sphere = SCNSphere(radius:radius) sphere.firstMaterial?.diffuse.contents = UIColor.green let newNode = SCNNode(geometry: sphere) //if we already have 2 or more nodes, we start a new measurement if measurementNodes.count >= 2 { clearMeasurement() } //we add the measuring point to the scene measurementNodes.append(newNode) self.sceneView.scene.rootNode.addChildNode(newNode) newNode.simdTransform = hit.worldTransform //if we have two measuring nodes here, we draw a line and show the measuring result if measurementNodes.count == 2 { computeDistance() } } } 71
  • 72. 72
  • 73. Things you should know • Plane detection only for horizontal planes • Take care of your users • Add arkit to UIRequiredDeviceCapabilities 73
  • 74. What I learned • Lighting is super important • Being familiar with SceneKit is a huge advantage • Don't expect the same result every time 74
  • 75. ! 75