【问题标题】:Why OpenGL ES Cover All Android View ? just I want to Off Screen Rendering ~为什么 OpenGL ES 覆盖所有 Android 视图?只是我想离屏渲染~
【发布时间】:2015-03-19 01:55:31
【问题描述】:

我尝试在 Android NDK 上使用 OpenGL ES 2.0 的屏幕外渲染来获取图像。

安卓版本:4.4.2
设备:三星 Galaxy Tab SM-T705

  1. 我在 Android 中做了一个简单的按钮示例
  2. 如果按下按钮,程序会在 NDK 中创建 CreatePbufferWindow
  3. 程序绘制一个三角形。
  4. glReadPixels() 获取结果图像。
  5. 并保存位图类
  6. 程序在 Android 窗口中绘制结果图像。

我不想覆盖栏和按钮 而且我不希望显示 OpenGL ES 视图。

NDK C 代码

#include <jni.h>
#include <android/log.h>

#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <android/bitmap.h>

#include "tga.h"
#include "jpge.h"

#define  LOG_TAG    "libgl2jni"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

//static void printGLString(const char *name, GLenum s) {
//    const char *v = (const char *) glGetString(s);
//    LOGI("GL %s = %s\n", name, v);
//}

static void checkGlError(const char* op) {
    for (GLint error = glGetError(); error; error
            = glGetError()) {
        LOGI("after %s() glError (0x%x)\n", op, error);
    }
}

static const char gVertexShader[] =
    "attribute vec4 vPosition;\n"
    "void main() {\n"
    "  gl_Position = vPosition;\n"
    "}\n";

static const char gFragmentShader[] =
    "precision mediump float;\n"
    "void main() {\n"
    "  gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
    "}\n";

/**
 * Initialize an EGL context for the current display.
 */
static int engine_init_display(int width, int height) {
    // initialize OpenGL ES and EGL

    /*
     * Here specify the attributes of the desired configuration.
     * Below, we select an EGLConfig with at least 8 bits per color
     * component compatible with on-screen windows
     */
    const EGLint attribs[] = {
            //EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
            EGL_BLUE_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_RED_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_NONE
    };
    EGLint w, h, dummy, format;
    EGLint numConfigs;
    EGLConfig config;
    EGLSurface surface;
    EGLContext context;

    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    checkGlError("eglGetDisplay");

    eglInitialize(display, 0, 0);
    checkGlError("eglInitialize");

    /* Here, the application chooses the configuration it desires. In this
     * sample, we have a very simplified selection process, where we pick
     * the first EGLConfig that matches our criteria */
    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
    checkGlError("eglChooseConfig");

    /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
     * guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
     * As soon as we picked a EGLConfig, we can safely reconfigure the
     * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */
    eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
    checkGlError("eglGetConfigAttrib");


    // using PexelBuffer
    EGLint attribList[] =
    {
        EGL_WIDTH, width,
        EGL_HEIGHT, height,
        EGL_LARGEST_PBUFFER, EGL_TRUE,
        EGL_NONE
    };

    surface = eglCreatePbufferSurface(display, config, attribList);
    checkGlError("eglCreatePbufferSurface");

//    surface = eglCreateWindowSurface(display, config, engine->app->window, NULL);

    const EGLint attrib_list[] = {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
    };
    context = eglCreateContext(display, config, NULL, attrib_list);
    checkGlError("eglCreateContext");

    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
//        LOGW("Unable to eglMakeCurrent");
        return -1;
    }

//    eglQuerySurface(display, surface, EGL_WIDTH, &w);
//    eglQuerySurface(display, surface, EGL_HEIGHT, &h);

    // Initialize GL state.
    glEnable(GL_CULL_FACE);
//    glDisable(GL_DEPTH_TEST);
    glEnable(GL_DEPTH_TEST);

    return 0;
}

