使用 GLES20 通过 Android 插件获取位图到 Unity

Get bitmap to Unity through Android plugin using GLES20

我正在尝试使用 UpdateExternalTextureCreateExternalTexture 将来自 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.

您可能开启了多线程渲染,所以可能是这样。 您可以尝试将其关闭,看看是否有所不同。