はじめに
こんにちは、技術開発部のOです。
普段はグラフィックスエンジニアのマネージャーとして、UnityやUnrealを問わず、レンダリング・シェーダー・パフォーマンス最適化など、CGに関わる幅広い領域をサポートするのが日々の仕事です。
過去の記事はこちらからご覧いただけます。
今回は、我々がかねてより開発していたもので、社内向けOBSおよびSpoutプラグインを紹介します。
背景
自社開発の配信・収録アプリ「スタジオアプリ」があります。
モーションキャプチャーや照明信号の適用から、VFXまでをリアルタイムにレンダリングを行い映像を送出するアプリケーションです。
主な使用方法はリアルタイムですが、収録の際にはポスト編集のための付加情報(マスク・深度・法線など)を出力してほしいという要望が強くありました。
これらの実装を検討した所、既存のスタジオアプリのリリース計画とのすり合わせや提供までにかかる時間が課題となっていました。
作ったもの

そこでスタジオアプリ側はSpoutの出力までを担当し、それ以後の加工・ファイル書き出しはすべてOBSに委ねる構成にしました。これにより、リリース計画に依存しないフットワークの軽い開発が実現し、UnityやUnreal Engineを問わない共通実装も可能になりました。
実装の過程で、映像素材出力だけでなく、通常のOBSフィルタでは実現できないGバッファを活用した3D演出もリアルタイムで可能に なりました。
開発したものは以下の3つです。
- スタジオアプリをはじめとする社内の内製ソフトウェアで使用される共通描画基盤からのSpout送出機能
- Spout受信OBSプラグイン
- 上記2機能と連携することでリッチなポスト表現を実現するOBSプラグイン
これらのプラグインにより、以下を実現しています。
- OBS上での3次元空間の再構築
- デカールレンダリング
- リライト(直接光・AO)
- 同期された映像編集素材の同時書き出し(floatバッファ可)
- 法線 / ワールド位置 / 深度 / カメラ情報 / リニアカラー / 最終カラー
- リッチなフィルターの提供
- ディフュージョン / DOF / レンズゆがみ
以下、各要素を詳しく解説します。
Spout
Spoutとは、アプリ間でGPUメモリを直接共有する仕組みによって、非常に高速に映像を送受信できる仕組みです。
弊社ではHoloSpoutとして、社内アプリの共通描画基盤の一部として実装しています。これを組み込むことで、スタジオアプリをはじめ同じ基盤を使うさまざまなアプリでSpoutに対応しています。
HoloSpoutの実装は、著名なKlakSpoutからforkして、浮動小数バッファ対応と最適化を加えた実装になっています。
出力される要素は以下の通りです。
| 要素 | 名前 | 概要 | フォーマット |
| 最終出力カラー | Holo FinalColor | 最終3Dカラーです。 録画・配信にはこのカラーを使用します。 | R8G8B8A8_SRGB |
| 素材用カラー | Holo PreProcessColor | ポスト処理をかける前の素材用カラーで、 リニアかつHDR状態の素のカラーです。 | R16G16B16A16_SFloat |
| アルファ | Holo Alpha | アルファ値です。 | R8_Unorm |
| 深度 | Holo LinearDepth | 0〜1の線形深度値です。 | R16_Float |
| ワールド座標 | Holo WorldPosition | ワールド座標です。 | R16G16B16A16_SFloat |
| 法線 | Holo Normal | ワールド法線です。 法線が0〜1になっているため、 n * 2 – 1をして-1〜1に展開して使用します。 | R8G8B8A8_Unorm |
| マスク | Holo Mask | キャラクターマスクです。 1がキャラクターです。 | R8_Unorm |
| データ | Holo DataBuffer | データ配列バッファです。 ピクセルの値にデータが入っています。 別表参照。 | R32_SFloat |
Holo DataBufferには「画像」ではなく、カメラ位置・画角・各種行列など3次元空間の再構築に必要な情報を埋め込んでいます。先頭の数バイトにはデータ確認用の固定色が書き込まれており、その後にカメラ位置などの情報が続きます。
| 要素 | 概要 |
| COLOR_PATTERN_0 | RGBA=(0.0, 0.0, 0.0, 1.0) |
| COLOR_PATTERN_1 | RGBA=(1.0/6.0, 1.0/6.0, 1.0/6.0, 1.0) |
| COLOR_PATTERN_2 | RGBA=(2.0/6.0, 2.0/6.0, 2.0/6.0, 1.0) |
| COLOR_PATTERN_3 | RGBA=(3.0/6.0, 3.0/6.0, 3.0/6.0, 1.0) |
| COLOR_PATTERN_4 | RGBA=(4.0/6.0, 4.0/6.0, 4.0/6.0, 1.0) |
| COLOR_PATTERN_5 | RGBA=(5.0/6.0, 5.0/6.0, 5.0/6.0, 1.0) |
| COLOR_PATTERN_6 | RGBA=(6.0/6.0, 6.0/6.0, 6.0/6.0, 1.0) |
| COLOR_PATTERN_7 | RGBA=(1.0, 0.0, 0.0, 1.0) |
| COLOR_PATTERN_8 | RGBA=(0.0, 1.0, 0.0, 1.0) |
| COLOR_PATTERN_9 | RGBA=(0.0, 0.0, 1.0, 1.0) |
| COLOR_PATTERN_10 | RGBA=(1.0, 0.0, 1.0, 1.0) |
| COLOR_PATTERN_11 | RGBA=(0.0, 1.0, 1.0, 1.0) |
| COLOR_PATTERN_12 | RGBA=(1.0, 1.0, 0.0, 1.0) |
| CAMERA_POSITION | カメラ座標: xyz |
| CAMERA_UP | カメラ上方向: xyz |
| CAMERA_RIGHT | カメラ右方向: xyz |
| PROJECTION_PARAMS | プロジェクションパラメータ x: near y: far z: fov w: aspect |
| VIEW_MATRIX_ROW0〜ROW3 | ViewMatrix: 4×4 |
| PROJECTION_MATRIX_ROW0〜ROW3 | ProjectionMatrix: 4×4 |
| VIEW_PORT | Viewport: x, y, width, height |
OBS
OBSとは、Open Broadcaster Softwareの略で、ライブ配信や画面録画を行うための無料のオープンソースソフトウェアです。YouTubeやTwitchなどの主要な配信プラットフォームに対応しており、画面キャプチャやWebカメラの映像・音声などを自由に組み合わせて配信できます。Windows・Mac・Linuxに対応しており、初心者からプロまで幅広く利用されている定番ツールです。
C++によるプラグイン開発も容易であり、シェーダーを用いたフィルターの実装を低コストで実現できます。
我々の実装では、前述したSpoutと連携することで3次元空間を再構築し、OBSだけでは不可能なAOやリライトなどを実現しています。リアルタイム処理の他に、ポスト編集を見越した素材収録機能も実装しています。
OBSでこれらのフィルタを実装することの利点は以下の通りです。
- UnityやUEに関係なく共通した実装ができる。
- アプリケーションのリリースタイミングに依存せず、独立して更新できる。
では、これらの特性を活かして実現したフィルタを、具体的に紹介していきます。
3次元空間の再構築によるフィルタ群
まずはSpoutからの出力結果を利用し、3次元空間の再構築を行うことで実現するフィルタ群です。いわゆるディファードレンダリングのような処理を行っています。
こちらは何も適用していない、OBSに入力として入ってきた時点での画像です。

