公司建设网站的公司,山西省网站建设制作,网站开发费用摊销时间,wordpress 模板 管理Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示 目录
Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示
一、简单介绍
二、共享纹理
1、共享纹理的原理
2、共享纹理涉及到的关键知识点
3、什么可以实现共享
不能实现共享…Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示 目录
Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示
一、简单介绍
二、共享纹理
1、共享纹理的原理
2、共享纹理涉及到的关键知识点
3、什么可以实现共享
不能实现共享的情况
4、共享纹理中的注意事项
三、注意事项
四、效果预览
五、简单实现 Android 共享图片给 Unity 显示 案例
1、案例环境
2、Android 端
3、Unity 端
六、关键代码 一、简单介绍
Unity 是一个功能强大的跨平台游戏引擎广泛用于开发视频游戏和其他实时3D互动内容如模拟器和虚拟现实应用。 游戏引擎 UnityUnity Technologies 开发的跨平台游戏引擎支持2D和3D图形、物理引擎、音频、视频、网络和多平台发布。 跨平台支持Unity 支持在多个平台上发布包括 Windows、macOS、Linux、iOS、Android、WebGL、PlayStation、Xbox、Switch 等。 开发环境 Unity Editor用于创建和管理 Unity 项目的集成开发环境IDE。开发者可以在其中创建场景、设计关卡、编写代码和调试游戏。场景SceneUnity 中的基本构建块一个场景可以被视为一个关卡或一个游戏中的独立部分。 编程语言 C#主要编程语言。Unity 使用 C# 脚本来控制游戏对象和实现游戏逻辑。UnityScript已弃用曾经支持的 JavaScript 变种但已经被弃用。 Unity 进阶开发涉及更复杂的技术和更深入的知识以创建高性能、复杂和专业的游戏和应用程序。以下是一些 Unity 进阶开发的关键领域和技术 设计模式 学习和应用常见的设计模式如单例模式、工厂模式、观察者模式以便更好地组织和管理代码。 异步编程 使用 C# 的 async 和 await 关键字进行异步编程以提高应用的响应速度和性能。 依赖注入 使用依赖注入如 Zenject来管理对象的依赖关系减少耦合提高代码的可测试性和可维护性。 二、共享纹理
共享纹理指的是在不同的图形处理环境例如 Unity 和 Android 原生代码之间共享图像数据而不需要进行图像数据的复制。这样可以大大提升性能减少内存使用因为避免了重复加载和处理同一图像数据的开销。 1、共享纹理的原理 共享纹理的基本原理是通过创建一个共享的图形上下文context并在不同的渲染环境中使用同一个纹理对象。主要涉及以下几个步骤 创建共享的EGL上下文 EGLEmbedded-System Graphics Library用于管理图形上下文和绘图表面。通过EGL可以创建一个共享的上下文使得不同的线程可以访问同一个纹理。 生成OpenGL纹理 在共享的EGL上下文中生成一个OpenGL纹理对象。 在Unity中使用该纹理 在Unity中通过Texture2D.CreateExternalTexture方法创建一个引用共享纹理的Texture2D对象从而在Unity的渲染环境中使用该纹理。 2、共享纹理涉及到的关键知识点 EGLContext EGLContext是EGL用于管理OpenGL ES图形上下文的对象。通过创建共享的EGLContext可以在不同的线程之间共享OpenGL资源。 EGLSurface EGLSurface是EGL用于表示绘图表面的对象。可以是窗口表面、Pbuffer像素缓冲区表面或屏幕外渲染表面。 OpenGL ES OpenGL ES是OpenGL的一个子集专门用于嵌入式系统如移动设备。在共享纹理的过程中OpenGL ES提供了创建和操作纹理的API。 Texture2D.CreateExternalTexture Unity中的方法用于创建一个引用外部纹理的Texture2D对象。这是将共享的OpenGL纹理引入Unity渲染环境的关键步骤。 3、什么可以实现共享 OpenGL纹理可以通过共享的EGLContext在不同的线程或进程中共享。Pbuffer Surface可以用于共享离屏渲染的纹理。 不能实现共享的情况 不同设备之间共享纹理通常仅限于同一设备上的不同进程或线程之间。非OpenGL资源非OpenGL的图形资源如CPU内存中的图像数据不能直接通过EGL共享。 4、共享纹理中的注意事项 同步问题 在多线程环境中使用共享纹理时需要注意同步问题避免不同线程同时对同一纹理进行读写操作导致数据竞争或一致性问题。 上下文管理 确保正确管理EGLContext和EGLSurface的生命周期。在创建和销毁上下文时需要注意资源的正确释放。 性能优化 共享纹理可以减少数据复制提高性能但仍需注意渲染流程中的其他瓶颈进行整体的性能优化。 兼容性 不同设备和操作系统版本可能对EGL和OpenGL ES的支持有所不同。在实现共享纹理时需要考虑设备兼容性问题确保在目标设备上正常运行。 三、注意事项
1、一定要注意 Unity 端设置的 多线程渲染 关闭掉以及 移除 Vulkan 配置不然会报错创建共享失败共享id 会是 0
2、共享图片的可能会翻转注意合理调整 四、效果预览 五、简单实现 Android 共享图片给 Unity 显示 案例
1、案例环境
Windows 10Android Studio Dolphin | 2021.3.1 Patch 1Unity 2021.3.16f
2、Android 端
该案例中使用的OpenGL和EGL函数主要用于设置OpenGL环境创建和管理纹理。EGL14和GLES20是Android的OpenGL ES 1.4和2.0的API。这段代码是Android原生代码与Unity集成的一个例子允许Unity访问和操作OpenGL纹理。
该类封装了OpenGL ES 2.0的初始化和纹理管理其主要包括下面参数与函数 1、成员变量: mSharedEglContext: 共享的EGL上下文从Unity线程获取。mSharedEglConfig: 共享的EGL配置。mTextureID: OpenGL纹理ID。mTextureWidth 和 mTextureHeight: 纹理的宽度和高度。mRenderThread: 一个执行服务用于在单独的线程上执行渲染任务。 2、构造函数 (public NativeAndroidApp()): 初始化一个单线程的执行服务用于执行OpenGL的初始化和渲染任务。 3、setupOpenGL: 这个方法被Unity调用用于设置OpenGL环境。它执行以下步骤 获取当前Unity线程的EGL上下文和显示。获取Unity绘制线程的EGL配置。在渲染线程上初始化OpenGL环境并生成OpenGL纹理ID。 4、initOpenGL: 私有方法用于初始化OpenGL环境。它包括 获取默认的EGL显示。初始化EGL获取版本信息。创建EGL上下文指定OpenGL ES 2.0。创建一个像素缓冲区表面PbufferSurface。将EGL上下文和表面设置为当前。 5、getStreamTextureID, getStreamTextureWidth, getStreamTextureHeight: 这些公共方法被Unity调用用于获取纹理ID和尺寸信息。 6、updateTexture: 这个方法被Unity调用以更新纹理内容。虽然这个方法的实现在代码片段中没有给出但它可能涉及将新的图像数据绑定到OpenGL纹理并使用glTexImage2D等函数更新纹理。 1打开 Android Studio 创建一个 Android Library 2 然后添加一张测试图片来作为共享 3然后创建建一个脚本编写对应函数实现共享纹理图片的功能 4接着 ‘Build-Make Module xxxxxx’ 打包成 aar 3、Unity 端
实现Unity与Android原生代码之间的纹理共享。它通过调用Android端的Java代码来获取和更新纹理数据并在Unity中显示这些纹理。以下是脚本的主要功能和关键函数的说明脚本通过与Android原生层交互实现了Unity与Android之间的纹理共享 1、成员变量: mGLTexCtrl: AndroidJavaObject 类型用于调用Java层的方法。mTexture2D: Texture2D 类型Unity中的纹理对象用于在Unity中显示纹理。mTextureId: 存储从Java层获取的纹理ID用于创建外部纹理。mWidth 和 mHeight: 存储纹理的宽度和高度。 2、Awake函数: 在Unity对象初始化时调用用于实例化AndroidJavaObject并调用Java层的setupOpenGL方法来初始化OpenGL环境。 3、Start函数: 在Unity对象启动时调用用于绑定纹理到UI组件RawImage。 4、BindTexture函数: 调用Java层的方法获取纹理ID、宽度和高度。使用获取到的纹理ID创建Unity的Texture2D对象格式为RGBA32不具有Mipmaps不使用Readable设置。将创建的纹理赋值给RawImage组件的texture属性以便在UI上显示。 5、Update函数: 每帧调用用于调用UpddateTextureate方法更新纹理数据。 6、UpddateTextureate函数: 调用Java层的updateTexture方法来更新纹理数据。这个方法应该在每帧或者在特定条件下调用以确保纹理内容是最新的。 7、OnDestroy函数: 当Unity对象被销毁时调用用于释放纹理资源避免内存泄漏。 1创建一个 Unity 工程创建文件夹 Plugins-Android 把之前的生成的 aar 添加到文件夹中 2在场景中添加一个 RawImage 用来显示共享的纹理图片 3创建一个脚本调用 Android 端封装暴露的纹理共享接口 4把创建的脚本挂载到场景中对应赋值 RawImage 5把平台切换到 Android 对应关闭 设置中的多线程渲染以及 移除 Vulkan 6、打包在设备上运行效果如下 7可能你会发现图片反了你可以Android端修改也可以 Unity端修改这里不在赘述最后调整效果如下 六、关键代码
1、TextureShareDemo.java
package com.ffalcon.unitytexturesharemodule;import static android.opengl.GLES20.glGetError;import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 共享纹理 Demo*/
public class TextureShareDemo {/*** 用于日志输出的标签*/private static final String TAG [TextureShareDemo] ;/*** 共享的EGL上下文从Unity线程获取*/private EGLContext mSharedEglContext;/*** 共享的EGL配置*/private EGLConfig mSharedEglConfig;/*** OpenGL纹理ID*/private int mTextureID;/*** 纹理宽度*/private int mTextureWidth;/*** 纹理高度*/private int mTextureHeight;/*** 用于OpenGL渲染操作的线程池*/private ExecutorService mRenderThread;/*** 构造函数初始化线程池*/public TextureShareDemo() {mRenderThread Executors.newSingleThreadExecutor();}/*** 由Unity调用用于设置OpenGL环境* 此方法在Unity线程执行*/public void setupOpenGL() {Log.d(TAG, setupOpenGL called by Unity);// 获取Unity线程当前的EGL上下文和显示mSharedEglContext EGL14.eglGetCurrentContext();EGLDisplay sharedEglDisplay EGL14.eglGetCurrentDisplay();// 获取Unity绘制线程的EGL配置int[] numEglConfigs new int[1];EGLConfig[] eglConfigs new EGLConfig[1];if (!EGL14.eglGetConfigs(sharedEglDisplay, eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) {Log.e(TAG, eglGetConfigs failed);return;}mSharedEglConfig eglConfigs[0];// 在渲染线程上执行OpenGL初始化和纹理创建mRenderThread.execute(new Runnable() {Overridepublic void run() {initOpenGL();// 生成OpenGL纹理IDint textures[] new int[1];GLES20.glGenTextures(1, textures, 0);if (textures[0] 0) {Log.e(TAG, glGenTextures failed);return;}mTextureID textures[0];// 这里设置的纹理尺寸是示例值mTextureWidth 670;mTextureHeight 670;}});}/*** 初始化OpenGL环境*/private void initOpenGL() {Log.d(TAG, initOpenGL: );EGLDisplay mEGLDisplay EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);int[] version new int[2];if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {Log.e(TAG, Failed to initialize EGL.);return;}// 创建EGL上下文指定OpenGL ES 3版本int[] eglContextAttribList new int[]{EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,EGL14.EGL_NONE};EGLContext mEglContext EGL14.eglCreateContext(mEGLDisplay, mSharedEglConfig, mSharedEglContext, eglContextAttribList, 0);if (mEglContext EGL14.EGL_NO_CONTEXT) {Log.e(TAG, Failed to create EGL context.);return;}// 创建像素缓冲区表面int[] surfaceAttribList {EGL14.EGL_WIDTH, 64,EGL14.EGL_HEIGHT, 64,EGL14.EGL_NONE};EGLSurface mEglSurface EGL14.eglCreatePbufferSurface(mEGLDisplay, mSharedEglConfig, surfaceAttribList, 0);if (mEglSurface EGL14.EGL_NO_SURFACE) {Log.e(TAG, Failed to create EGL surface.);return;}// 设置EGL上下文和表面为当前if (!EGL14.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEglContext)) {Log.e(TAG, Failed to make EGL context current.);return;}GLES20.glFlush();// 检查OpenGL是否有错误发生int error glGetError();if (error ! GLES20.GL_NO_ERROR) {Log.e(TAG, OpenGL error: error);}}/*** 获取纹理ID* return 纹理ID*/public int getStreamTextureID() {Log.d(TAG, getStreamTextureID: mTextureID mTextureID);return mTextureID;}/*** 获取纹理宽度* return 纹理宽度*/public int getStreamTextureWidth() {Log.d(TAG, getStreamTextureWidth: mTextureWidth mTextureWidth);return mTextureWidth;}/*** 获取纹理高度* return 纹理高度*/public int getStreamTextureHeight() {Log.d(TAG, getStreamTextureHeight: mTextureHeight mTextureHeight);return mTextureHeight;}/*** 更新纹理内容* 此方法应在渲染线程中调用*/public void updateTexture() {mRenderThread.execute(new Runnable() {Overridepublic void run() {// 从资源中加载位图final Bitmap bitmap BitmapFactory.decodeResource(getUnityContext().getResources(), R.drawable.wutiaowu);if (bitmap ! null) {// 绑定纹理IDGLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);// 设置纹理参数GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);// 将位图数据上传到纹理GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);// 解绑纹理GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);// 回收位图占用的内存bitmap.recycle();} else {// 如果位图加载失败记录错误日志android.util.Log.e(TAG, Failed to load bitmap.);}}});}// region 获取Unity的Activity和Context/*** 设置一个 Activity 参数*/protected static Activity unityActivity;/*** 通过反射获取Unity的Activity* return Unity的Activity*/protected static Activity getActivity() {// 反射获取当前Activity如果尚未获取则尝试通过UnityPlayer类获取if (null unityActivity) {try {Class? classtype Class.forName(com.unity3d.player.UnityPlayer);unityActivity (Activity) classtype.getDeclaredField(currentActivity).get(classtype);} catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) {e.printStackTrace();}}return unityActivity;}/*** 获取Unity的上下文Context* return Unity的上下文Context*/protected static Context getUnityContext(){return getActivity().getApplicationContext();}//endregion}2、TextureShareDemo.cs
using System;
using UnityEngine;
using UnityEngine.UI;/// summary
/// Android Unity 共享纹理 demo
/// /summary
public class TextureShareDemo : MonoBehaviour
{// 用于日志输出的标签const string TAG [UnityNativeTexture] ;// AndroidJavaObject对象用于与Java代码交互private AndroidJavaObject mGLTexCtrl;// Unity中的Texture2D对象用于显示纹理private Texture2D mTexture2D;// 纹理ID用于创建外部纹理private int mTextureId 0;// 纹理的宽度private int mWidth;// 纹理的高度private int mHeight;// Unity UI组件RawImage用于显示纹理public RawImage RawImage;/// summary/// 在Unity初始化时调用/// /summaryvoid Awake(){// 创建AndroidJavaObject实例与Java层的TextureShareDemo类交互// 这里的包名和类名应与实际Java类名一致mGLTexCtrl new AndroidJavaObject(com.ffalcon.unitytexturesharemodule.TextureShareDemo);// 调用Java层的方法以初始化OpenGL上下文mGLTexCtrl.Call(setupOpenGL);}/// summary/// 在Unity开始时调用/// /summaryvoid Start(){// 绑定纹理到RawImage组件BindTexture();}/// summary/// 绑定纹理的方法/// /summaryvoid BindTexture(){Debug.Log(TAG BindTexture : );// 从Java层获取纹理ID、宽度和高度mTextureId mGLTexCtrl.Callint(getStreamTextureID);mWidth mGLTexCtrl.Callint(getStreamTextureWidth);mHeight mGLTexCtrl.Callint(getStreamTextureHeight);// 检查纹理ID是否有效if (mTextureId ! 0){Debug.Log(TAG BindTexture : mTextureId mTextureId);// 使用获取到的纹理ID创建外部纹理mTexture2D Texture2D.CreateExternalTexture(mWidth, mHeight, TextureFormat.RGBA32, false, false, (IntPtr)mTextureId);// 将创建的纹理赋值给RawImage组件的texture属性RawImage.texture mTexture2D;}else{// 如果纹理ID无效记录错误日志Debug.LogError(TAG BindTexture : Failed to get valid texture ID from Android.);}}/// summary/// 每帧调用的Update方法/// /summaryprivate void Update(){// 调用UpdateTextureate方法来更新纹理数据UpddateTextureate();}/// summary/// 更新纹理数据的方法/// /summaryvoid UpddateTextureate(){// 这里应该是一个拼写错误正确的方法名应该是UpdateTexture// 调用Java层的方法来更新纹理数据if (mGLTexCtrl ! null mTextureId ! 0){Debug.Log(TAG Update : mGLTexCtrl.Call(\updateTexture\) mTextureId mTextureId);mGLTexCtrl.Call(updateTexture);}}/// summary/// 当脚本对象被销毁时调用/// /summaryvoid OnDestroy(){// 确保在销毁时释放纹理资源if (mTexture2D ! null){// 释放纹理资源mTexture2D null;}}
}参考文献Unity安卓共享纹理