Happy AR Painter
大綱
Introduction
Let's review what you'll be learning in this part of the course, and review the history of its inspiration, the legendary Bob Ross.
pointOfView and Matrix Math
So far, you’ve been writing apps that simply draw images over the real world. It’s now time to write apps that respond the to real world. Your first step: finding out where in the real world the device is, and which way its camera is facing.





Location, Orientation, and Position
取得鏡頭在3D座標系統的相對位置跟轉向
// MARK: ARSCNViewDelegate methods
// ===============================
// Called every time the augmented reality scene is about to be rendered
// (ideally, at least 60 times a second).
func renderer(_ renderer: SCNSceneRenderer,
willRenderScene scene: SCNScene,
atTime time: TimeInterval) {
// A canvas‘ pointOfView is a node representing the camera‘s point of view.
guard let pointOfView = canvas.pointOfView else { return }
// Get the device’s location, orientation, and position
// The current location and orientaton of the camera relative to the origin point
// are embedded in the pointOfView’s transformation matrix (a 4 by 4 matrix).
let transform = pointOfView.transform
// The orientation is in the 3rd column of the transform matrix.
let orientation = SCNVector3(-transform.m31,
-transform.m32,
-transform.m33)
// The location is in the 4th column of the transform matrix.
let location = SCNVector3(transform.m41,
transform.m42,
transform.m43)
let position = orientation + location
print("location: \(location)\norientation: \(orientation)")
}
The Other Tab
What’s with the other tab in this app? You’ll find out here.
Painting Animated Shapes With Actions
Now that you can determine the device’s position and orientation in the real world, you can use that information to leave a trail of virtual objects, effectively “painting” in 3D space.
加到print下方
// By putting this code in a “DispatchQueue.main.async” block,
// we ensure that this code gets executed in the main queue.
// We need to do this because we’re removing nodes from the scene
// and checking the state of the “Paint” button, both of which
// need to be done in the main queue.
DispatchQueue.main.async {
// Erase any old cursor shapes
self.eraseNodes(named: "cursor")
// Create the brush
let brush = self.createBrush(brushShape: self.brushSettings.shape,
brushSize: self.brushSettings.size,
position: position)
if self.paintButton.isHighlighted {
// The user IS pressing the “Paint” button
// Give the shape a shine and set it to the selected color
brush.geometry?.firstMaterial?.diffuse.contents = self.brushSettings.color
brush.geometry?.firstMaterial?.specular.contents = UIColor.white
if self.brushSettings.isSpinning {
// Spin the shape continuously around the y-axis
let rotateAction = SCNAction.rotate(by: 2 * .pi,
around: SCNVector3(0, 1, 0),
duration: 2)
let rotateForeverAction = SCNAction.repeatForever(rotateAction)
brush.runAction(rotateForeverAction)
}
} else {
// The user IS NOT pressing the “Paint” button
// Set the shape to the cursor color and name
brush.geometry?.firstMaterial?.diffuse.contents = UIColor.lightGray
brush.name = "cursor"
}
// Paint the shape to the screen
self.canvas.scene.rootNode.addChildNode(brush)
}
// MARK: Node creation methods
// ===========================
// Erase any nodes with the given name.
func eraseNodes(named nameToErase: String) {
self.canvas.scene.rootNode.enumerateChildNodes { (node, _) in
if node.name == nameToErase {
node.removeFromParentNode()
}
}
}
Painting Animated Shapes With Actions
Your “paint” trails are SceneKit objects, and SceneKit objects can have SceneKit actions applied to them. Let’s liven them up by animating them.
Conclusion
You’ve just written a 3D painting app! Let's review where you are and see what comes next.
Last updated
Was this helpful?