こちらに各種フィルターを追加していきます。
リライト
OBS上で新たなライトを定義します。画像では平行光を追加しています。

Spoutソースとして、法線・データバッファ・位置を使用します。平行光と点光源を選択できます。

AO(アンビエントオクルージョン)
簡易的に環境光の遮蔽を適用し、3D空間上のオブジェクトの設置感を改善します。タレントに適用すると意図しない陰影や中途半端なリアル感が出てしまうため、除外できるようになっています。


Spoutソースとして法線・キャラクターマスク・データバッファ・ワールド位置を使用します。(ワールド位置バッファでは無く、深度からも算出可能です。)
デカール
遮蔽を考慮したデカールを貼ることができます。画像では、hololiveのロゴが椅子などで遮蔽されつつ床に投影されていることが確認できます。

動画やキャプチャをソースにできるため、ゲーム画面やVJ映像を壁や床に自然に投影できます。

板描画
位置とサイズを指定して、再構築された3次元空間上に板を描画します。データバッファから得たカメラ情報と位置バッファを元にOBS上で定義した板とのレイトレーシングを行い、遮蔽を考慮しつつ描画を実現しています。以下の画像では、そらさんと椅子の間にCOVERロゴを描画しています。


各フィルターをすべて適用した結果です。


映像素材の出力
生配信を想定したリアルタイム処理以外にも、映像を編集するための素材出力機能も用意しています。通常、Spoutを素材ごとに取得するとフレームの同期が難しくなるため、4つのSenderを同時に取得するソースを作成しました。値の範囲を指定して0〜1を再定義するRemap機能があります。また、DataBufferを選択した際の特殊機能として、文字列で画角、アスペクト比などが表示されます。これらは動画として書き出された後、映像編集ソフトでのリライトやモーショントラッキング、マスキングの素材として使用されます。

ポストフィルター群
ディフュージョンフィルタ
UEで実装したものをOBSに移植しました。オーバーレイ処理とディフュージョンを組み合わせたものです。白飛びしないよう、指定した輝度以上には適用しない処理が入っています。


簡易DOF
深度などを参照せず、画面中央に焦点を合わせる簡易的なDOF風フィルタです。

まとめ・今後の展望
当初要望の有った素材出力の機能はOBSのエコシステムに載せることで依存も負担も少なく作ることができました。また、SpoutとOBSを組み合わせることで、通常では実現できない3次元空間の再構築を利用した演出を実装できました。
開発したプラグインについては、現在タレントを含め外部への配布(Distribution)は行わず、社内のみで使用しています。今後は、タレントにも配布できるよう、機密部分を別アプリ/DLL化してプラグインと分離することで、配布可能な状態へ移行することを課題としています。
さらに技術的な展望として、今回の構成でUnityとUnreal Engineに依存しない共通基盤ができた強みを活かし、将来的には両エンジンを組み合わせたハイブリッドレンダリングについても検証を進めていきたいと考えています。