Android用ネイティブレンダリングPlugin(c++[so])の作成

#環境
Unity5.6.1f1
Javaのプラグイン作成ができる状態
<a href=http://qiita.com/fukaken5050/items/c9ac144344c5f6ff9f57 >Android用Plugin(java[aar])の作成</a>

#概要
低レベルネイティブプラグインインターフェースを使用して、c++側でテクスチャの内容を書き換えます
https://docs.unity3d.com/ja/540/Manual/NativePluginInterface.html

#AndroidStudioとUnityの事前設定
・以下を参照してプロジェクトを作成する（例：プロジェクト名:TestNativeCppRender）
　<a href=http://qiita.com/fukaken5050/items/c9ac144344c5f6ff9f57 >Android用Plugin(java[aar])の作成</a>
　今回は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 

~~~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

~~~c#
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 );
		}
	}
}
~~~