基于textureview编写opengl程序

泡在网上的日子 / 文 发表于2014-12-13 13:57 次阅读 textureview

ps这篇文章不仅可以更清楚的了解TextureView,还可以学到一点关于opengl的知识。

与SurfaceView相比,TextureView并没有创建一个单独的Surface用来绘制,这使得它可以像一般的View一样执行一些变换操作,设置透明度等。

另外,Textureview必须在硬件加速开启的窗口中。下面的例子演示了通过TextureView来创建一个opengl程序。


基于TextureView的程序,我们需要实现TextureView.SurfaceTextureListener这个接口,首先给出Activity的代码,在该类中,我们实现了此接口:

package com.fyj.test;
import java.util.concurrent.atomic.AtomicBoolean;
import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.os.Bundle;
import android.view.TextureView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
public class TextureviewtestActivity extends Activity implements TextureView.SurfaceTextureListener{
    private TextureView mTextureView;
    private Thread mProducerThread = null;
    private GLRendererImpl mRenderer;
                                                                                                              
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        mTextureView = new TextureView(this);
        mTextureView.setSurfaceTextureListener(this);
        mTextureView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);//隐藏虚拟按键,即navigator bar
        setContentView(mTextureView);
                                                                                                                  
        mRenderer = new GLRendererImpl(this);
    }
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
            int height) {
        // TODO Auto-generated method stub
        mRenderer.setViewport(width, height);
        mProducerThread = new GLProducerThread(surface, mRenderer, new AtomicBoolean(true));
        mProducerThread.start();
    }
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        // TODO Auto-generated method stub
        mProducerThread = null;
        return true;
    }
    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
            int height) {
        // TODO Auto-generated method stub
        mRenderer.resize(width, height);
                                                                                                                  
    }
    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        // TODO Auto-generated method stub
                                                                                                                  
    }
}

其中,在方法onSurfaceTextureAvailable中,我们开启一个OpenGL线程,在该线程中执行OpenGL的一些操作,该线程的定义如下:

package com.fyj.test;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;
import android.graphics.SurfaceTexture;
import android.opengl.GLUtils;
public class GLProducerThread extends Thread {
    private AtomicBoolean mShouldRender;
    private SurfaceTexture mSurfaceTexture;
    private GLRenderer mRenderer;
                                                                                                      
    private EGL10 mEgl;
    private EGLDisplay mEglDisplay = EGL10.EGL_NO_DISPLAY;
    private EGLContext mEglContext = EGL10.EGL_NO_CONTEXT;
    private EGLSurface mEglSurface = EGL10.EGL_NO_SURFACE;
    private GL mGL;
                                                                                                      
                                                                                                      
    private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
    private static final int EGL_OPENGL_ES2_BIT = 4;
                                                                                                      
    public interface GLRenderer {
        public void drawFrame();
    }
                                                                                                      
    public GLProducerThread(SurfaceTexture surfaceTexture, GLRenderer renderer, AtomicBoolean shouldRender)
    {
        mSurfaceTexture = surfaceTexture;
        mRenderer = renderer;
        mShouldRender = shouldRender;
    }
                                                                                                      
    private void initGL()
    {
        mEgl = (EGL10)EGLContext.getEGL();
                                                                                                          
        mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
            throw new RuntimeException("eglGetdisplay failed : " +
                                                                    GLUtils.getEGLErrorString(mEgl.eglGetError()));
        }
                                                                                                          
        int []version = new int[2];
        if (!mEgl.eglInitialize(mEglDisplay, version)) {
            throw new RuntimeException("eglInitialize failed : " +
                    GLUtils.getEGLErrorString(mEgl.eglGetError()));
        }
                                                                                                          
        int []configAttribs = {
                EGL10.EGL_BUFFER_SIZE, 32,
                EGL10.EGL_ALPHA_SIZE, 8,
                EGL10.EGL_BLUE_SIZE, 8,
                EGL10.EGL_GREEN_SIZE, 8,
                EGL10.EGL_RED_SIZE, 8,
                EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
                EGL10.EGL_NONE
        };
                                                                                                          
