166.6K Views
September 11, 22
スライド概要
AR Developer
iOSDC 2022 Unreal EngineとiPhoneで始める リアルタイムAR配信 Satoshi Hattori @shmdevelop
Unreal Engineと
iPhoneを使って
リアルタイムAR配信...
格好良いぜ...!
今日はその考え方と要点を説明します!
服部 智 CyberAgent - AICG事業部 - xRギルド リーダー - AR Next Expert 最近の興味: Unreal Engine 5、LiveLink Plugin、 iPhoneの映像ワークフロー活用、 NDI、動画倍速視聴・スキップ視聴
Unreal Engine リアルタイム映像処理に向いたゲームエンジン
リアルタイムAR配信
このシーンではPanasonic AW-UE150 使用
前置きとして "多様な実現方法の1パターン" です
前置きとして "半年前の自分が泣いて喜ぶ資料" です
前置きとして "今回はWindows PC使用" です
GALLERIA Windows10 CPU: Core i9 11900K グラフィックボード: GeForce RTX 3080 メモリ: DDR4-3200 16GB
目次 AR合成の概要 カメラトラッキング方法 Unreal Engineを使った基本的なシーン作成 iPhoneを使う手法 bonus: PTZカメラを使う手法
目次 AR合成の概要 カメラトラッキング方法 Unreal Engineを使った基本的なシーン作成 iPhoneを使う手法
現実世界とCG空間をぴったり合わせる作業
AR合成の概要
AR合成の概要 CG原点を決める 現実と同じサイズでCG空間を作成する CG空間のカメラと現実のカメラの情報を合わせる カメラの動きをCG空間に伝える
CG原点を決める
CG原点を決める 現実世界での 動かない、消えない、分かりやすい一点
CG原点を決める
CG原点を決める
CG原点を決める
CG原点を決める
AR合成の概要 CG原点を決める 現実と同じサイズでCG空間を作成する CG空間のカメラと現実のカメラの情報を合わせる カメラの動きをCG空間に伝える
現実と同じサイズでCG空間を作成する
現実と同じサイズでCG空間を作成する
現実と同じサイズでCG空間を作成する
1 Unreal Unit は 現実の1cm
現実と同じサイズでCG空間を作成する
AR合成の概要 CG原点を決める 現実と同じサイズでCG空間を作成する CG空間のカメラと現実のカメラの情報を合わせる カメラの動きをCG空間に伝える
CG空間のカメラと現実のカメラの情報を合わせる
「位置」を合わせる
CG空間のカメラと現実のカメラの情報を合わせる
CG空間のカメラと現実のカメラの情報を合わせる
ボッシュ(BOSCH) レーザー距離計 ZAMO3
「カメラとレンズの特性」を合わせる
カメラの Focal Length (焦点距離)、FOV、Sensor Size (センサーサイズ) の関係 Unity Documentation, 物理カメラの使用 https://docs.unity3d.com/ja/2019.4/Manual/PhysicalCameras.html
CG空間のカメラと現実のカメラの情報を合わせる
CG空間のカメラと現実のカメラの情報を合わせる
AR合成の概要 CG原点を決める 現実と同じサイズでCG空間を作成する CG空間のカメラと現実のカメラの情報を合わせる カメラの動きをCG空間に伝える
カメラの動きをCG空間に伝える
カメラの動きをCG空間に伝える
AR合成の概要 CG原点を決める 現実と同じサイズでCG空間を作成する CG空間のカメラと現実のカメラの情報を合わせる カメラの動きをCG空間に伝える
目次 AR合成の概要 カメラトラッキング方法 Unreal Engineを使った基本的なシーン作成 iPhoneを使う手法
目次 AR合成の概要 カメラトラッキング方法 Unreal Engineを使った基本的なシーン作成 iPhoneを使う手法
カメラトラッキング方式 カメラトラッキング 送信プロトコル
カメラトラッキング方式 光学式 機械式 画像式 その他 慣性センサ 磁気 etc
カメラトラッキング方式 光学式 事前設置した反射マーカー位置を専用カメラで捉えカメラ位置を判定 機械式 画像式 その他 慣性センサ 磁気 etc https://www.nacxr.jp/product/redspy/
カメラトラッキング方式 光学式 機械式 ギアやエンコーダなどの回転角や変位を測定するセンサを使用 画像式 その他 慣性センサ 磁気 etc https://www.nacinc.jp/creative/virtual-products/stypekit/
カメラトラッキング方式 光学式 機械式 画像式 画像の特徴点を取得しトラッキングする その他 慣性センサ 磁気 etc
カメラトラッキング方法 光学式 機械式 画像式 その他 慣性センサ 磁気 etc
カメラトラッキング方式 カメラトラッキング 送信プロトコル
カメラトラッキング 送信プロトコル FreeD NDI
FreeD
FreeD ネットワーク経由で送信されたカメラトラッキングデータを 読み込み、仮想3Dカメラを制御するために使用できるプロトコル FreeD は特定のトラッキングシステム専用ではなく 幅広いハードウェアとソフトウェアに対応しているため 交換プロトコルとして広く普及している
https://www.manualsdir.com/manuals/641433/vinten-radamec-free-d.html
FreeD Type D1
https://github.com/satoshi0212/FreeDSendSimulator
カメラトラッキング 送信プロトコル FreeD NDI
NDI
NDI(Network Device Interface)とは IPネットワークを経由してビデオとオーディオストリームを 伝送する方式のひとつ 現在世界で最も利用されているIP伝送テクノロジーで、 IPを利用したライブ映像制作ワークフロー支援を目的としている
https://speakerdeck.com/satoshi0212/nditoarkitwolian-dong-sasetaxin-siiying-xiang-biao-xian
https://github.com/satoshi0212/NDISenderExample
カメラトラッキング方式 カメラトラッキング 送信プロトコル
目次 AR合成の概要 カメラトラッキング方法 Unreal Engineを使った基本的なシーン作成 iPhoneを使う手法
目次 AR合成の概要 カメラトラッキング方法 Unreal Engineを使った基本的なシーン作成 iPhoneを使う手法
Unreal Engineを使った基本的なシーン作成
今回の話を聞く上で最低限知っておきたい世界観
World Level Actor Actor Actor Actor Actor Actor Actor Actor Actor
Place Actors
Content Browser
ViewPort
ViewPortに追加すると LevelのActorになる
Unreal Engineを使った基本的なシーン作成
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定 公式サイトからダウンロード
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定 Epic Games Launcher
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定 Film/VideoのBlankテンプレートから作成
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定 エディタ設定画面
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定 エディタ設定画面
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定 プロジェクト設定画面
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定 プロジェクト設定画面
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定 Actorを親にしてCineCameraActor追加
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定 Levelブループリントを開く
インストール 新規プロジェクト作成 エディタ初期設定 プロジェクト初期設定 カメラ追加 初期カメラ設定
Play時にPlayerControllerのカメラを指定の物に設定
Unreal Engineを使った基本的なシーン作成 🎉
目次 AR合成の概要 カメラトラッキング方法 Unreal Engineを使った基本的なシーン作成 iPhoneを使う手法
目次 AR合成の概要 カメラトラッキング方法 Unreal Engineを使った基本的なシーン作成 iPhoneを使う手法
iPhoneを使って実現
映像 トラッキング情報
映像 → NDIで送信 トラッキング情報 → NDIのメタデータで送信
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映 公式ページより NDI Toolsインストール
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映 NDI SDK for Unreal Engineインストール
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映 PluginsよりNDI IO Pluginを有効化
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
Texture作成
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映 NDIで映像送信 1920x1080 30fps MetaDataにカメラ情報
https://github.com/satoshi0212/NDISenderExample
NDIWrapper.m
- (void)send:(CMSampleBufferRef)sampleBuffer
width:(int)width height:(int)height
metadata:(const char *)metadata {
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
NDIlib_video_frame_v2_t video_frame;
video_frame.frame_rate_N = 30000;
video_frame.frame_rate_D = 1000;
video_frame.xres = width;
video_frame.yres = height;
video_frame.FourCC = NDIlib_FourCC_type_BGRA;
video_frame.frame_format_type = NDIlib_frame_format_type_progressive;
video_frame.picture_aspect_ratio = (float)width / (float)height;
video_frame.line_stride_in_bytes = video_frame.xres * 4;
video_frame.p_metadata = metadata;
CVPixelBufferLockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
video_frame.p_data = CVPixelBufferGetBaseAddress(imageBuffer);
CVPixelBufferUnlockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
NDIlib_send_send_video_async_v2(my_ndi_send, &video_frame);
}
NDIWrapper.m
- (void)send:(CMSampleBufferRef)sampleBuffer
width:(int)width height:(int)height
metadata:(const char *)metadata {
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
NDIlib_video_frame_v2_t video_frame;
video_frame.frame_rate_N = 30000;
video_frame.frame_rate_D = 1000;
video_frame.xres = width;
video_frame.yres = height;
video_frame.FourCC = NDIlib_FourCC_type_BGRA;
video_frame.frame_format_type = NDIlib_frame_format_type_progressive;
video_frame.picture_aspect_ratio = (float)width / (float)height;
video_frame.line_stride_in_bytes = video_frame.xres * 4;
video_frame.p_metadata = metadata;
CVPixelBufferLockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
video_frame.p_data = CVPixelBufferGetBaseAddress(imageBuffer);
CVPixelBufferUnlockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
NDIlib_send_send_video_async_v2(my_ndi_send, &video_frame);
}
NDIWrapper.m
- (void)send:(CMSampleBufferRef)sampleBuffer
width:(int)width height:(int)height
metadata:(const char *)metadata {
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
NDIlib_video_frame_v2_t video_frame;
video_frame.frame_rate_N = 30000;
video_frame.frame_rate_D = 1000;
video_frame.xres = width;
video_frame.yres = height;
video_frame.FourCC = NDIlib_FourCC_type_BGRA;
video_frame.frame_format_type = NDIlib_frame_format_type_progressive;
video_frame.picture_aspect_ratio = (float)width / (float)height;
video_frame.line_stride_in_bytes = video_frame.xres * 4;
video_frame.p_metadata = metadata;
CVPixelBufferLockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
video_frame.p_data = CVPixelBufferGetBaseAddress(imageBuffer);
CVPixelBufferUnlockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
NDIlib_send_send_video_async_v2(my_ndi_send, &video_frame);
}
NDIWrapper.m
- (void)send:(CMSampleBufferRef)sampleBuffer
width:(int)width height:(int)height
metadata:(const char *)metadata {
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
NDIlib_video_frame_v2_t video_frame;
video_frame.frame_rate_N = 30000;
video_frame.frame_rate_D = 1000;
video_frame.xres = width;
video_frame.yres = height;
video_frame.FourCC = NDIlib_FourCC_type_BGRA;
video_frame.frame_format_type = NDIlib_frame_format_type_progressive;
video_frame.picture_aspect_ratio = (float)width / (float)height;
video_frame.line_stride_in_bytes = video_frame.xres * 4;
video_frame.p_metadata = metadata;
CVPixelBufferLockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
video_frame.p_data = CVPixelBufferGetBaseAddress(imageBuffer);
CVPixelBufferUnlockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
NDIlib_send_send_video_async_v2(my_ndi_send, &video_frame);
}
NDIWrapper.m
- (void)send:(CMSampleBufferRef)sampleBuffer
width:(int)width height:(int)height
metadata:(const char *)metadata {
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
NDIlib_video_frame_v2_t video_frame;
video_frame.frame_rate_N = 30000;
video_frame.frame_rate_D = 1000;
video_frame.xres = width;
video_frame.yres = height;
video_frame.FourCC = NDIlib_FourCC_type_BGRA;
video_frame.frame_format_type = NDIlib_frame_format_type_progressive;
video_frame.picture_aspect_ratio = (float)width / (float)height;
video_frame.line_stride_in_bytes = video_frame.xres * 4;
video_frame.p_metadata = metadata;
CVPixelBufferLockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
video_frame.p_data = CVPixelBufferGetBaseAddress(imageBuffer);
CVPixelBufferUnlockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
NDIlib_send_send_video_async_v2(my_ndi_send, &video_frame);
}
ViewController.swift import CoreMotion private let motionManager = CMMotionManager() private var baseAttitude: CMAttitude? override func viewDidLoad() { super.viewDidLoad() ... motionManager.deviceMotionUpdateInterval = 1.0 / 60.0 motionManager.startDeviceMotionUpdates(using: .xArbitraryCorrectedZVertical) }
ViewController.swift import CoreMotion private let motionManager = CMMotionManager() private var baseAttitude: CMAttitude? override func viewDidLoad() { super.viewDidLoad() ... motionManager.deviceMotionUpdateInterval = 1.0 / 60.0 motionManager.startDeviceMotionUpdates(using: .xArbitraryCorrectedZVertical) }
ViewController.swift
private func makeMetaData() -> String {
guard let deviceMotion = motionManager.deviceMotion else { return "" }
let attitude = deviceMotion.attitude
if let baseAttitude = baseAttitude {
attitude.multiply(byInverseOf: baseAttitude)
}
let quat = attitude.quaternion
let arguments = [Float(quat.x), Float(quat.y), Float(quat.z), Float(quat.w)]
return arguments.map { String(format: "%.6f", $0) }.joined(separator: ",")
}
ViewController.swift
private func makeMetaData() -> String {
guard let deviceMotion = motionManager.deviceMotion else { return "" }
let attitude = deviceMotion.attitude
if let baseAttitude = baseAttitude {
attitude.multiply(byInverseOf: baseAttitude)
}
let quat = attitude.quaternion
let arguments = [Float(quat.x), Float(quat.y), Float(quat.z), Float(quat.w)]
return arguments.map { String(format: "%.6f", $0) }.joined(separator: ",")
}
なぜオイラー角ではなくクォータニオンを送るのか?
→ ジンバルロックを回避するため
当初はFreeDでオイラー角を送っていた しかしジンバルロック発生のため クォータニオンを送るよう変更
ジンバルロック "3軸の全てに自由な運動がある場合は、機体の回転によって3つ のジンバルリングのうち2つの軸が同一平面上にそろってしまう ジンバルロックという現象が発生しうる。発生すると、本来3あ るはずの自由度が2になってしまう。" https://ja.wikipedia.org/wiki/%E3%82%B8%E3%83%B3%E3%83%90%E3%83%AB
Euler (gimbal lock) Explained https://www.youtube.com/watch?v=zc8b2Jo7mno&t=58s
クォータニオン4:クォータニオンの演算 より https://www.sports-sensing.com/brands/labss/motionmeasurement/motion̲biomechanics/quaternion04.html
ViewController.swift extension ViewController : AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let ndiWrapper = self.ndiWrapper, isSending else { return } ndiWrapper.send(sampleBuffer, width: 1920, height: 1080, metadata: makeMetaData()) } }
ViewController.swift extension ViewController : AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let ndiWrapper = self.ndiWrapper, isSending else { return } ndiWrapper.send(sampleBuffer, width: 1920, height: 1080, metadata: makeMetaData()) } }
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映
現実世界のカメラ情報をCineCameraActorに設定
iPhone 13 Proのカメラ情報をCineCameraActorに設定
iPhone 13 Pro 広角カメラのカメラ情報は?
注: 正直、試行錯誤中です
知りたいのは Sensor Width Focal Length
Sensor Width Apple公式情報なし 調査すると「Sony IMX703」が使用されているようだ サイズは 1/1.66 型 横幅情報が見つからないため 1/1.7 型のサイズ 7.5 5.6 mm を参照
Focal Length https://www.youtube.com/watch?v=EvGOlAkLSLw "26mm focal length" を35mm換算すると5.7
iPhone 13 Pro のField of view func session(_ session: ARSession, didUpdate frame: ARFrame) { let a = Float(frame.camera.imageResolution.width) let b = 2 * frame.camera.intrinsics[0,0] let horizontalFov = 2 * atan(a / b) * 180 / Float.pi print(horizontalFov) } ... 70.362946 70.36179 70.359474 70.35788 70.35992 70.36179 70.36122 ...
動作させながら微調整した暫定値
一旦、平面に映像を表示してみる
Planeを追加し位置とサイズ調整
NDIのTextureをD&D (Materialが自動生成される)
Play
🎉
どの方向を向いても表示するようにする
?
平面だと、カメラを振った時に 現実世界の映像が切れてしまう
その解決策として
内側を表にした大きな立方体を追加
事前に作成しておいた 「内側を表にした立方体」 をImportし配置
NDIのTextureを描画するMaterialを改修
🎉
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映
NDI Plugin インストール NDI Plugin 有効化 NDI Media Receiver作成 BluePrint作成 iPhoneアプリ実装 カメラへ映像を表示 カメラに動作を反映
NDIでフレーム単位で受信している MetaDataのカメラ情報を反映
BluePrintにEventBind追加
位置確認用オブジェクトを置いてみる
Play
FreeDで失敗した動画
NDIで送付
🎉
まとめ AR合成の概要 カメラトラッキング方法 Unreal Engineを使った基本的なシーン作成 iPhoneを使う手法
リアルタイムAR映像配信 皆も実装してみよう!