iOS - ARkit - P.2 Geometry Object
Như ở bài trước mình đã giới thiệu sơ lược về ARKit của iOS, về cách tạo 1 project và init 1 scene view của AR. Thì trong bài này mình sẽ giới thiệu về các khối hình học cơ bản trong ARKit. Overview Có thể nói các vật thể 3D được hiển thị trong scene view là các Geometry Object (SCNGeometry), ...
Như ở bài trước mình đã giới thiệu sơ lược về ARKit của iOS, về cách tạo 1 project và init 1 scene view của AR. Thì trong bài này mình sẽ giới thiệu về các khối hình học cơ bản trong ARKit.
Overview
Có thể nói các vật thể 3D được hiển thị trong scene view là các Geometry Object (SCNGeometry), nó gồm các thuộc tính về hình dáng, kích thước và màu sắc của vật thể. SCNGeometry được gắn với SCNNode để hiển thị trên scene và SCNMaterial được gắn với SCNGeometry để biểu thị các thuộc tính bên ngoài của nó.
Trong SceneKit đã bao gồm 1 số khối hình học cơ bản như là hình cầu (SCNSphere), lập phương (SCNBox), hình trụ (SCNCylinder)... cũng như text (SCNText) và SCNShape để biểu thị các hình học 2 chiều (có thể biến SCNShape trở thành hình 3 chiều), tham khảo thêm tại đây.
Tạo một Geometry
Sử dụng lại class đã code phần trước, lần này chúng ta sẽ thêm vào màn hình đã code 1 nút add và 1 nút reset như sau: Nút Add là để thêm vào cái vật thể hình cầu màu vàng ở giữa màn hình mọi người đang thấy. Reset thì như tên của nó, là để xoá các vật thể đã thêm vào trong Scene view đồng thời thiết lập lại trục toạ độ.
class MainScreenViewController: UIViewController { @IBOutlet private weak var scnKitView: ARSCNView! private let configuration = ARWorldTrackingConfiguration() override func viewDidLoad() { super.viewDidLoad() scnKitView.debugOptions = [ARSCNDebugOptions.showFeaturePoints, ARSCNDebugOptions.showWorldOrigin] scnKitView.session.run(configuration) scnKitView.autoenablesDefaultLighting = true } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } @IBAction func add(_ sender: Any) { let node = SCNNode() node.geometry = SCNSphere(radius: 0.03) node.geometry?.firstMaterial?.diffuse.contents = UIColor.yellow node.position = SCNVector3(0, 0, -0.3) scnKitView.scene.rootNode.addChildNode(node) } @IBAction func reset(_ sender: Any) { restartSession() } func restartSession() { scnKitView.session.pause() scnKitView.scene.rootNode.enumerateChildNodes { (node, _) in node.removeFromParentNode() } scnKitView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors]) } }
Đoạn code trong nút hàm add(_ sender: Any) là để khởi tạo 1 node trong scene. Node đó sẽ có geometry (shape) là 1 hình cầu, mọi người có thể thử các hình khác cũng như là SCNText. Như đã đề cập ở đầu, SCNGeometry được gắn vào SCNode để biểu thị hình dáng của node và SCNMaterial được gắn vào SCNGeometry, nó sẽ chứa các thuộc tính về màu sắc, đổ bóng, màu của bóng...
node.geometry?.firstMaterial?.diffuse.contents = UIColor.yellow // Màu sắc của khối cầu
Mọi người có thể thêm vào đoạn sau và xem hiệu ứng của nó:
node.geometry?.firstMaterial?.specular.contents = UIColor.red
Ngoài ra chúng ta cũng phải xác lập vị trí của node nếu không thì sẽ không có gì hiện ra trong scene của chúng ta cho dù đã thêm node vào scene. Vị trí trong scene đương nhiên phải là 1 vector 3 chiều rồi:
node.position = SCNVector3(0, 0, -0.3) // -0.3 tức là z = -0.3 vật thể sẽ nằm trước mặt chúng ta 0.3m
Xoay
Để xoay 1 node chúng ta sẽ sử dụng thuộc tính eulerAngles của node, thuộc tính này là 1 vector 3 chiều, nhưng trước hết mọi người hãy tìm hiểu về Euler Angles để dễ hình dung về chiều xoay và cách để xoay của 1 vật thể.
Trong hàm add(_ sender: Any) sẽ được sửa lại như sau
@IBAction func add(_ sender: Any) { let node = SCNNode() node.geometry = SCNCylinder(radius: 0.03, height: 0.06) node.geometry?.firstMaterial?.diffuse.contents = UIColor.yellow node.eulerAngles = SCNVector3(0, 0, 90.0.radian) node.position = SCNVector3(0, 0, -0.3) scnKitView.scene.rootNode.addChildNode(node) }
Lần này chúng ta thay hình cầu bằng 1 hình trụ bởi vì hình cầu thì có xoay theo trục nào đi chăng nữa thì cũng không thấy được sự thay đổi ¯(ツ)/¯. Và đoạn code để xoay:
node.eulerAngles = SCNVector3(0, 0, 90.0.radian) // Hãy thử xoay theo các trục khác
Bởi vì dùng đơn vị radian để xoay nên sẽ có 1 extension nhỏ như sau để convert biến double sang radian
extension Double { var radian: Double { return self * (Double.pi/180) } }
Có 1 chút lưu ý nhỏ khi chúng ta reset lại gốc, để lấy lại gốc toạ độ nhanh thì nên quay camera về nơi có các vật thể có màu sắc tương phản nhau và đưa camera qua lại 1 chút. Có thể thấy sự khác biệt nếu chĩa camera về 1 bức tường đơn sắc hoặc 1 tờ giấy trắng thì thao tác sẽ chậm hơn nhiều.
Ở phần tiếp theo mình sẽ giới thiệu về 1 thứ rất quen thuộc là UIBeizerPath và cách tạo 1 mô hình Trái Đất.