GLuint loadShader(GLenum shaderType, const char* pSource) {
    GLuint shader = glCreateShader(shaderType);
    if (shader) {
        glShaderSource(shader, 1, &pSource, NULL);
        glCompileShader(shader);
        GLint compiled = 0;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
        if (!compiled) {
            GLint infoLen = 0;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
            if (infoLen) {
                char* buf = (char*) malloc(infoLen);
                if (buf) {
                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
                    LOGE("Could not compile shader %d:\n%s\n",
                            shaderType, buf);
                    free(buf);
                }
                glDeleteShader(shader);
                shader = 0;
            }
        }
    }
    return shader;
}

GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
    if (!vertexShader) {
        return 0;
    }

    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
    if (!pixelShader) {
        return 0;
    }

    GLuint program = glCreateProgram();
    if (program) {
        glAttachShader(program, vertexShader);
        checkGlError("glAttachShader");
        glAttachShader(program, pixelShader);
        checkGlError("glAttachShader");
        glLinkProgram(program);
        GLint linkStatus = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        if (linkStatus != GL_TRUE) {
            GLint bufLength = 0;
            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
            if (bufLength) {
                char* buf = (char*) malloc(bufLength);
                if (buf) {
                    glGetProgramInfoLog(program, bufLength, NULL, buf);
                    LOGE("Could not link program:\n%s\n", buf);
                    free(buf);
                }
            }
            glDeleteProgram(program);
            program = 0;
        }
    }
    return program;
}

GLuint gProgram;
GLuint gvPositionHandle;

bool setupGraphics(int w, int h) {
//    printGLString("Version", GL_VERSION);
//    printGLString("Vendor", GL_VENDOR);
//    printGLString("Renderer", GL_RENDERER);
//    printGLString("Extensions", GL_EXTENSIONS);

    LOGI("setupGraphics(%d, %d)", w, h);
    gProgram = createProgram(gVertexShader, gFragmentShader);
    if (!gProgram) {
        LOGE("Could not create program.");
        return false;
    }
    gvPositionHandle = glGetAttribLocation(gProgram, "vPosition");
    checkGlError("glGetAttribLocation");
    LOGI("glGetAttribLocation(\"vPosition\") = %d\n",
            gvPositionHandle);

    glViewport(0, 0, w, h);
    checkGlError("glViewport");
    return true;
}

GLfloat gTriangleVertices[] = { 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f,
        -1.5f, 1.5f, 1.5f };

char* renderFrame(int width, int height) {
    static float grey;
    grey += 0.01f;
    if (grey > 1.0f) {
        grey = 0.0f;
    }

    // 크기 조정
    for(int count = 0; count < 9; ++count)
    {
        gTriangleVertices[count] *= 0.8f;
    }

    char* pixelData = (char*)malloc(4 * width * height * sizeof(char));

    for(int count = 0; count < width * height * 4; ++count) {
        pixelData[count] = 0;
    }

    // 깊이 버퍼 활성화
    glEnable(GL_DEPTH_TEST);

    // 깊이 버퍼 초기화
    glClearDepthf(1.F);

//    glClearColor(grey, grey, grey, 1.0f);
    glClearColor(1.0, 0.0, 0.0, 1.0f);
    checkGlError("glClearColor");
    glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    checkGlError("glClear");

    glUseProgram(gProgram);
    checkGlError("glUseProgram");

    glVertexAttribPointer(gvPositionHandle, 3, GL_FLOAT, GL_FALSE, 0, gTriangleVertices);
    checkGlError("glVertexAttribPointer");
    glEnableVertexAttribArray(gvPositionHandle);
    checkGlError("glEnableVertexAttribArray");
    glDrawArrays(GL_TRIANGLES, 0, 3);
    checkGlError("glDrawArrays");

    glReadPixels(
            0, 0,
            width, height,
            GL_RGBA,
            GL_UNSIGNED_BYTE,
            pixelData
    );

//    int jpgCount = 0;
//    for(int count = 0; count < width * height * 4; ++count) {
//      if (count % 4 != 3) {
//          pixelJpgData[jpgCount] = pixelData[count];
//          ++jpgCount;
//      }
//    }

//    tgaGrabScreenSeries("/storage/emulated/0/Pictures/CTgaTest", 0, 0, width, height);
    jpge::compress_image_to_jpeg_file(
            "/storage/emulated/0/Pictures/CJpgTest.jpg",
            width, height,
            4,
            (jpge::uint8*)pixelData
    );

//  LOGI("%s", (char const *)pixelData);
//    for(int count = 0; count < width*height*4; ++count) {
//      LOGI("%x", pixelData[count]);
//    }


    return pixelData;

//    if(pixelData) {
//    free(pixelData);
////        delete[] pixelData;
//    }
}

