VRChatで動画のフレームをTexture2Dとして取得する
この記事はUniMagic Advent Calendar 2024の7日目の記事です
相変わらずゆにまじに関係なさそうな話題ですが、一応関係あります
(そろそろ本当にネタが尽きてきました)
皆さんきっと動画プレイヤーの映像の特定フレームをTexture2Dとして取得したいと思ったことがあると思います
しかし、VRChatで利用可能なコンポーネントであるAVProPlayer
において、出力されるmaterialから映像に紐づくテクスチャを取得することはできますが、取得できるテクスチャは断続的に内容が更新されるものであり、特定のフレームを単体で取得することはできません
そこで、VRCGraphics
というAPIを用いてTexture2dへ焼く処理を実装してみました
ソースコード
public class VideoLoader : UdonSharpBehavior
{
private RenderTexture _tmpRenderTexture;
private int _vlTextureWidth;
private int _vlTextureHeight;
protected virtual void CopyToRenderTexture(Texture2D texture, bool flipHorizontal = false,
bool flipVertical = false)
{
_tmpRenderTexture = new RenderTexture(texture.width, texture.height, 0, RenderTextureFormat.ARGB32,
RenderTextureReadWrite.Linear);
_tmpRenderTexture.Create();
_vlTextureWidth = texture.width;
_vlTextureHeight = texture.height;
VRCGraphics.Blit(texture, _tmpRenderTexture, new Vector2(flipHorizontal ? -1 : 1, flipVertical ? -1 : 1),
new Vector2(flipHorizontal ? 1 : 0, flipVertical ? 1 : 0));
VRCAsyncGPUReadback.Request(_tmpRenderTexture, 0, (IUdonEventReceiver)this);
}
public override void OnAsyncGpuReadbackComplete(VRCAsyncGPUReadbackRequest request)
{
Destroy(_tmpRenderTexture);
_tmpRenderTexture.Release();
var data = new byte[_vlTextureWidth * _vlTextureHeight * 4];
request.TryGetData(data);
var readableText = new Texture2D(_vlTextureWidth, _vlTextureHeight, TextureFormat.RGBA32, false);
readableText.LoadRawTextureData(data);
readableText.Apply();
//readableTextをどうにかする
}
}
解説
_tmpRenderTexture = new RenderTexture(texture.width, texture.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
_tmpRenderTexture.Create();
書き出し先のRenderTextureを生成します
Unity公式はRenderTexture.GetTemporary
を使うことを推奨していますが残念ながらVRChatでは使えません
VRCGraphics.Blit(texture, _tmpRenderTexture, new Vector2(flipHorizontal ? -1 : 1, flipVertical ? -1 : 1),
Graphics.Blitの代替としてVRChat向けに提供されているVRCGraphics.Blitを使用します
おそらく純粋なラッパーだと考えて大丈夫です
RenderTextureで良い場合はここで完了です
VRCAsyncGPUReadback.Request(_tmpRenderTexture, 0, (IUdonEventReceiver)this);
Texture2Dに変換を行うため、RenderTextureのピクセルデータを読み取ります
ReadPixelsは重いのでVRCAsyncGPUReadback
を用いて非同期で読み取ります
Destroy(_tmpRenderTexture);
_tmpRenderTexture.Release();
読み取りが完了した(=ピクセルデータがすでに配列として取得できる)ため、不要になったRenderTextureを削除します
var data = new byte[_vlTextureWidth * _vlTextureHeight * 4];
request.TryGetData(data);
ピクセルデータを配列に読み取ります
var readableText = new Texture2D(_vlTextureWidth, _vlTextureHeight, TextureFormat.RGBA32, false);
最終的な書き出し対象のTexture2Dを生成します
readableText.LoadRawTextureData(data);
readableText.Apply();
ピクセルデータを読み込ませ、反映します
以上でReadableなTexture2Dを生成することに成功しました
おわりに
以上のコードは実際にスライドシステムで使用しているものです
VRChatにおいて、複数の画像を読み込む方法として動画はとても優秀なので、外部からのデータの取得を検討される場合は活用してみてください
宣伝
魔術学舎United、略してUniMagic(ユニマジック)とは、VRの世界に関係の深い技術を学ぶ学園空間型のイベントです。
15名~18名程度で構成されるクラスで、2週間の授業と1週間の修了制作を通して、技術についての知見を深めていきます。
Discussion