【ARKit】ARで踊っている人に手袋をつける方法
ARKitを使って踊っている人にARの手袋をつけてみます。
今回は、ARViewを用いて作っていきます。
ARViewの設定をする
人の身体の動きがトラッキングできるように、ARViewのセッションをARBodyTrackingConfigurationを使って構成します。
まずはデバイスがARBodyTrackingConfigurationをサポートしているかを確認します。
guard ARBodyTrackingConfiguration.isSupported else {
fatalError("This feature is only supported on devices with an A12 chip")
}
ARBodyTrackingConfigurationを適用して、ARセッションを開始します。
let configuration = ARBodyTrackingConfiguration()
arView.session.run(configuration)
これで身体の動きがトラッキングできるようになりました。
手袋を準備する
RealityComposerを使って、手袋のアンカーを配置します。
手袋は何でも良いのですが、自分で作ったり、ネットで購入した3DモデルをRealityComposerにインポートする作業は、3Dモデルの形式の変換がうまくいかないことが多くて結構ハマります。
その方法はこちらにまとめてあるので、参考にして下さい。
作成したモデルは読み込んでAR空間に配置しておきます。
// 宣言
let leftHandAnchor = AnchorEntity()
let rightHandAnchor = AnchorEntity()
var leftHandGlove: Experience.LeftHand!
var rightHandGlove: Experience.RightHand!
// 読み込み
leftHandGlove = try! Experience.loadLeftHand()
rightHandGlove = try! Experience.loadRightHand()
// AR空間に配置
arView.scene.addAnchor(leftHandAnchor)
arView.scene.addAnchor(rightHandAnchor)
身体の情報を取得する
ARKitではアンカーの状態が変わるとARSessionDelegateのsessionメソッドが呼ばれるようになっています。まずはdelegateを登録しておきます。
// 予め登録しておく
arView.session.delegate = self
sessionが呼ばれたら、それが身体の情報なのかどうかを判別します。
アンカーがARBodyAnchor型であれば身体の情報であることがわかります。
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
for anchor in anchors {
guard let bodyAnchor = anchor as? ARBodyAnchor else { continue }
・・・
}
}
身体の情報からさらに手の位置と角度を特定する
ARBodyAnchorは、skeletonプロパティを持っていて、身体の91個のジョイントが登録されています。
これらのジョイントの中から手の情報を取得します。
ジョイントにはすべて名前がつけられており、その名前を使って情報を取得することができます。
例えば、左手はmodelTransformメソッドをこのように呼び出すと取得できます。
guard let leftHandTransform = bodyAnchor.skeleton.modelTransform(for: .leftHand) else { continue }
modelTransformメソッドで得られる情報は何か?
ARBodyAnchorの座標はお尻の位置が中心となっていますが、modelTransformは、そこからの移動量と回転量を表しています。
なお、modelTransform以外にも、localTransformというメソッドもありますが、こちらは親のジョイントに対する移動量と回転量を表しています。
modelTransformの宣言を確認してみましょう。
@nonobjc public func modelTransform(for jointName: ARSkeleton.JointName) -> simd_float4x4?
simd_float4x4という変わった名前の型になっています。
これは、simd_float4型の4行4列の行列で、移動や回転を表すことができます。
なぜこのような行列を使うかいうことについては、こちらの記事がわかりやすかったです。
なお、先頭にあるsimdとはGPUの計算方式で、1つの命令で複数のデータを加工できることができます。
これで手の位置を取得できたわけですが、その位置に手袋をおいてもうまくいきません。
なぜなら、取得した手の位置は、あくまでお尻から手までの位置・回転にすぎないからです。その前に、お尻(身体の中心)の位置を得る必要があります。これはbodyAnchor.transformで得ることができます。
そしてこれらのtransform掛け合わせることで、3D空間上での手の位置・回転を得ることができます。
let leftHandTrans = bodyAnchor.transform * leftHandTransform
leftHandAnchor.transform = Transform(matrix: leftHandTrans)
simd_float4x4は「*」の関数が定義されているので、そのまま掛け合わせることができます。
取得した手の情報に手袋をつける
手袋をつける処理は、手の位置に手袋の3Dモデルをそのまま配置することで実現できます。
手袋のアンカーに先程取得したtransformを代入するだけです。
代入する前に、先に配置しておいたアンカーに手袋のアンカーをぶら下げています。
if leftHandGlove.parent == nil {
leftHandAnchor.addChild(leftHandGlove)
}
rightHandAnchor.transform = Transform(matrix: rightHandTrans)
仕上がり
家族が協力してくれないので、購入した動画につけてみました。実際の人の方が正確な位置に手袋が付きます。
手以外の位置にも配置できるので、応用すれば帽子を作ったり、身体の動きにあわせて物体を動かしたり、可能性が広がりますね。
コード全体はこちらです。
import UIKit
import RealityKit
import ARKit
class ViewController: UIViewController {
@IBOutlet var arView: ARView!
let leftHandAnchor = AnchorEntity()
let rightHandAnchor = AnchorEntity()
var leftHandGlove: Experience.LeftHand!
var rightHandGlove: Experience.RightHand!
var nowRecording: Bool = false
override func viewDidLoad() {
func loadAnchor() {
leftHandGlove = try! Experience.loadLeftHand()
rightHandGlove = try! Experience.loadRightHand()
arView.scene.addAnchor(leftHandAnchor)
arView.scene.addAnchor(rightHandAnchor)
}
super.viewDidLoad()
loadAnchor()
}
override func viewDidAppear(_ animated: Bool) {
func configureARKit() {
guard ARBodyTrackingConfiguration.isSupported else {
fatalError("This feature is only supported on devices with an A12 chip")
}
arView.session.delegate = self
let configuration = ARBodyTrackingConfiguration()
arView.session.run(configuration)
arView.debugOptions = [.showWorldOrigin]
}
super.viewDidAppear(animated)
configureARKit()
}
}
extension ViewController: ARSessionDelegate{
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
for anchor in anchors {
guard let bodyAnchor = anchor as? ARBodyAnchor else { continue }
guard let leftHandTransform = bodyAnchor.skeleton.modelTransform(for: .leftHand) else { continue }
guard let rightHandTransform = bodyAnchor.skeleton.modelTransform(for: .rightHand) else { continue }
if leftHandGlove.parent == nil {
leftHandAnchor.addChild(leftHandGlove)
rightHandAnchor.addChild(rightHandGlove)
}
let leftHandTrans = bodyAnchor.transform * leftHandTransform
leftHandAnchor.transform = Transform(matrix: leftHandTrans)
let rightHandTrans = bodyAnchor.transform * rightHandTransform
}
}
}
githubでもコードを公開しています。こちらには録画のコードも含まれています。また、手袋のモデルは有償のものなので、ピザととうもろこしに差し替えてありますのでご了承下さい。
この記事が気に入ったらサポートをしてみませんか?