XÂY DỰNG ỨNG DỤNG VỚI ARKit iOS.

Augmented reality(AR) là gì ?

Trần Tuấn An
7 min readNov 29, 2017

Augmented reality hay thực tế gia tăng là một công nghệ kết hợp giữa đồ hoạ và thực tế để tạo ra những ứng dụng , hình ảnh sinh động. Kể từ khi Apple cho ra mắt ARKit việc tạo ra những ứng dụng AR đã trở lên dễ dàng hơn bao giờ hết cho lập trình viên . Bài viết này sẽ giới thiệu cách cơ bản việc tạo 1 ứng dụng AR với ARKit và Swift .

Yêu cầu

Để có thể tạo ra những ứng dụng AR với ARKit bạn cần có Xcode 9 hoặc cao hơn và thiết bị device thật có bộ xử lý Apple A9 trở lên .

Hãy bắt đầu luôn viết những dòng code để xây dựng ứng dụng nào !!!

. Tạo 1 ứng dụng mới cho ARKit apps

. Thiết lập cài đặt cho ARKit SceneKit View

. Kết nối ARSCNView với ViewController

. Tạo ra liên kết IBOutlet.

. Configuring ARSCNView Session

. Xin cấp quyền truy cập Camera

. Thêm 1 đối tượng 3D vào ARSCNView

. Thêm sự kiện Gesture Recognizer vào ARSCNView

. Loại bỏ đối tượng 3D khỏi ARSCNView

. Thêm nhiều đối tượng vào ARSCNView

Tạo 1 project mới

Trong menu Xcode chọn File > New > Project…. Chọn SingleView App và ấn Next .

Đặt tên cho project , bạn có thể đặt bất cứ tên gì , trong ví dụ này mình sẽ đặt là ARKitDemo.

Thiết lập ARKit SceneKit View

Mở Main.storyboard sau đó chọn phần Object Library tiếp theo chọn ARKit SceneKit View. Kéo ARKit SceneKit View vào ViewController của bạn .

Chọn đối tượng ARKitSceneKit

Thiết lập constraints để ARScene tràn hết màn hình của bạn . Kết quả sẽ như thế này .

Vậy là đã hoàn thành 1/3 việc tạo lập rồi , tiếp tục nào !

Kết nối với ViewController

Mở class ViewController và import ARKit

Giữ và kéo ARKit SceneKit View vào ViewController.swift file. Liên kết sẽ hiện thị IBOutlet sceneView

Configuring ARSCNView Session

Apple đã tạo ra ARKit để hỗ trợ lập trình viên trong việc hiện thị những đối tượng 3D xen kẽ trong hiện tại thực mà không cần phải quan tâm tới toàn bộ công nghệ của quá trình hình thành đối tượng.

Bước đầu tiên là định hình lại ARScence bằng 1 ARWorldTrackingConfiguration

override func viewWillAppear(_ animated: Bool) {

super.viewWillAppear(animated)

let configuration = ARWorldTrackingConfiguration()

sceneView.session.run(configuration)

}

Trong hàmviewWillAppear(_:) , Khời taọARWorldTrackingConfiguration. Đây chính là việc cấu hình cho việc chạy tracking liên tục của ARKit.

“World tracking provides 6 degrees of freedom tracking of the device. By finding feature points in the scene, world tracking enables performing hit-tests against the frame.
Tracking can no longer be resumed once the session is paused.”
— Apple’s Documentation

Bước cấu hình trên cũng giúp cho việc định hình các bề mặt thực tế dễ dàng hơn .

Trong classViewController:

override func viewWillDisappear(_ animated: Bool) {

super.viewWillDisappear(animated)

sceneView.session.pause()

}

Trong hàmviewWillDisappear(_:) mình sẽ thiết lập để việc tracking dừng lại khi view biến mất.

Xin cấp quyền truy cập Camera

Thêm 1 đối tượng 3D vào ARSCNView .

Bên trong class ViewController thêm 1 hàm : addBox()

func addBox() {

let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)

let boxNode = SCNNode()

boxNode.geometry = box

boxNode.position = SCNVector3(0, 0, -0.2)

let scene = SCNScene()

scene.rootNode.addChildNode(boxNode)

sceneView.scene = scene

}

Mình sẽ tạo ra 1 chiếc hộp , quy ước 1 Float = 1 meter.

Ở đây với mỗi đối tượng được tạo ra , chúng ta sử dùng 1 SCNode để định hình dạng của đối tượng , sau đó thêm node này vào scence .

Gọi hàm addBox() trongviewDidLoad():

override func viewDidLoad() {

super.viewDidLoad()

addBox()

}

Bạn cũng có thể viết hàm addBox() 1 cách đơn giản hơn :

func addBox() {

let box = SCNBox(width: 0.05, height: 0.05, length: 0.05, chamferRadius: 0)

let boxNode = SCNNode()

boxNode.geometry = box

boxNode.position = SCNVector3(0, 0, -0.2)

sceneView.scene.rootNode.addChildNode(boxNode)

}

