見出し画像

Apple Vision Pro で3Dキャラクターに付いてきてもらう

「Apple Vision Pro」での3Dキャラクターに付いてきてもらう手順をまとめました。

・Xcode 15.2 (Apple Silicon)


前回

1. VRM を usdz に変換

「VRM」をusdzに変換する手順は、次のとおりです。

(1) モデルの準備。
ニコニコ立体」から「ニコニコ立体ちゃん」をダウンロードします。

(2) Blenderを起動して「VRM Add-on for Blender」をインストール。
リポジトリからアドオンファイル (VRM_Addon_for_Blender-release.zip) をダウンロードし、Blenderの「プリファレンス→アドオン→インストール」でインストールし、チェックボックスで有効化します。

(3) Blenderのメニュー「ファイル → インポート → VRM」でニコニコ立体ちゃんのVRMファイル (AliciaSolid.vrm) をインポート。

(4) Blenderのメニュー「ファイル → エクスポート → FBX」で「AliciaSolid.fbx」という名前でエクスポート。

(5) 「Mixamo」を開き、「UPLOAD CHARACTER」で「AliceSolid.fbx」をアップロードし、アクション「Walking」を選択し、右下の「In Place」をチェックし、「DOWNLOAD」ボタンを押す。
「Walking.fbx」がダウンロードされます。

(6) 「Reality Converter」をダウンロードしてインストール。

(7) 「Reality Converter」を起動して、「Walking.fbx」をドラッグ&ドロップし、右側の「Material」で各マテリアルにテクスチャを設定。

VRMのマテリアルのテクスチャは、「VRMEditorTools」で取得できます。

(8) 「Reality Converter」のメニュー「ファイル→書き出す」で「Walking.usdz」で保存。

2. プロジェクトの作成

プロジェクトの作成手順は、次のとおりです。

(1) Xcodeの最新版をインストールして起動。

(2) 「Create New Project」で「visionOS」の「App」を選択。

(3) 「Initial Scene」に「Window」、「Immersive Space Render」に「RealityKit」、「Immersive Space」に「Mixed」を指定。

3. 3Dキャラクターについてきてもらう

3Dキャラクターについてきてもらう手順は、次のとおりです。

(1) 「Package.realitycomposerpro」ダブルクリックで「Reality Composer」を起動し、「Walking.usdz」のモデルを中央に配置し、デフォルトの球を削除。

(2)「AppleVisionPro.swift」を追加。

・AppleVisionPro.swift

import SwiftUI
import ARKit

@MainActor
class AppleVisionPro: ObservableObject {

    let session = ARKitSession()
    let worldTracking = WorldTrackingProvider()

    func run() async {
        if WorldTrackingProvider.isSupported {
            do {
                try await session.run([worldTracking])
            } catch {
                assertionFailure("Failed to run session: (error)")
            }
        }
    }
    
    func getTransform() async -> simd_float4x4? {
        if let anchor = worldTracking.queryDeviceAnchor(atTimestamp: CACurrentMediaTime()) {
            return anchor.originFromAnchorTransform
        }
        return nil
    }
}

(3)「ImmersiveView.swift」を以下のように編集。
3Dキャラクターに、ユーザーの前方2.0の場所まで移動してもらいます。

・ImmersiveView.swift

import SwiftUI
import RealityKit
import RealityKitContent
import simd

struct ImmersiveView: View {
    @ObservedObject var appleVisionPro = AppleVisionPro()
    @State var entity: Entity?
    private var timer = Timer.publish(every: 0.02, on: .main, in: .common).autoconnect()

    var body: some View {
        RealityView { content in
            if let scene = try? await Entity(named: "Immersive", in: realityKitContentBundle) {
                content.add(scene)
                
                // アニメーションを再生
                if scene.availableAnimations.count > 0 {
                    scene.playAnimation(scene.availableAnimations[0].repeat())
                }
                entity = scene
            }
        }
        .task() {
            await appleVisionPro.run()
        }
        .onReceive(timer) { _ in
            Task {
                let transform = await appleVisionPro.getTransform()
                if entity != nil && transform != nil{
                    let speed: Float = 0.01
                    
                    // モデル位置
                    let modelPosition = SIMD3<Float>(entity!.position.x, entity!.position.y, entity!.position.z)

                    // ユーザー位置
                    let userPosition = SIMD3<Float>(transform!.columns.3.x, transform!.columns.3.y, transform!.columns.3.z)
                    
                    // ターゲット位置
                    var translation = matrix_identity_float4x4
                    translation.columns.3.z = -2.0; translation.columns.3.y = -1.0
                    let targetTransform = simd_mul(transform!, translation)
                    let targetPosition = simd_make_float3(targetTransform.columns.3.x, targetTransform.columns.3.y, targetTransform.columns.3.z)

                    // 距離
                    let distance = length(targetPosition - modelPosition)
                    if distance > 0.5 {
                        // ターゲット位置に移動
                        let direction = normalize(modelPosition - targetPosition)
                        let movement = direction * speed
                        entity!.position -= movement
                        
                        // ターゲットの方向を向く
                        let targetAngle = -atan2(direction.z, direction.x) - .pi / 2
                        let targetRotation = simd_quatf(angle: targetAngle, axis: [0, 1, 0])
                        entity!.orientation = simd_slerp(entity!.orientation, targetRotation, 0.5)
                    } else {
                        // ユーザー方向を向く
                        let direction = normalize(modelPosition - userPosition)
                        let angle = -atan2(direction.z, direction.x) - .pi / 2
                        entity!.orientation = simd_quatf(angle: angle, axis: [0, 1, 0])
                    }
                }
            }
        }
    }
}

#Preview {
    ImmersiveView()
        .previewLayout(.sizeThatFits)
}

(4) 実行。

参考

次回



この記事が気に入ったらサポートをしてみませんか?