Apple Vision Proでアプリからペルソナを表示する
Apple Vision Pro では SharePlay でペルソナを使用することができます。SharePlayで会話相手にそっくりなアバターが表示され、そこから声が聞こえたり、身振り手振りや表情が反映されていると、実際に会って話しているのと同じような感覚を得ることができます。
今回はそんなペルソナをアプリケーション側から使用する方法について調べたので解説していきます。
環境設定
現在は映像としてしか自分のペルソナを表示することができないため、仮想カメラから自分のペルソナの映像を取得する必要があります。
まずはカメラにアクセスするためプライバシーの設定を行います。
CameraUsageDescription
に使用理由を追加します。
ペルソナ用カメラ映像の取得
まずは setupCamera
という関数を用意しカメラへのアクセス権をリクエストします。
func setupCamera() async {
await AVCaptureDevice.requestAccess(for: .video)
...
}
次にカメラの取得をします。visionOSではsystemPreferredCamera
を取得することでペルソナ用のカメラを取得することができるようです。
guard let camera = AVCaptureDevice.systemPreferredCamera else { return }
取得したカメラを使用してセッションを作成します。captureSession.addInput
にAVCaptureDeviceInput
を追加することでセッションの入力ソースを追加しています。
guard let videoInput = try? AVCaptureDeviceInput(device: camera) else { return }
captureSession = AVCaptureSession()
if captureSession.canAddInput(videoInput) {
captureSession.addInput(videoInput)
} else {
print("Could not add video input")
return
}
次に出力の設定をします。
videoDataOutput.setSampleBufferDelegate
ではバッファを取得するためのデリゲートを設定しています。今回作っているクラスはNSObject
とAVCaptureVideoDataOutputSampleBufferDelegate
を継承し、captureOutput
を実装することで、captureOutput
でバッファを取得できるようになります。
videoDataOutput = AVCaptureVideoDataOutput()
// ピクセルバッファのフォーマットを32bitRGBAにしている
videoDataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as String): Int(kCVPixelFormatType_32BGRA)]
// ビデオに遅延が起きた時にフレームを削除するか
videoDataOutput.alwaysDiscardsLateVideoFrames = true
// 出力用のキューとデリゲートの設定
// AVCaptureVideoDataOutputSampleBufferDelegate を継承しているのでselfを渡す
videoDataOutputQueue = DispatchQueue(label: "VideoDataOutputQueue")
videoDataOutput.setSampleBufferDelegate(self, queue: videoDataOutputQueue)
if captureSession.canAddOutput(videoDataOutput) {
captureSession.addOutput(videoDataOutput)
} else {
print("Could not add video output")
return
}
//セッションを開始する
captureSession.startRunning()
最後にcaptureOutput
の実装を見ていきます。
CMSampleBufferGetImageBuffer
を使用してpixelBuffer
を取得します、そこからCIMage
を作成し createCGImage
でCGImage
に変換した後、UIImage
に変換しています。
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
let imageRect = CGRect(x: 0, y: 0, width: CVPixelBufferGetWidth(pixelBuffer), height: CVPixelBufferGetHeight(pixelBuffer))
let context = CIContext()
if let image = context.createCGImage(ciImage, from: imageRect) {
DispatchQueue.main.async {
self.callback(UIImage(cgImage: image))
}
}
}
}
ペルソナ用カメラ映像の表示
これで UIImage
を取得することができるようになりました。
あとは取得したUIImage
を表示することでカメラの映像を表示することができます。
@State var personaCamera: PersonaCamera?
@State var image: UIImage?
var body: some View {
ZStack {
if let image = self.image {
Image(uiImage: image)
.resizable()
.scaledToFill()
.frame(width: 800, height: 800)
}
}
.glassBackgroundEffect()
.onAppear() {
personaCamera = .init(callback: { image in
self.image = image
})
Task {
await personaCamera?.setupCamera()
}
}
}
まとめ
これで自分のペルソナを使用することができました!
まだ映像でしか使用できないですが今後のアップデートでモデルにもアプリケーション側で触れるようになると良いですね。
書いた人
佐藤 寿樹
株式会社コナミデジタルエンタテインメントに入社し5年間ウイニングイレブンのオンライン実装に携わる。
その後、株式会社コロプラで9年間エンジニアとしてアプリ開発・運用を行い、位置情報やARを使用したARゲーム開発、OculusRiftやPSVRなどのVRゲーム開発を経験しMESONへ入社。
MESON Works
MESONの制作実績一覧もあります。ご興味ある方はぜひ見てみてください。
Discussion