Android(Java)のSurfaceTextureのテクスチャをUnityで描画する方法

Android(Java)のSurfaceTextureのテクスチャをUnityで描画する方法をまとめます。



SurfaceTexture は、Javaで利用可能なAndroidのクラスです。
SurfaceTexture を利用すると、カメラやビデオの映像をテクスチャとして扱うことができます。

「Android(Java)のSurfaceTextureのテクスチャをUnityで描画する方法」についての情報は、ウェブ上にいくつもありますが、価値のある情報、価値のない情報が入り混じっており、苦労しました。
価値のない情報には、たとえば、解決に至っていない質問スレッドや、昔は正しかったかもしれないが2021年9月現在は正しくなさそうな情報、などが含まれます。

「Android(Java)のSurfaceTextureのテクスチャをUnityで描画する方法」を、失敗(「こうやってもうまくいかない」)も含めて、まとめます。
2021年9月時点における「こうやってもうまくいかなかった」「こうやってうまくいった」のまとめです。
使用ソフトウェア(Unity)、使用ライブラリ(Android SDK)のバージョンアップにより、「うまくいかなかった方法でできるようになる」「うまくいっていた方法でできなくなる」こともありえます。
「こうやってもうまくいかなかった」に関しては、実装変更や設定変更によって「うまくいくようになる」こともありえます。実装不備、設定不備等の問題点を発見された場合には「コメントを投稿する」もしくは「ホーム > コンタクト」より連絡していただけると幸いです。

使用開発環境は、以下です。
Unity : 2020.3.12f1
Android Studio : 4.2.2

01.Unity側で作成したテクスチャを、Java側でGL_TEXTURE_EXTERNAL_OESのテクスチャとしてSurfaceTextureに割り付けて使用する方法
結果としては、失敗。この方法はうまくいかなかった。

事象としては、glBindTexture() で GL_INVALID_OPERATION エラーが発生する。

考察としては、「Unityで作成したテクスチャは、GL_TEXTURE_2D をターゲットとして作成されているため、異なるターゲットである GL_TEXTURE_EXTERNAL_OES での glBindTexture() でエラーが発生する」と考える。

02.Unity側で作成したテクスチャを、Java側でGL_TEXTURE_2DのテクスチャとしてSurfaceTextureに割り付けて使用する方法
結果としては、失敗。この方法はうまくいかなかった。

事象としては、glBindTexture() でのエラーは発生しないが、SurfaceTexture#updateTexImage() でエラーが発生する。

考察としては、「SurfaceTextureに割り付けできるテクスチャは、GL_TEXTURE_EXTERNAL_OESをターゲットしてバインドできるテクスチャである必要がある。そのため、SurfaceTextureに、Unityで作成したGL_TEXTURE_2Dをターゲットとするテクスチャを、割り付けたとしても、SurfaceTexture#updateTexImage() でエラーが発生する」と考える。

03.テクスチャを異なるターゲットでバインドするとエラーが発生する事象の確認
結果としては、以下の事象を確認した。
・GL_TEXTURE_2Dでバインド、バインド解除した後に、GL_TEXTURE_EXTERNAL_OESでバインドするとエラーが発生する。
・GL_TEXTURE_EXTERNAL_OESでバインド、バインド解除した後に、GL_TEXTURE_2Dでバインドするとエラーが発生する。

04.Java側で作成したGL_TEXTURE_EXTERNAL_OESのテクスチャを、Unity側でUpdateExternalTexture()を利用し使用する方法
結果としては、失敗。この方法はうまくいかなかった。

事象としては、GLエラーは発生しないが、SurfaceTextureのテクスチャの内容は、Unityのテクスチャとして描画されない。

考察としては、「Texture2D#UpdateExternalTexture関数は、『GL_TEXTURE_2Dをターゲットとする』異なるネイティブのテクスチャで、Unityのテクスチャを更新する関数であり、『GL_TEXTURE_EXTERNAL_OESをターゲットとする』異なるネイティブのテクスチャで、Unityのテクスチャを更新しても、Unityのテクスチャとして描画されない」と考えます。

05.Java側で作成したGL_TEXTURE_EXTERNAL_OESのテクスチャを、Unity側でCreateExternalTexture()を利用し使用する方法
結果としては、失敗。この方法はうまくいかなかった。

事象としては、GLエラーは発生しないが、SurfaceTextureのテクスチャの内容は、Unityのテクスチャとして描画されない。

考察としては、「Texture2D#CreateExternalTexture関数は、『GL_TEXTURE_2Dをターゲットとする』外部で作成されたネイティブのテクスチャオブジェクトから Unity テクスチャを作成する関数であり、『GL_TEXTURE_EXTERNAL_OESをターゲットとする』外部で作成されたネイティブのテクスチャオブジェクトから Unity テクスチャを作成しても、Unityのテクスチャとして描画されない」と考えます。