        int []numConfigs = new int[1];
        EGLConfig []configs = new EGLConfig[1];
        if (!mEgl.eglChooseConfig(mEglDisplay, configAttribs, configs, 1, numConfigs)) {
            throw new RuntimeException("eglChooseConfig failed : " +
                    GLUtils.getEGLErrorString(mEgl.eglGetError()));
        }
                                                                                                          
        int []contextAttribs = {
                EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL10.EGL_NONE
        };
        mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, contextAttribs);
        mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, configs[0], mSurfaceTexture, null);
        if (mEglSurface == EGL10.EGL_NO_SURFACE || mEglContext == EGL10.EGL_NO_CONTEXT) {
            int error = mEgl.eglGetError();
            if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
                throw new RuntimeException("eglCreateWindowSurface returned  EGL_BAD_NATIVE_WINDOW. " );
            }
            throw new RuntimeException("eglCreateWindowSurface failed : " +
                    GLUtils.getEGLErrorString(mEgl.eglGetError()));
        }
                                                                                                          
        if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
            throw new RuntimeException("eglMakeCurrent failed : " +
                    GLUtils.getEGLErrorString(mEgl.eglGetError()));
        }
                                                                                                          
        mGL = mEglContext.getGL();
    }
                                                                                                      
    private void destoryGL()
    {
        mEgl.eglDestroyContext(mEglDisplay, mEglContext);
        mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
        mEglContext = EGL10.EGL_NO_CONTEXT;
        mEglSurface = EGL10.EGL_NO_SURFACE;
    }
                                                                                                      
    public void run()
    {
        initGL();
                                                                                                          
        if (mRenderer != null) {
            ((GLRendererImpl)mRenderer).initGL();
        }
                                                                                                          
        while (mShouldRender != null && mShouldRender.get() != false) {
            if (mRenderer != null)
                mRenderer.drawFrame();
            mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
                                                                                                              
            try {
                sleep(5);
            } catch(InterruptedException e) {
                                                                                                                  
            }
        }
                                                                                                          
        destoryGL();
    }
}

在该线程中,我们定义了GLRenderer接口,该接口的实现者负责具体的OpenGL绘制代码:

package com.fyj.test;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;
import com.fyj.test.GLProducerThread.GLRenderer;
public class GLRendererImpl implements GLRenderer {
                                                                                              
    private int mProgramObject;
    private int mWidth;
    private int mHeight;
    private FloatBuffer mVertices;
    private ShortBuffer mTexCoords;
    private Context mContext;
    private int mTexID;
    private static String TAG = "GLRendererImpl";
    private final float[] mVerticesData = { -0.5f, -0.5f, 0, 0.5f, -0.5f, 0, -0.5f, 0.5f, 0, 0.5f, 0.5f, 0 };
    private final short[] mTexCoordsData = {0, 1, 1, 1, 0, 0, 1, 0};
    public GLRendererImpl(Context ctx)
    {
        mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        mVertices.put(mVerticesData).position(0);
                                                                                                  
        mTexCoords = ByteBuffer.allocateDirect(mTexCoordsData.length * 2)
                .order(ByteOrder.nativeOrder()).asShortBuffer();
        mTexCoords.put(mTexCoordsData).position(0);
                                                                                                  
        mContext = ctx;
                                                                                                  
    }
                                                                                              
    public void setViewport(int width, int height)
    {
        mWidth = width;
        mHeight = height;
    }
                                                                                              
    public void initGL()
    {
        comipleAndLinkProgram();
                                                                                                  
        loadTexture();
                                                                                                  
        GLES20.glClearColor(0,  0, 0, 0);
    }
                                                                                              
    public void resize(int width, int height)
    {
        mWidth = width;
        mHeight = height;
                                                                                                  
    }
                                                                                              
