LoginSignup
8
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-06-09

環境

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 );
        }
    }
}
8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2