使用 GLES20 通过 Android 插件获取位图到 Unity
Get bitmap to Unity through Android plugin using GLES20
我正在尝试使用 UpdateExternalTexture
和 CreateExternalTexture
将来自 android 插件的简单位图图像传递到 unity 中。在文档中它说后者是为了与低级插件一起使用。我用 java 代码在上面一层。
我在进行任何 GLES20 调用时遇到上下文错误:
call to OpenGL ES API with no current context (logged once per thread)
根据此excellent question,这是因为上下文不正确(或有 none)。我按照该页面上的说明进行操作,并设法创建了一个上下文。但是,现在他们似乎指的是不同的资源 "areas"。我得到的指针是“1”,统一将其理解为 "current frame on the screen" 而不是我的位图。所以看起来像这样:
这是我的代码片段。首先是 Unity 部分。这是附加到场景中的游戏对象。
// This function is called shortly after Start
private void TestingTransfer() {
// get the current activity from unity
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
androidActivity = jc.GetStatic<AndroidJavaObject>("currentActivity");
// create a link to the android plugin
AndroidJavaObject obj = new AndroidJavaObject("com.test.pc.sample.SampleClass", androidActivity);
// Make the call to put the bitmap in the graphic memory and get it's PTR
Int32 texPtr = obj.Call<Int32>("PutBitmapInGraphicsCard");
Debug.Log("texture pointer == " + texPtr);
// Stage the texture in unity
Texture2D nativeTexture = Texture2D.CreateExternalTexture(100, 100, TextureFormat.ARGB32, false, false, (IntPtr)texPtr);
// Update it with the new PTR
nativeTexture.UpdateExternalTexture(nativeTexture.GetNativeTexturePtr());
// Notify other things in unity that this updated (delegate)
// I'm using this from another game object to pickup the new texture
if (OnNewImageReported != null) OnNewImageReported(nativeTexture);
}
现在 Java 一方。 Unity 通过 AAR 文件使用此代码。
private int PutBitmapInGraphicsCard() {
final int[] textureHandle = new int[1];
final Bitmap bitmap = GenerateTestImage(); // this creates a simple bitmap, blank with a blue circle in the center, works fine in android.
CreateContext(); // look below, it's defined there
GLES20.glGenTextures(1, textureHandle, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
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);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
return textureHandle[0];
}
// This function is a (almost) direct copy from the question linked above
public void CreateContext(){
// gets hold of the display
EGLDisplay dpy = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] vers = new int[2];
EGL14.eglInitialize(dpy, vers, 0, vers, 1);
// get some basic configs going
int[] configAttr = {
EGL14.EGL_COLOR_BUFFER_TYPE, EGL14.EGL_RGB_BUFFER,
EGL14.EGL_LEVEL, 0,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfig = new int[1];
EGL14.eglChooseConfig(dpy, configAttr, 0,
configs, 0, 1, numConfig, 0);
if (numConfig[0] == 0) {
Log.e("GLEDB-Error", "Could not find/create config!!");
}
EGLConfig config = configs[0];
// creating an offscreen (PBuffer) surface to render stuff
int[] surfAttr = {
EGL14.EGL_WIDTH, 100,
EGL14.EGL_HEIGHT, 100,
EGL14.EGL_NONE
};
EGLSurface surf = EGL14.eglCreatePbufferSurface(dpy, config, surfAttr, 0);
// create the context
int[] ctxAttrib = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
EGLContext ctx = EGL14.eglCreateContext(dpy, config, EGL14.EGL_NO_CONTEXT, ctxAttrib, 0);
// connect all these things together
EGL14.eglMakeCurrent(dpy, surf, surf, ctx);
}
所以,如果我对问题的理解是正确的,我需要的是引用 unity 用来绘制场景的相同上下文。有办法get/use吗?
解决方案:
如已接受的答案所述,主要问题是我使用的是多线程渲染。对于我的用例,这没有区别。
上面的代码并不能立即生效。需要注意的几点:
- 如果您这样做,请不要创建您自己的上下文。它搞砸了统一并阻止了表面的任何更新(冻结任何绘图)
- 代码略有不同,不完全确定为什么上面的代码不起作用,但下面的代码可以。
[因为 Whosebug 无法在要点后呈现代码而有意添加的行]
public int PutBitmapInGraphicsCard() {
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
final Bitmap bitmap = GenerateTestImage();
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0, bitmap, 0);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
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);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textureHandle[0];
}
基于此Manual:
Rendering in Unity can be multithreaded if the platform and number of available CPUs will allow for it. When multithreaded rendering is used, the rendering API commands happen on a thread which is completely separate from the one that runs MonoBehaviour scripts.
您可能开启了多线程渲染,所以可能是这样。
您可以尝试将其关闭,看看是否有所不同。
我正在尝试使用 UpdateExternalTexture
和 CreateExternalTexture
将来自 android 插件的简单位图图像传递到 unity 中。在文档中它说后者是为了与低级插件一起使用。我用 java 代码在上面一层。
我在进行任何 GLES20 调用时遇到上下文错误:
call to OpenGL ES API with no current context (logged once per thread)
根据此excellent question,这是因为上下文不正确(或有 none)。我按照该页面上的说明进行操作,并设法创建了一个上下文。但是,现在他们似乎指的是不同的资源 "areas"。我得到的指针是“1”,统一将其理解为 "current frame on the screen" 而不是我的位图。所以看起来像这样:
这是我的代码片段。首先是 Unity 部分。这是附加到场景中的游戏对象。
// This function is called shortly after Start
private void TestingTransfer() {
// get the current activity from unity
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
androidActivity = jc.GetStatic<AndroidJavaObject>("currentActivity");
// create a link to the android plugin
AndroidJavaObject obj = new AndroidJavaObject("com.test.pc.sample.SampleClass", androidActivity);
// Make the call to put the bitmap in the graphic memory and get it's PTR
Int32 texPtr = obj.Call<Int32>("PutBitmapInGraphicsCard");
Debug.Log("texture pointer == " + texPtr);
// Stage the texture in unity
Texture2D nativeTexture = Texture2D.CreateExternalTexture(100, 100, TextureFormat.ARGB32, false, false, (IntPtr)texPtr);
// Update it with the new PTR
nativeTexture.UpdateExternalTexture(nativeTexture.GetNativeTexturePtr());
// Notify other things in unity that this updated (delegate)
// I'm using this from another game object to pickup the new texture
if (OnNewImageReported != null) OnNewImageReported(nativeTexture);
}
现在 Java 一方。 Unity 通过 AAR 文件使用此代码。
private int PutBitmapInGraphicsCard() {
final int[] textureHandle = new int[1];
final Bitmap bitmap = GenerateTestImage(); // this creates a simple bitmap, blank with a blue circle in the center, works fine in android.
CreateContext(); // look below, it's defined there
GLES20.glGenTextures(1, textureHandle, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
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);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
return textureHandle[0];
}
// This function is a (almost) direct copy from the question linked above
public void CreateContext(){
// gets hold of the display
EGLDisplay dpy = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] vers = new int[2];
EGL14.eglInitialize(dpy, vers, 0, vers, 1);
// get some basic configs going
int[] configAttr = {
EGL14.EGL_COLOR_BUFFER_TYPE, EGL14.EGL_RGB_BUFFER,
EGL14.EGL_LEVEL, 0,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfig = new int[1];
EGL14.eglChooseConfig(dpy, configAttr, 0,
configs, 0, 1, numConfig, 0);
if (numConfig[0] == 0) {
Log.e("GLEDB-Error", "Could not find/create config!!");
}
EGLConfig config = configs[0];
// creating an offscreen (PBuffer) surface to render stuff
int[] surfAttr = {
EGL14.EGL_WIDTH, 100,
EGL14.EGL_HEIGHT, 100,
EGL14.EGL_NONE
};
EGLSurface surf = EGL14.eglCreatePbufferSurface(dpy, config, surfAttr, 0);
// create the context
int[] ctxAttrib = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
EGLContext ctx = EGL14.eglCreateContext(dpy, config, EGL14.EGL_NO_CONTEXT, ctxAttrib, 0);
// connect all these things together
EGL14.eglMakeCurrent(dpy, surf, surf, ctx);
}
所以,如果我对问题的理解是正确的,我需要的是引用 unity 用来绘制场景的相同上下文。有办法get/use吗?
解决方案:
如已接受的答案所述,主要问题是我使用的是多线程渲染。对于我的用例,这没有区别。
上面的代码并不能立即生效。需要注意的几点:
- 如果您这样做,请不要创建您自己的上下文。它搞砸了统一并阻止了表面的任何更新(冻结任何绘图)
- 代码略有不同,不完全确定为什么上面的代码不起作用,但下面的代码可以。
[因为 Whosebug 无法在要点后呈现代码而有意添加的行]
public int PutBitmapInGraphicsCard() {
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
final Bitmap bitmap = GenerateTestImage();
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0, bitmap, 0);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
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);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textureHandle[0];
}
基于此Manual:
Rendering in Unity can be multithreaded if the platform and number of available CPUs will allow for it. When multithreaded rendering is used, the rendering API commands happen on a thread which is completely separate from the one that runs MonoBehaviour scripts.
您可能开启了多线程渲染,所以可能是这样。 您可以尝试将其关闭,看看是否有所不同。