Thêm sự kiện Gesture Recognizer vào ARSCNView

Tạo ra hàm addTapGestureToSceneView

func addTapGestureToSceneView{

let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.didTap(withGestureRecognizer:)))

sceneView.addGestureRecognizer(tapGestureRecognizer)}

Removing Object From ARSCNView

TrongViewController.swift file , thêm 1 hàm :

@objc func didTap(withGestureRecognizer recognizer: UIGestureRecognizer) {

let tapLocation = recognizer.location(in: sceneView)

let hitTestResults = sceneView.hitTest(tapLocation)

guard let node = hitTestResults.first?.node else { return }

node.removeFromParentNode()

}

Here, we created a didTap(withGestureRecognizer:) method. We retrieve the user’s tap location relative to the sceneView and hit test to see if we tap onto any node(s).

Afterward, we safely unwrap the first node from our hitTestResults. If the result does contain at least a node, we will remove the first node we tapped on from its parent node.

Trước khi chúng ta bắt tay vào test việc remove đối tượng , cập nhập lại hàm viewDidLoad() gọi hàm addTapGestureToSceneView() phía trong hàm này :

override func viewDidLoad() {

super.viewDidLoad()

addBox()

addTapGestureToSceneView()

}

Chạy thử project nào , bây giờ bạn có thể chạm vào vị trí nào đó để loại bỏ đối tượng 3D mà mình vừa thêm vào.

Sau khi kiểm tra xong thì bắt tay vào việc thêm nhiều đối tượng 3D thôi

Looks like we are back to ground one.

Ok. Now it’s time to add multiple objects.

Adding Multiple Objects to ARSCNView

Chúng ta sẽ tiếp cận với khái niệm : “feature points”

Vậy feature points là cái gì ?

A point automatically identified by ARKit as part of a continuous surface, but without a corresponding anchor.

Phần này mình viết tiếng anh nha tại hơi khó diễn tả ! :3:3

It is basically the detected points on the surface of real world objects. So back to the implementation of adding boxes. Before we do that, let’s create an extension at the end of the ViewController class:

extension float4x4 {

var translation: float3 {

let translation = self.columns.3

return float3(translation.x, translation.y, translation.z)

}

}

This extension basically transforms a matrix into float3. It gives us the x, y, and z from the matrix.

Also, we need to modify addBox() to the following:

func addBox(x: Float = 0, y: Float = 0, z: Float = -0.2) {

let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)

let boxNode = SCNNode()

boxNode.geometry = box

boxNode.position = SCNVector3(x, y, z)

sceneView.scene.rootNode.addChildNode(boxNode)

}

We basically added parameters to the initial addBox() function. We also gave it default parameter values. This means that we can call addBox() without having to specify the x, y, and z coordinates like in viewDidLoad().

Cool.

Now we need to modify the didTap(withGestureRecognizer:) method. We want to add an object to the feature point if there is one that can be detected.

So inside of our guard let statement and before our return statement. Add the following code:

let hitTestResultsWithFeaturePoints = sceneView.hitTest(tapLocation, types: .featurePoint)

if let hitTestResultWithFeaturePoints = hitTestResultsWithFeaturePoints.first {

let translation = hitTestResultWithFeaturePoints.worldTransform.translation

addBox(x: translation.x, y: translation.y, z: translation.z)

}

This is what we are doing.

First, we perform a hit test, similar to how we hit test the first time around. Except that, we specify a .featurePoint result type for the types parameter. The types parameter asks the hit test to search for real-world objects or surfaces detected through the AR session’s processing of the camera image. There are many types of the result type. However, we will focus on just the feature point in this tutorial.

After the hit test of feature points, we safely unwrap the first hit test result. This is important because there may not always be a feature point. ARKit may not always detect a real world object or a surface in the real world.

If the first hit test result can be safely unwrapped, then we transform the matrix of type matrix_float4x4 to float3. This is possible because of the extension we created earlier. This handily gives us the x, y, and z real world coordinates that we are interested in.

We then take the x, y, and z to add a new box upon tapping on a detected feature point.

Your didTap(withGestureRecognizer:) method should look like this:

@objc func didTap(withGestureRecognizer recognizer: UIGestureRecognizer) {

let tapLocation = recognizer.location(in: sceneView)

let hitTestResults = sceneView.hitTest(tapLocation)

guard let node = hitTestResults.first?.node else {

let hitTestResultsWithFeaturePoints = sceneView.hitTest(tapLocation, types: .featurePoint)

if let hitTestResultWithFeaturePoints = hitTestResultsWithFeaturePoints.first {

let translation = hitTestResultWithFeaturePoints.worldTransform.translation

addBox(x: translation.x, y: translation.y, z: translation.z)

}

return

}

node.removeFromParentNode()

}

Build and run your project. In addition to everything you did before, you should now be able to add a box onto a feature point.

Done !!!!!!!!!!!

--

--