在上一节中我们介绍了多线程OpenGL绘制方案,但是如果需要在Java线程不断修改纹理数据,会由于并发访问导致Unity线程出现访问非法内存而崩溃。因此,考虑在Java线程加载数据,然后在unity线程调用OpenGL操作更新纹理。这样所有的OpenGL操作都在Unity绘制线程完成,从而避免了多线程OpenGL引入的各种问题。为了能够从Java线程切换到Unity线程执行,我们获取到Unity线程的Looper,然后使用该Looper实例化一个Handler,这样就可以通过往上发送消息或者Runnable在Unity线程执行任务了。Java代码如下:
1 package com.thornbirds.unity; 2 3 import android.graphics.Bitmap; 4 import android.graphics.BitmapFactory; 5 import android.opengl.GLES10; 6 import android.opengl.GLES11Ext; 7 import android.opengl.GLES20; 8 import android.opengl.GLUtils; 9 import android.os.Handler; 10 import android.os.Looper; 11 import android.util.Log; 12 13 import java.util.concurrent.ExecutorService; 14 import java.util.concurrent.Executors; 15 16 public class PluginTexture { 17 private static final String TAG = "PluginTexture"; 18 19 private int mTextureID = 0; 20 private int mTextureWidth = 0; 21 private int mTextureHeight = 0; 22 23 // 创建单线程池,用于加载图片资源 24 private final ExecutorService mJavaThread = Executors.newSingleThreadExecutor(); 25 // 使用Unity线程Looper的Handler,用于执行Java层的OpenGL操作 26 private Handler mUnityRenderHandler; 27 28 public int getStreamTextureWidth() { 29 return mTextureWidth; 30 } 31 32 public int getStreamTextureHeight() { 33 return mTextureHeight; 34 } 35 36 public int getStreamTextureID() { 37 return mTextureID; 38 } 39 40 public PluginTexture() { 41 } 42 43 private void glLogE(String msg) { 44 Log.e(TAG, msg + ", err=" + GLES10.glGetError()); 45 } 46 47 public void setupOpenGL() { 48 // 注意:该调用一定是从Unity绘制线程发起 49 if (Looper.myLooper() == null) { 50 Looper.prepare(); 51 } 52 mUnityRenderHandler = new Handler(Looper.myLooper()); 53 // 生成OpenGL纹理ID 54 int textures[] = new int[1]; 55 GLES20.glGenTextures(1, textures, 0); 56 if (textures[0] == 0) { 57 glLogE("glGenTextures failed"); 58 return; 59 } 60 mTextureID = textures[0]; 61 mTextureWidth = 640; 62 mTextureHeight = 360; 63 } 64 65 public void updateTexture() { 66 mJavaThread.execute(new Runnable() { 67 @Override 68 public void run() { 69 // 加载图片资源 70 String imageFilePath = "/sdcard/test/image.png"; 71 final Bitmap bitmap = BitmapFactory.decodeFile(imageFilePath); 72 // 切换到Unity绘制线程更新纹理 73 mUnityRenderHandler.post(new Runnable() { 74 @Override 75 public void run() { 76 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID); 77 GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); 78 GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); 79 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); 80 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); 81 bitmap.recycle(); 82 } 83 }); 84 } 85 }); 86 } 87 88 public void destroy() { 89 mJavaThread.shutdownNow(); 90 mUnityRenderHandler.removeCallbacksAndMessages(null); 91 } 92 }
至此,我们完整介绍了Unity3D开发中在Android Java Plugin中进行纹理更新的方案和实现方法。基于相同的原理,我们可以很方便地给出iOS Object-C Plugin的实现,此处不在赘述。
时间: 2024-11-03 03:41:59