06.Java側で作成したGL_Texture_2Dのテクスチャを、Unity側で扱うことができることの確認
結果としては、以下の事象を確認した。
・CreateExternalTexture関数を用いて、Java側で作成したGL_TEXTURE_2Dのテクスチャから、Unityのテクスチャを作成すると、Java側で作成したテクスチャがUnityテクスチャとして描画される。
・UpdateExternalTexture関数を用いて、Java側で作成したGL_TEXTURE_2Dのテクスチャで、Unityのテクスチャを更新すると、Java側で作成したテクスチャがUnityテクスチャとして描画される。

07.Java側で作成したGL_TEXTURE_EXTERNAL_OESのテクスチャを、Unity側で作成したGL_TEXTURE_2Dのテクスチャに転写する方法
前の項までで、以下が判明した。
・Java側のSurfaceTextureのテクスチャは、GL_TEXTURE_EXTERNAL_OESをターゲットとしてバインドできるテクスチャである必要がある。
・Unity側のテクスチャは、GL_TEXTURE_2Dをターゲットとしてバインドできるテクスチャである必要がある。

したがって、「Android(Java)のSurfaceTextureのテクスチャをUnityで描画する方法」として、「Java側でGL_TEXTURE_EXTERNAL_OES のテクスチャを作成し、SurfaceTextureに割り付ける。Unity側で表示する際には、Java側で作成した GL_TEXTURE_EXTERNAL_OES のテクスチャの画像を、Unity側で作成した GL_TEXTURE_2Dのテクスチャに転写する」方法に辿り着く。

結果としては、成功。この方法はうまくいった。

08.変化する画像を連続で描画
前の項で、Android(Java)のSurfaceTextureのテクスチャをUnityで描画できるようになりました。

ただし、画面に描画される画像が変わることはありませんでした。

本項では、変化する画像を連続で描画するように処理を変更します。

参考

OpenGL ES 2.0 Reference Pages
OpenGL ES 2.0のリファレンス
SurfaceTexture  |  Android Developers
Android(Java)のSurfaceTextureクラスのリファレンス
SurfaceTexture  |  Android Open Source Project
Android(Java)のSurfaceTextureクラスのアーキテクチャの説明
Unity - Scripting API: Texture2D.UpdateExternalTexture
Unity(C#)のTexture2D#UpdateExternalTextureの関数リファレンス
Unity - Scripting API: Texture2D.CreateExternalTexture
Unity(C#)のTexture2D#CreateExternalTextureの関数リファレンス
External texture rendering with Unity and Android | by Raju K | XRPractices | Medium
基本となるいくつかの説明は有用であった。ただし、このページの方法は、動作しなかった。
(「Unity側で作ったTextureを、Java側でGL_TEXTURE_EXTERNAL_OESターゲットでバインド」においてGL_INVALID_OPERATIONエラーが発生した)
c# - Rendering SurfaceTexture to Unity Texture2D - Stack Overflow
質問の回答の中に、「ImageReaderが必要」との回答あり。
ImageReaderの利用は、パフォーマンス不安があり、試していない。
(ImageReaderを利用すると、テクスチャの画像をImage型で取得できるが、Image型を介して処理する分の処理コストが生じ、パフォーマンスに影響を及ぼす)
unity3d - SurfaceTexture in Android plugin doesn't work in Unity - Stack Overflow
質問の回答の中に、「ImageReaderを利用したgithubリポジトリ上のサンプルプロジェクト」の提示あり。
ImageReaderの利用は、パフォーマンス不安があり、試していない。
(ImageReaderを利用すると、テクスチャの画像をImage型で取得できるが、Image型を介して処理する分の処理コストが生じ、パフォーマンスに影響を及ぼす)
Can an Android (Java not C/C++) plugin alter unity textures? - Stack Overflow
2021年9月時点で、質問スレッドは解決に至っていない?
Unity3d Render openGL FBO to texture in android (java) - Stack Overflow
2021年9月時点で、質問スレッドは解決に至っていない?
UnityとAndroid(Java)間のテクスチャに関する質問スレッドだが、SurfaceTextureに関しては触れられていない。
java - Unity 4.3 + Android - I Need to Update Texture2D or RenderTexture at least 30 FPS at 1080 x 1920 - Stack Overflow
2021年9月時点で、質問スレッドは解決に至っていない?
UnityとAndroid(Java)間のテクスチャに関する質問スレッドだが、SurfaceTextureに関しては触れられていない。