[英]Unity3d Render openGL FBO to texture in android (java)
我试图在Android(java)中为Unity3D创建一个插件,以使用OpenGL纹理进行渲染,获取本机指针并在Unity中映射一个Quad。 到目前为止,我的Unity代码很简单:
// Use this for initialization
void Start () {
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
context = jc.GetStatic<AndroidJavaObject>("currentActivity");
surface2Unity = new AndroidJavaObject("com.everywoah.surface2unity.Surface2Unity");
int i = surface2Unity.Call<int> ("getTextureID");
t =Texture2D.CreateExternalTexture (1280, 720, TextureFormat.ARGB32, false,false, new IntPtr(i));
t.filterMode = FilterMode.Bilinear;
t.wrapMode = TextureWrapMode.Repeat;
debug.text = "" + i;
GetComponent<MeshRenderer> ().material.mainTexture = t;
}
// Update is called once per frame
void Update () {
//transform.Rotate(1f,1f,1f);
surface2Unity.Call ("draw");
}
在Java中,我有:
public MyRenderer(){
initGL();
initFBO();
}
private void initGL(){
GLstatics.checkGlError("initGL_S");
String vertexShader = "attribute vec4 aPosition;\n" +
"attribute vec4 aTex;\n" +
"varying vec2 tex;\n" +
"void main() {\n" +
" gl_Position = aPosition;\n" +
"tex=aTex.xy;\n" +
"}";
String fragmentShader = "precision mediump float;\n" +
"\n" +
"void main(){\n" +
" gl_FragColor = vec4(1.0,1.0,0.0,1.0);\n" +
"\n" +
"}";
mProgram = GLstatics.createProgram(vertexShader, fragmentShader);
maPositionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition");
maTexHandle = GLES30.glGetAttribLocation(mProgram, "aTex");
GLstatics.checkGlError("initGL_E");
}
private void initFBO(){
GLstatics.checkGlError("initFBO_S");
GLES30.glGenFramebuffers(1, mFboId, 0);
GLES30.glGenRenderbuffers(1, mRboId, 0);
GLES30.glGenTextures(1, mTexId, 0);
GLES30.glGenBuffers(1,buffer, 0);
GLES30.glBindRenderbuffer(GLES30.GL_RENDERBUFFER, mRboId[0]);
GLES30.glRenderbufferStorage(GLES30.GL_RENDERBUFFER, GLES30.GL_DEPTH_COMPONENT16, 1280,
720);
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFboId[0]);
GLES30.glFramebufferRenderbuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_ATTACHMENT,
GLES30.GL_RENDERBUFFER, mRboId[0]);
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTexId[0]);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, 1280, 720, 0,
GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0,
GLES30.GL_TEXTURE_2D, mTexId[0], 0);
GLstatics.checkGlError("initFBO_E ");
}
public void draw() {
GLstatics.checkGlError("draw_S");
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFboId[0]);
GLES30.glClearColor(0.0f,0.0f,1.0f,1.0f);
GLES30.glViewport(0, 0, 1280, 720);
GLES30.glClear(GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_COLOR_BUFFER_BIT);
GLES30.glUseProgram(mProgram);
mVtxBuf.position(0);
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, buffer[0]);
GLES30.glVertexAttribPointer(maPositionHandle,
3, GLES30.GL_FLOAT, false, 4*(3+2), mVtxBuf);
GLES30.glEnableVertexAttribArray(maPositionHandle);
mVtxBuf.position(3);
GLES30.glVertexAttribPointer(maTexHandle, 2, GLES30.GL_FLOAT, false, 4 * (3 + 2), mVtxBuf);
GLES30.glEnableVertexAttribArray(maTexHandle);
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
GLstatics.checkGlError("draw_E ");
}
它应该是一个非常简单的代码,只需在蓝色背景上绘制一个黄色矩形,我就可以在Activity中使用它。 这里的问题是我的应用程序看起来像这样:
从我认为我已经弄乱了已经绑定的openGL缓冲区的地方,导致绘制了矩形的顶点,但是里面有一个奇怪的东西。 我试过保存它们并在draw函数之后将它们绑定,但结果却相同。 有什么想法我做错了吗? 如果可能的话,我真的很想用Java解决这个问题,我使用NDK的经验最多为0。
好了,所以问题是我必须自己控制EGLContex,我无法在与Unity相同的上下文中进行渲染,因此我需要此类:
public class SurfaceManager {
public final static String TAG = "TextureManager";
private static final int EGL_RECORDABLE_ANDROID = 0x3142;
// Contexto donde dibujar
private EGLContext mEGLContext = null;
// Contexto compartido entre hilos. Para poder pasar el FBO de un hilo a otro
private EGLContext mEGLSharedContext = null;
private EGLSurface mEGLSurface = null;
private EGLDisplay mEGLDisplay = null;
// La surface donde se va a dibujar
private Surface mSurface;
/**
* Creates an EGL context and an EGL surface.
*/
public SurfaceManager(Surface surface) {
EGLContext shared = EGL14.eglGetCurrentContext();
if (surface == null) {
throw new NullPointerException();
}
mSurface = surface;
mEGLSharedContext = shared;
Log.d("Surface2UnityDebug", "vamos al setup");
eglSetup();
}
// Hace que la surface actual sea esta
public void makeCurrent() {
if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext))
throw new RuntimeException("eglMakeCurrent failed");
}
// Cambia el buffer donde se está pintando por el de la surface. es decir, guarda lo que se haya pintado.
public void swapBuffers() {
EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface);
}
/**
* Sends the presentation time stamp to EGL. Time is expressed in nanoseconds.
*/
public void setPresentationTime(long nsecs) {
EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, nsecs);
checkEglError("eglPresentationTimeANDROID");
}
/**
* Prepares EGL. We want a GLES 2.0 context and a surface that supports recording.
*/
private void eglSetup() {
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
Log.d("Surface2UnityDebug", "unable to get EGL14 display");
throw new RuntimeException("unable to get EGL14 display");
}
int[] version = new int[2];
if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
Log.d("Surface2UnityDebug", "unable to initialize EGL14");
throw new RuntimeException("unable to initialize EGL14");
}
// Configure EGL for recording and OpenGL ES 2.0.
int[] attribList;
attribList = new int[]{
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL_RECORDABLE_ANDROID, 1,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
numConfigs, 0);
checkEglError("eglCreateContext RGB888+recordable ES2");
// Configure context for OpenGL ES 2.0.
int[] attrib_list = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
EGL14.EGL_NONE
};
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], mEGLSharedContext, attrib_list, 0);
checkEglError("eglCreateContext");
// Create a window surface, and attach it to the Surface we received.
int[] surfaceAttribs = {
EGL14.EGL_NONE
};
mEGLSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW);
checkEglError("eglCreateWindowSurface");
}
/**
* Discards all resources held by this class, notably the EGL context. Also releases the
* Surface that was passed to our constructor.
*/
public void release() {
if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
EGL14.EGL_NO_CONTEXT);
EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
EGL14.eglReleaseThread();
EGL14.eglTerminate(mEGLDisplay);
}
mEGLDisplay = EGL14.EGL_NO_DISPLAY;
mEGLContext = EGL14.EGL_NO_CONTEXT;
mEGLSurface = EGL14.EGL_NO_SURFACE;
mSurface.release();
}
/**
* Checks for EGL errors. Throws an exception if one is found.
*/
private void checkEglError(String msg) {
int error;
if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
Log.d("Surface2UnityDebug", msg + ": EGL error: 0x" + Integer.toHexString(error));
throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error));
}
}}
和方法:
// Guarda el estado actual
public void saveRenderState() {
mSavedEglDisplay = EGL14.eglGetCurrentDisplay();
mSavedEglDrawSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW);
mSavedEglReadSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_READ);
mSavedEglContext = EGL14.eglGetCurrentContext();
}
// Carga el estado guardado
public void restoreRenderState() {
if (!EGL14.eglMakeCurrent(
mSavedEglDisplay,
mSavedEglDrawSurface,
mSavedEglReadSurface,
mSavedEglContext)) {
throw new RuntimeException("eglMakeCurrent failed");
}
}
这样,我可以调用saveRenderState(),makeCurrent(),draw()和restoreRenderState(),现在一切正常。
Java方面:
public int loadImageReturnTexturePtr(String imagePath, int width, int heigh) {
Log.d("unity", "loading image1: " + imagePath);
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
Log.d("unity", "Bitmap is: " + bitmap);
int textures[] = new int[1];
GLES20.glGenTextures(1, textures, 0);
int textureId = textures[0];
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, heigh, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
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);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap,0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
Log.d("unity", "texture id returned: " + textureId);
return textureId;
}
统一C#面:
// Use this for initialization
void Start ()
{
AndroidJavaObject mImageLoader = new AndroidJavaObject ("com.dvision.load.LoadTexture");
Texture2D texture2D = new Texture2D (2048, 2048, TextureFormat.ARGB32, false);
int texPtr = mImageLoader.Call <int> ("loadImageReturnTexturePtr", "/sdcard/Lfront.jpg", 2048, 2048);
Debug.Log ("texture pointer? " + texPtr);
Texture2D nativeTexture = Texture2D.CreateExternalTexture (2048, 2048, TextureFormat.ARGB32, false, true, new System.IntPtr (texPtr));
texture2D.UpdateExternalTexture (nativeTexture.GetNativeTexturePtr ());
gameObject.GetComponent<Renderer> ().material.mainTexture = texture2D;
}
您可以使用以下函数: Texture2D.CreateExternalTexture
做到这一点非常简单。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.