AfterEffectsでカメラトラッキングした座標をUnrealEngine5上で再生し実写合成のワークフローを構築する

株式会社GENEROSITYのCGデザイナー丸山です。

今回は、UnrealEngine5(以下UE)上で実写の動画にCGを合成し、
それをAfterEffects(以下AE)でコンポジットする方法を解説します。
マッチムーブとよく呼ばれる手法でプリレンダではメジャーな手法ですが、
UEで作っている人はまだそこまで多くない印象です。

## 目指すアウトプット
![Output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/14809b4d-eb5b-f76a-b499-9c8c0dbd610d.gif)

上記のように、スマートフォンで撮影した動画のカメラ情報をAEでトラッキングし、
それをUEにインポートしてUE上でアセットを配置しプレビュー、
MovieRenderQueueでレンダリングしてみようという内容です。

## UEを使うメリット
UEを使うメリットとしてはとしては、下記3点です。


- [x] UEマーケットプレイスのアセットを流用できる
- [x] リアルタイムプレビューのため柔軟に修正に対応できる
- [x] UEのDataSmithC4DImpoterでAEのカメラトラッキング情報を手軽に流し込める

## 参考動画
下記の動画が大変参考になりました。
[Joshua M Kerr](https://www.joshuamkerr.com/)さんありがとうございます。
<iframe width="560" height="315" src="https://www.youtube.com/embed/r7IW5te_3ZI?si=Y7vk5YRkvPwEV3Gp" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>


## バージョン
最初にソフトのバージョン情報です。
- [x] AfterEffects2024
- [x] UnrealEngine5.4

## 作成手順

| 手順 | ページ内リンク |
|:----:|:-------------|
| 1 | [カメラトラッキングの座標の抽出(AE)](#1カメラトラッキングの座標の抽出ae) |
| 2 | [UEにAEで抽出したカメラ座標をDataSmithC4DImpoterで流し込む(UE)](#2ueにaeで抽出したカメラ座標をdatasmithc4dimpoterで流し込むue) |
| 3 | [動画のプレビュー環境を作成する(UE)](#3動画を読み込みマテリアルとして扱う準備をするue) |
| 4 | [プレーンに動画マテリアルを配置しシーンで再生する(UE)](#4プレーンに動画マテリアルを配置しシーンで再生するue) |
| 5 | [BluePrintで再生システムを組む](#5blueprintで再生システムを組む) |
| 6 | [3Dアセットの配置(UE)](#63dアセットの配置ue) |
| 7 | [アルファ付きの連番pngを書き出す(UE)](#7アルファ付きの連番pngを書き出すue) |
| 8 | [シャドウのみをレンダリングする(UE)](#8シャドウのみをレンダリングするue) |
| 9 | [コンポジット(AE)](#9コンポジットae) 


行程が多いのですが順を追って解説していきます。

### 1.カメラトラッキングの座標の抽出(AE)
まず、エフェクト&プリセットで3D カメラトラッカーを検索し適応します。
![3DCamtracker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/4809ea49-7ab1-63cf-2d99-5979d0e5117f.png)
分析が終わる前で待機します。

![CamTracking.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/e0741f32-6d85-70de-65f6-b524ac8aa0dc.png)
動画に特徴点が多ければ特に問題なくバッテン印が追従している結果が確認できるはずです。

![SetGridAndOrigin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/a74fb99b-69d6-6f4b-eb4f-c21025035850.png)
平面になっている特徴点を選択し、グリッドと原点ををクリックし、(0,0,0)の位置を定義します。

![SetCamAndPlane.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/873786d1-e885-0cb7-2414-342b2438cc72.png)
平面とカメラを作成します。

![TrackingPlane.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/ef7521d5-28aa-2233-6d79-e2a1d27ca906.png)
カメラに追従する平面が表示されます。

この記事ではトラッキングのAEの細かい調整については言及しませんので
わからないことがあれば[公式のドキュメント](https://helpx.adobe.com/jp/after-effects/using/tracking-3d-camera-movement.html)を参照いただければと思います。

トラッキングが完了したらデータを保存し、
書き出し>Maxon Cinema4D Exporterをクリックし、エクスポートします。
![C4DFormatExport.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/bd7849b0-a35d-4b9b-b3fa-b9809a4622a4.png)

その際に2Dレイヤーは書き出せないとアラートができますが、
2Dレイヤーは不要なので問題ありません。

![About2DLayer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/c7f12c19-e490-dbf9-f12d-e1e63461daab.png)

任意のフォルダーにc4dファイルを保存します。
![C4DExport.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/82dbc160-953d-7ded-f04a-26f67bb1bfe5.png)


### 2.UEにAEで抽出したカメラ座標をDataSmithC4DImpoterで流し込む(UE)
UEでカメラ情報を読み込みたいプロジェクトに
C4Dのデータがインポートできるよう設定します。

EditPlugin>Pluginsから、Datasmith C4D importerを探してチェックを入れます。
プロジェクトを再起動されるか聞かれるので、そのまRestart Nowのボタンを押し再起動します。
![DataSmith.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/454d91e8-6478-1722-9f88-341fb2ec917d.png)

追加ボタン>Datasmith>File Importを押し先ほどAEから出力したC4DFileをインポートします。
![ImportC4DFile.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/6864b3c2-3fb3-12a7-56cd-def1f4d35365.png)
任意のディレクトリを選択。
![C4DDataSave 2024-10-21 155737.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/4147cd35-a376-0849-9748-d0fcffd53424.png)

インポートオプションのポップアップが開くので、
Animationにチェックが入っているかを確認します。
今回はライトなど特に含まれていないですが、OKを押します。
![ImportOptions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/11b9f8f9-c7e3-0471-3fa8-beb4f47f0042.png)

Animationsフォルダの中にレベルシーケンスがインポートされました。
この中にカメラのアニメーション情報が入っています。
![スクリーンショット 2024-10-21 160337.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/c7738476-f14e-0cd0-a883-278c82751a96.png)

シーケンスをダブルクリックして開くと、キーフレームが打たれていることが
確認できましたが、スケールが合っていないためとても大きな動きをしてしまっています。
![BigMotion.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/f755423b-a0ec-4286-e279-698a4253db20.png)

アクタのスケールを0.01に設定し、カメラのサイズを縮小して、
Pilotで見てみるとアイレベルも問題なさそうな見え方になりました。
![ChangeScale.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/b3ebe659-4ecf-bb97-47a8-6e829ae6b6fe.png)


### 3.動画を読み込みマテリアルとして扱う準備をする(UE)


実写の動画がプレビューできるマテリアルを作っていきます。
動画をマテリアルとして扱うには下記3つのアセットが必要です。
- [x] FileMediaSource
- [x] MediaPlayer
- [x] MediaTexture

少しややこしいですが、順を追って説明していきます。

#### ・FileMediaSource

![ImportMovie.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/ba626f66-01dc-b394-57b7-0051c2dab220.png)
UE上で再生可能な動画データは、インポートするとFileMediaSourceとして扱われます。
ImportのUIもしくはDrag&Dropで、先ほどAEでカメラトラッキングした
ベースの動画データ(mp4)をインポートします。

#### ・MediaPlayer
![CreateMediaPlayer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/40b1d27c-bc4f-5659-22b0-2f239e995594.png)
先ほどのFileMediaSourceをマップ上で再生するにはMediaPlayerが必要になります。
右クリック>Media>Media Playerをクリック　で作成。

![VideoTextura.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/12f6c78d-fd67-241a-1233-fb73fcf14f2e.png)
作成するMediaPlayerに紐づくMediaTextureを作成するかポップアップが出ます。
MediaTextureはマテリアルでテクスチャとしてFileMediaSourceを扱うために必要なアセットです。

問題ないのでチェックしてOKを押します。

![Playlist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/5ff2cde9-32d0-856a-bda1-1fabbaa48524.png)
MediaPlayerから、再生するFileMediaSourceを指定する必要があります。
ダブルクリックで詳細を開くと、下部のNameに再生可能なFileMediaSource一覧が表示されるので
クリックして指定します。指定すると右上のプレイリストにメディア名が表示され
プレビューできます。

#### ・MediaTexture

![MediaTexture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/b8bf67c4-2db0-6f2c-5bd2-9443ad0189ae.png)
MediaPlayerで再生される動画をMaterialGraphのテクスチャノードとして扱うため
MediaTextureを作成し設定する必要があります。
今回の場合は使用したいMediaPlayerが既に紐づいた状態のMediaTextureを
流れで作成したため、ダブルクリックして詳細を一応確認しておきます。
(後から作成する場合は別途紐づけが必要です。)

### 4.プレーンに動画マテリアルを配置しシーンで再生する(UE)
前述したFileMediaSource/MediaPlayer/MediaTextureの
準備ができたので、MaterialGraphでビデオが再生できるマテリアルを組んでいきます。

#### ・Materialノード

新規Materialを作成し、MaterialGraphを開きます。
![Unlit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/d66a9a7c-a571-0f57-fae3-a176549ccbb8.png)
動画をプレビューしたいだけなのでライトの影響は不要です。Unlitに設定します。

![MaterialGraph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/21471499-2cae-42dd-de43-20e40ba79bba.png)
MaterialGraphを設定します。
- [x] TextureSample
TextureSampleの詳細で先ほど作成したMediaTextureを指定します。
RGBをEmissiveに繋ぎます。
- [x] ScreenPosition
カメラの位置に依存せずViewPortの全体で動画をプレビューするために作成します。
ViewportUVとTextureSampleのUVsをつなぎます。


![Camera.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/831b0ba6-ea91-5025-9d38-ed06b4698afa.png)
シーンに動画プレビュー用の巨大なPlaneを配置します。
画面全体に動画を表示したいので
トラッキング用のカメラのViewを覆うサイズまで拡大します。
(Floorなどは非表示にしておきます。)
![ViewportView.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/b4697985-c3c0-6659-6b9a-81cff56d9189.png)
先ほど作成したマテリアルを適応します。

ViewPortのアスペクトで拡縮するので、
トラッキング用のカメラで再生すれば正しいアスペクトで再生されます。


### 5.BluePrintで再生システムを組む
OpenLevelBlueprintで、Playボタンを押すとトラッキングカメラが動画と同時にループ再生される
簡易プレビューの仕組みを作成します。
(BlankなどでPlayerStartがデフォルトで配置されている場合は消してください。)
![OpenLevelBlueprint.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/5fce5329-c45e-f070-f45f-259264328b7d.png)

![Variable.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/85a000c4-503d-e515-e614-65bb8757c993.png)

左のVariableのパネルからMediaPlayerを<font color = "red">Object</font>として定義、そこから先ほどのMoviePlayerを探して適応します。
Compileを押すと先ほど作成したMediaPlayerが選択できるようになるのでそちらを選択します。

![DragDropAssets.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/a1690641-ac07-80f0-9f60-7641ae065dcc.png)


シーンに出ているアセットをDrag&Dropし、Setを選択することで
BluePrintで扱うことができるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/9fbb6d12-9f0d-b3da-062e-81e8a5add980.png)
OpenSourceのMediaSourceは再生する動画を指定しましょう。

![BluePrintDetails.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/67c8ea64-b55b-4520-0f07-d322355f62b0.png)
EventBeginPlay(始まって再生される瞬間)からの流れを順番に組み立てます。
最終的に画像のような形になりました。

初歩的な実装のためBluePrintの細かい使い方は割愛しますが、
こちらが参考になりました。
<iframe width="560" height="315" src="https://www.youtube.com/embed/wmOHDYjMprU?si=pGTaISyyk4wGdZaZ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>


### 6.3Dアセットの配置(UE)
![PilotView.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/a181edda-ca81-ab63-29b5-b54f9d170bed.png)
合成したい任意のアセットを配置します。
アイレベルの確認のため、Floorのアクタを表示して
トラッキング用のカメラのPilotモードで置いていくのがよさそうです。
ライティングなど自由に調整します。

### 7.アルファ付きの連番pngを書き出す(UE)
MovieRenderQueueで背景透過でレンダリングします。
![enablealphachannel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/b4373bbe-f557-13a3-28f5-36e6c28b8320.png)
Edit>ProjectSettings>Engine-Renderingから
Enable alpha channnel support in post processingの設定を
"Allow therough tonemapper"に変更します。
設定を反映するためRestartするかポップアップが出るので、
流れでプロジェクトを再起動します。
![ActorHiddenInGame.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/09c6c3d2-3792-f311-1df6-2251efbd45b5.png)
SkySphereやfog、Floorなど不必要な背景の要素は
シーケンサーの"Actor Hidden In Game"のVisibilityをfalseにすることで
レンダリングに表示されないようにしておきます。
![RenderingSettings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/006b5e6c-4eb2-a28e-3edc-7597340e4262.png)
レンダリングの設定をします。
今回はデフォルトで用意されていたStill_Draftを選択し、
png SequenceのExportを追加し、Write Alphaをtrueにします。
![pngSettings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/77f8d9cf-3415-628e-3fd9-bb872dba5d33.png)
透過の連番pngで書き出すことができました。
![pngseq.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/c988ea2e-4e0d-d2b0-f6b7-f763843dee00.png)

### 8.シャドウのみをレンダリングする(UE)
![shadow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/e636e51c-d6aa-837a-6f34-5ac4bf0d42b8.png)
Floorを消してレンダリングしたので、落ち影がレンダリングされていません。
影は別でレンダリングして後からAEで合成していきます。
![LightSourceSettings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/352657af-0432-6563-986c-fff1fa74b7f0.png)
LightSourceのRotationで日光の時間帯、SourceAngleで落ち影のシルエットを調整して
実写に馴染むよう調整します。
![HiddenShadow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/1c65daaf-3e75-b984-1bce-76284266a754.png)
対象のアセットのHiddenShadowとActor Hidden in Gameをtrueにします。
この設定で再生時にメッシュは表示されないけれど影のみが表示できます。

Floorには反射のない真っ白なマテリアルを適応して、
不必要なアセットは先ほどのレンダリングと同様にActor Hidden in Gameで
消した状態で書き出します。
![ShadowOnly.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/a64fd8b5-318a-48bd-4468-80996beee3bc.png)
影の連番pngの用意ができました。
これでUEからの書き出しは終了です。

### 9.コンポジット(AE)

AEでそれぞれを合成していきます。
影のみの動画は乗算で好きな濃度に調整していきます。
任意の拡張子に書き出して完成です。
![Composite.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/c3f1a86e-f463-96e8-78dd-47d90f891c3b.png)

## おわりに
かなりのボリュームですが一度ワークフローを確立すると
汎用的に使いまわせそうですね。
UEはどんどんアップデートしていくので、
現在はもっと簡単な方法があるかもしれません。
ご存知の方は是非教えていただけると嬉しいです。