int
decodeMemory(JNIEnv* env, const void* data, size_t len, jobject* bitmap)
{
        jclass clazz = env->FindClass("android/graphics/BitmapFactory");
        if (env->ExceptionCheck()) {
                env->ExceptionClear();
                return 2;
        }

        jmethodID mid = env->GetStaticMethodID(clazz, "decodeArray",
                        "([BII)Landroid/graphics/Bitmap;");
        if (env->ExceptionCheck()) {
                env->ExceptionClear();
                return 2;
        }

        jbyteArray jarray = env->NewByteArray(len);
        env->SetByteArrayRegion(jarray, 0, len, (jbyte*)data);
        *bitmap = env->CallStaticObjectMethod(clazz, mid, jarray, 0, len);

        return 1;
}

extern "C"
{
JNIEXPORT void JNICALL Java_com_javacodegeeks_android_buttonexample_GL2JNILib_init
(JNIEnv * env, jobject obj, jint width, jint height)
{
     engine_init_display(width, height);
    setupGraphics(width, height);
}
}

extern "C"
{
JNIEXPORT void JNICALL Java_com_javacodegeeks_android_buttonexample_GL2JNILib_step
(JNIEnv * env, jobject obj, jobject jBitmap, jint width, jint height)
//(JNIEnv * env, jobject obj, jint width, jint height)
{
    char* pixelData = NULL;
    pixelData = renderFrame(width, height);
//  renderFrame(width, height);

    decodeMemory(env, pixelData, width * height * 4, &jBitmap);

    if (pixelData)
    {
        free(pixelData);
    }
}

}

java代码

package com.javacodegeeks.android.buttonexample;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Environment;
import android.util.Log;
import android.view.View;

class GL2View extends View
{
    public GL2View(Context context, int w, int h) {
        super(context);
        // TODO Auto-generated constructor stub

        width = w;
        height = h;

//      //OpenGL 테스트 구현부 시작 /////////////////////////////////////////////////////
        mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        GL2JNILib.init(width, height);

        mCanvas = new Canvas();
        mCanvas.setBitmap(mBitmap);
//        //OpenGL 테스트 구현부 끝 /////////////////////////////////////////////////////

        mPaint = new Paint();

    }

    /* Image SDCard Save (input Bitmap -&gt; saved file JPEG)
      * Writer intruder(Kwangseob Kim)
      * @param bitmap : input bitmap file
      * @param folder : input folder name
      * @param name   : output file name
      */
       public static void saveBitmaptoJpeg(Bitmap bitmap,String folder, String name){
//        String ex_storage =Environment.getExternalStorageDirectory().getAbsolutePath(); // 절대 경로 
           String ex_storage = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath(); // 이미지 경로 
                                           // Get Absolute Path in External Sdcard
          String foler_name = "/"+folder+"/";
          String file_name = name+".jpg";
          String string_path = ex_storage+foler_name;

           Log.d(VIEW_LOG_TAG, ex_storage);

          File file_path;
          try{
           file_path = new File(string_path);
           if(!file_path.isDirectory()){
            file_path.mkdirs();
           }
           FileOutputStream out = new FileOutputStream(string_path+file_name);

           bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
           out.close();

          }catch(FileNotFoundException exception){
           Log.e("FileNotFoundException", exception.getMessage());
          }catch(IOException exception){
           Log.e("IOException", exception.getMessage());
          }
         }