    @Override
    public void drawFrame() {
        // TODO Auto-generated method stub
        GLES20.glViewport(0, 0, mWidth, mHeight);
                                                                                                  
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glUseProgram(mProgramObject);
                                                                                                  
        GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, mVertices);
        GLES20.glEnableVertexAttribArray(0);
        GLES20.glVertexAttribPointer(1, 2, GLES20.GL_SHORT, false, 0, mTexCoords);
        GLES20.glEnableVertexAttribArray(1);
                                                                                                  
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexID);
        int loc = GLES20.glGetUniformLocation(mProgramObject, "u_Texture");
        GLES20.glUniform1f(loc, 0);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
                                                                                              
        Log.i("GLRendererImpl", "drawing..." + mWidth);
    }
                                                                                              
    private void loadTexture() {
        Bitmap b = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher);
        if (b != null) {
            int []texID = new int[1];
            GLES20.glGenTextures(1, texID, 0);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texID[0]);
            mTexID = texID[0];
                                                                                                      
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
                    GLES20.GL_LINEAR);
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
                    GLES20.GL_LINEAR);
                                                                                                      
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
                    GLES20.GL_REPEAT);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
                    GLES20.GL_REPEAT);
                                                                                                      
            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, b, 0);
            b.recycle();
        }
    }
                                                                                              
    private int loadShader(int shaderType, String shaderSource) {
        int shader;
        int[] compiled = new int[1];
        // Create the shader object
        shader = GLES20.glCreateShader(shaderType);
        if (shader == 0)
            return 0;
        // Load the shader source
        GLES20.glShaderSource(shader, shaderSource);
        // Compile the shader
        GLES20.glCompileShader(shader);
        // Check the compile status
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {
            Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
            GLES20.glDeleteShader(shader);
            return 0;
        }
        return shader;
    }
                                                                                              
    private void comipleAndLinkProgram() {
        String vShaderStr = "attribute vec4 a_position;    \n"
                +"attribute vec2 a_texCoords; \n"
                +"varying vec2 v_texCoords; \n"
                + "void main()                  \n"
                + "{                            \n"
                + "   gl_Position = a_position;  \n"
                +"    v_texCoords = a_texCoords; \n"
                + "}                            \n";
        String fShaderStr = "precision mediump float;                     \n"
                +"uniform sampler2D u_Texture; \n"
                +"varying vec2 v_texCoords; \n"
                + "void main()                                  \n"
                + "{                                            \n"
                + "  gl_FragColor = texture2D(u_Texture, v_texCoords) ;\n"
                + "}                                            \n";
        int vertexShader;
        int fragmentShader;
        int programObject;
        int[] linked = new int[1];
        // Load the vertex/fragment shaders
        vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vShaderStr);
        fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fShaderStr);
        // Create the program object
        programObject = GLES20.glCreateProgram();
        if (programObject == 0)
            return ;
        GLES20.glAttachShader(programObject, vertexShader);
        GLES20.glAttachShader(programObject, fragmentShader);
        // Bind vPosition to attribute 0
        GLES20.glBindAttribLocation(programObject, 0, "a_position");
        GLES20.glBindAttribLocation(programObject, 1, "a_texCoords");
        // Link the program
        GLES20.glLinkProgram(programObject);
        // Check the link status
        GLES20.glGetProgramiv(programObject, GLES20.GL_LINK_STATUS, linked, 0);
        if (linked[0] == 0) {
            Log.e(TAG, "Error linking program:");
            Log.e(TAG, GLES20.glGetProgramInfoLog(programObject));
            GLES20.glDeleteProgram(programObject);
            return  ;
        }
        mProgramObject = programObject;
    }
}

需要注意的是,所有EGL及GL相关的操作必须置于GLThread线程中进行。

收藏 赞 (6) 踩 (2)
上一篇:Android TextureView简易教程
如果你想显示一段在线视频或者任意的数据流比如视频或者OpenGL 场景,你可以用android中的TextureView做到。 应用程序的视频或者opengl内容往往是显示在一个特别的UI控件中:SurfaceView。SurfaceView的工作方式是创建一个置于应用窗口之后的新窗口。这种方
下一篇:开源视频录制库LandscapeVideoCamera
非常强大的android 视频录制库,可以选择视频尺寸以及视频质量,只允许横屏录制。 使用Android自带的Camera应用可以录制视频,只需发送MediaStore.ACTION_VIDEO_CAPTURE的intent即可,但是有一些缺陷: 内置应用intent的视频质量参数只允许为0和1 分别代表最