Android用ネイティブレンダリングPlugin(c++[so])の作成
※この記事は2017年06月09日にqiitaに投稿した内容です。
環境
Unity5.6.1f1 Javaのプラグイン作成ができる状態 Android用Plugin(java[aar])の作成
概要
低レベルネイティブプラグインインターフェースを使用して、c++側でテクスチャの内容を書き換えます https://docs.unity3d.com/ja/540/Manual/NativePluginInterface.html
AndroidStudioとUnityの事前設定
・以下を参照してプロジェクトを作成する(例:プロジェクト名:TestNativeCppRender) Android用Plugin(java[aar])の作成 今回はaarを出力しないのでプラグインのexportAarタスクは書かなくてもいい Javaクラスを追加する必要もない
UnityEditorの設定
・プロジェクトを開き、「PlayerSetting」>「OherSetting」>「MultiThreadedRendering」にチェックを入れる
AndroidStudioの設定
c++プロジェクトの追加
・プロジェクトを開き、プロジェクトツリーからプラグイン名(例:unityplugin)を選択する ・右クリックし、「ShowinExplorer」を押し、フォルダを開く ・「プラグイン名(例:unityplugin)」/src/main/に移動し、「cpp」フォルダを作成し、移動する ・「CMakeLists.txt」をテキストエディタ等で作成する この時点では内容は空のファイルにする ・プロジェクトツリーからプラグイン名(例:unityplugin)を選択する ・右クリックし、「LinkC++ProjectWithGradle」を押す NDKが「sdk\ndk-bundle」にない場合インストールを促されるのでインストールする Error:InstallNdkAndsyncprojectをクリックする ・ダイアログの「ProjectPath」で作成した、cppフォルダ内の「CMakeLists.txt」を選択し、OKを押す ・OKを押す ・プロジェクトツリーの根元の部分のタグ(左上)が「Android」になっているので「Project」に変更する
IUnityGraphics インターフェースの追加
・以下のフォルダにある
C:\Program Files\Unity\Editor\Data\PluginAPI
・「PluginAPI」フォルダを[cpp]フォルダの中にフォルダごとコピーし、「Unity」に変更
・以下のURLにアクセスする
https://bitbucket.org/Unity-Technologies/graphicsdemos/downloads/
・「リポジトリをダウンロードする」を押し、ファイルを保存する
・内部に入っている「Unity」フォルダを作成した[cpp]フォルダの中にフォルダごとコピーする
「NativeRenderingPlugin/PluginSource/source/Unity」 -> 「cpp/Unity」
IUnityGraphicsXXX.hが6ファイルある
cppファイルの追加
・[cpp]フォルダの中にxxx.cppを追加する(例:native-lib.cpp) 例のnative-lib.cppの内容は下記に記載
CMakeLists.txtの書き換え
・[cpp]フォルダの中の「CMakeLists.txt」を開き、内容を記述する 例のCMakeLists.txtの内容は下記に記載 「add_library」の内部に追加したcppを記載する(例:native-lib.cpp) ヘッダファイルの入った「Unity」フォルダを追加しているので「include_directories」の中に「Unity」を記述 GLES2.0を使用するので「target_link_libraries」の中に「GLESv2」を記述
exportSoの追加
・プロジェクトツリーから「プラグイン名」>「build.gradle」を開く ・ファイルの最後に下記を追加する Aarの時と同じ方法で「exportSo」をダブルクリックし、ビルドする soファイル名はlibから始まらないとならないので自動で付与されるようにしている
def OUTPUT_SONAME='nativecpprender'
def ABI_NAME='armeabi-v7a'
task exportSo(type: Copy){
from( 'build/intermediates/cmake/release/obj/' + ABI_NAME + '/' )
into( '../../../Assets/Plugins/Android/' )
include( 'libnative-lib.so' )
rename( 'libnative-lib.so', 'lib' + OUTPUT_SONAME + '.so' )
}
exportSo.dependsOn( build )
CMakeLists.txt
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp )
# Specifies a path to native header files.
include_directories( Unity/ )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib}
GLESv2
)
プログラム
テクスチャを作成し、ポインタをc++側に渡し、c++側でテクスチャを黒から赤になるように書き換えています
native-lib.cpp
#include "IUnityInterface.h"
#include "IUnityGraphics.h"
#include <math.h>
#include <stdio.h>
#include <assert.h>
#include <GLES2/gl2.h>
static GLuint g_textureId = NULL;
static int g_texWidth;
static int g_texHeight;
static u_char* g_pBytes = NULL;
#define LOG_PRINTF printf
extern "C" bool SetupNativeTextureRender( void* textureId, int width, int height )
{
g_textureId = (GLuint)(size_t)textureId;
g_texWidth = width;
g_texHeight = height;
LOG_PRINTF( "SetupNativeTextureRender:%d, %d, %d", g_textureId, g_texWidth, g_texHeight );
g_pBytes = new u_char[ g_texWidth * g_texHeight * 4 ];
return true;
}
extern "C" void FinishNativeTextureRender()
{
if( g_pBytes != NULL )
delete[] g_pBytes;
g_pBytes = NULL;
}
static void UNITY_INTERFACE_API
OnRenderEvent( int eventID )
{
glBindTexture( GL_TEXTURE_2D, g_textureId );
static u_char s_r = 0;
u_char* bytes = g_pBytes;
for( int y = 0; y < g_texHeight; y++ )
{
for( int x = 0; x < g_texWidth; x++ )
{
int offset = ( ( y * g_texWidth ) + x ) * 4;
bytes[ offset + 0 ] = s_r;
bytes[ offset + 1 ] = 0;
bytes[ offset + 2 ] = 0;
bytes[ offset + 3 ] = 255;
}
}
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, g_texWidth, g_texHeight, GL_RGBA, GL_UNSIGNED_BYTE, bytes );
s_r ++;
s_r %= 255;
}
extern "C" UnityRenderingEvent UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
GetRenderEventFunc()
{
return OnRenderEvent;
}
Assets/TestNativeCppRender.cs
using System;
using System.Collections;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;
public class TestNativeCppRender : MonoBehaviour
{
[SerializeField] private RawImage _rawImage = null;
[SerializeField] private int _width = 512;
[SerializeField] private int _height = 512;
//PluginFunction
[DllImport ("nativecpprender")]
private static extern bool SetupNativeTextureRender( IntPtr textureId, int width, int height );
[DllImport ("nativecpprender")]
private static extern void FinishNativeTextureRender();
[DllImport ("nativecpprender")]
private static extern IntPtr GetRenderEventFunc();
private void Start()
{
var texture = new Texture2D( _width, _height, TextureFormat.ARGB32, false );
_rawImage.texture = texture;
if( SetupNativeTextureRender( texture.GetNativeTexturePtr(), texture.width, texture.height ) == false )
return;
StartCoroutine( NativeTextureRenderLoop() );
}
private void OnDestroy()
{
FinishNativeTextureRender();
}
private IEnumerator NativeTextureRenderLoop()
{
while( true )
{
yield return new WaitForEndOfFrame();
GL.IssuePluginEvent( GetRenderEventFunc(), 1 );
}
}
}