    public void SaveBitmapToSDcard()
    {
//      File path =  Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

        saveBitmaptoJpeg(mBitmap, "./", "JavaTest");
    }

//  @Override
//  protected void onSizeChanged(int w, int h, int oldw, int oldh)
//  {
//      mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
//      mCanvas = new Canvas();
//      mCanvas.setBitmap(mBitmap);
//      
//      testDrawing();
//  }
//  
//  private void testDrawing()
//  {
//      mCanvas.drawColor(Color.WHITE);
//      mPaint.setColor(Color.RED);
//      mCanvas.drawRect(100, 100, 200, 200, mPaint);
//  }
//  
    @Override
    protected void onDraw(Canvas canvas)
    {
        GL2JNILib.step(mBitmap, width, height);
//      GL2JNILib.step(width, height);

        if(mBitmap != null)
        {
            canvas.drawBitmap(mBitmap, 0, 0, null);
        }

        SaveBitmapToSDcard();
    }

    private Bitmap mBitmap;
    private Canvas mCanvas;
    private Paint mPaint;

    int width;
    int height;
}

本地调用

//package com.android.gl2jni;
package com.javacodegeeks.android.buttonexample;

import android.graphics.Bitmap;

// Wrapper for native library

public class GL2JNILib {

     static {
         System.loadLibrary("gl2jni");
     }

    /**
     * @param width the current view width
     * @param height the current view height
     */
     public static native void init(int width, int height);
     public static native void step(Bitmap bitmap, int width, int height);
//     public static native void step(int width, int height);
}

Buttom Image Result Image

【问题讨论】:

  • 如果不想显示视图,为什么还要创建视图?我看不出您调用本机代码的代码需要在视图中的原因。如果需要,您可以完全在本机代码中创建上下文和 PBuffer 表面。
  • 嗯...我不想显示 OpenGL 视图。我只想获取位图数据。如果我不调用 "protected void onDraw(Canvas canvas)" ,OpenGL 视图会覆盖我的渲染视图(导航栏和按钮)。我认为是 eglCreatePBufferSurface() 问题或深度缓冲区问题,
  • 不确定我的观点是否清楚。我什至不会为此创建视图。
  • 不,CreatePbufferSurface() 不是生成 fbos 的函数。转到 khronos.org/opengles/sdk/docs/man 并查看 glgenframebuffers、glbindframebuffers。您还需要将 rbos 附加到您制作的 fbo。

标签: image android-ndk opengl-es-2.0


【解决方案1】:

如果您询问如何仅从绘制对象的 Offscreen-FBO 导出像素数据,则应在调用 glreadpixels() 之前绑定 offscreen-FBO

问题 1. 你永远不会绑定和解除绑定 FBO。因此,您可以在屏幕缓冲区上绘制对象。

问题2.你永远不会在glreadpixels()之前生成offscreen-FBO并绑定它;

解决方案。生成offscreen-FBO,在绘制对象之前绑定,调用glreadpixels()之前绑定;

  • 更新

这是生成FBO和RBO的代码以及如何将rbo附加到fbo

 glGenFramebuffers(1, &fbo[object_id]);
 glBindFramebuffer(GL_FRAMEBUFFER, fbo[object_id]);
 glGenRenderbuffers(1, &rboColor[object_id]);
 glBindRenderbuffer(GL_RENDERBUFFER, rboColor[object_id]);
 glRenderbufferStorage(GL_RENDERBUFFER,GL_RGBA8_OES, width, height);
 glBindFramebuffer(GL_FRAMEBUFFER, fbo[object_id]);

// Attach ColorRenderbuffers   
 glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_RENDERBUFFER,rboColor[object_id]);

【讨论】:

  • 感谢您的回答~但我尝试使用FBO。我有同样的结果。我试过这个过程: CreatePbufferSurface() -> bind FBO -> glReadPixels()
猜你喜欢
  • 1970-01-01
  • 2018-06-11
  • 2011-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-03
  • 2011-12-01
相关资源
最近更新 更多