TextureView展示OpenGL内容(Demo)

47 篇文章 2 订阅
1 篇文章 0 订阅


 

        上图的实现,如果是用GLSurfaceView实现,那会相对比较简单,直接在设置的Renderer实现类中实现gl展示即可,但是使用TextureView就没那么方便了,一般TextureView与OpenGL和结合就是相机预览和视频播放了,那如果是一般的gles展示呢? 这就需要重新创建一个GL线程了(相比之下,这种方式是真真不好的,通过实践发现:这种实现方式CPU占用率比GLSurfaceView的CPU占用率还高。再者GLSurfaceView本就对渲染同步、状态控制等做了优化封装),但既然已经做了,就写下来mark一下!

        那这个demo主要有3个类,主要看代码,代码中有注释。

1.GLESTextureView,这个类继承自TextureView,主要是添加了setRenderer和setRenderMode方法,使其用起来像GLSurfaceView一样。

2.GLESTVThread,这是一个线程类,在GLESTextureView的SurfaceTexture准备好之后开启该线程作为一个GL线程(GL的一些操作都要在该线程中去实现)。

3.IGLESRenderer,该类是GL线程流程的接口,该接口的实现可用于GLESTextureView的setRenderer,也可以用于GLSurfaceView中的renderer的方法体实现。

 

package com.dandy.helper.gles;

/**
 * <pre>
 * GLES里用到的用于渲染的一个接口。
 * 如果是GLSurfaceView要用到,则其对应的GLSurfaceView.Renderer可以来调用IGLESRenderer的实现类来实现逻辑
 * 如果是TextureView要用到,则使用自定义的一个线程里调用IGLESRenderer的实现类来做一个类似于GLSurfaceView.Renderer的操作
 * 所以IGLESRenderer中的方法都要在GL线程里运行(TextureView创建一个线程,把它当做一个GL线程)
 * </pre>
 * 
 * @author flycatdeng
 * 
 */
public interface IGLESRenderer {
    /**
     * <pre>
     * Surface创建好之后
     * </pre>
     */
    public void onSurfaceCreated();

    /**
     * <pre>
     * 界面大小有更改
     * </pre>
     * 
     * @param width
     * @param height
     */
    public void onSurfaceChanged(int width, int height);

    /**
     * <pre>
     * 绘制每一帧
     * </pre>
     */
    public void onDrawFrame();

    /**
     * <pre>
     * Activity的onResume时的操作
     * </pre>
     */
    public void onResume();

    /**
     * <pre>
     * Activity的onPause时的操作
     * </pre>
     */
    public void onPause();

    /**
     * <pre>
     * Activity的onDestroy时的操作
     * </pre>
     */
    public void onDestroy();

}

 

package com.dandy.module.gles.textureview;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.util.AttributeSet;
import android.view.TextureView;

import com.dandy.helper.gles.IGLESRenderer;

/**
 * <pre>
 * 一个类似于GLSurfaceView的TextureView,用于显示opengl
 *  {@link #setRenderer(IGLESRenderer)}类似于GLSurfaceView的setRenderer(Renderer)
 *  {{@link #setRenderMode(int)}类似于GLSurfaceView的setRenderMode(int)
 *  详细调用可模仿com.dandy.gldemo.glestextureview.DemoGlesTextureView
 *  没事可看<a href='http://blog.csdn.net/fuyajun01/article/details/8931647#' >参照1</>或者<a href="http://www.jianshu.com/p/b2d949ab1a1a">参照2</a>
 * </pre>
 * 
 * @author flycatdeng
 * 
 */
public class GLESTextureView extends TextureView implements TextureView.SurfaceTextureListener {
    public final static int RENDERMODE_WHEN_DIRTY = 0;
    public final static int RENDERMODE_CONTINUOUSLY = 1;
    private GLESTVThread mGLThread;
    private IGLESRenderer mRenderer;
    private int mRendererMode = RENDERMODE_CONTINUOUSLY;

    public GLESTextureView(Context context) {
        super(context);
        init(context);
    }

    public GLESTextureView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    /**
     * <pre>
     * 类似于GLSurfaceView的setRenderer
     * </pre>
     */
    public void setRenderer(IGLESRenderer renderer) {
        mRenderer = renderer;
    }

    /**
     * <pre>
     * 类似于GLSurfaceView的setRenderMode
     * 渲染模式,是循环刷新,还是请求的时候刷新
     * </pre>
     */
    public void setRenderMode(int mode) {
        mRendererMode = mode;
    }

    /**
     * Request that the renderer render a frame. This method is typically used when the render mode has been set to {@link #RENDERMODE_WHEN_DIRTY}, so
     * that frames are only rendered on demand. May be called from any thread. Must not be called before a renderer has been set.
     */
    public void requestRender() {
        if (mRendererMode != RENDERMODE_WHEN_DIRTY) {
            return;
        }
        mGLThread.requestRender();
    }

    private void init(Context context) {
        setSurfaceTextureListener(this);
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        mGLThread = new GLESTVThread(surface, mRenderer);// 创建一个线程,作为GL线程
        mGLThread.setRenderMode(mRendererMode);
        mGLThread.start();
        mGLThread.onSurfaceChanged(width, height);
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false;
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        mGLThread.onSurfaceChanged(width, height);
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }

    public void onResume() {
        if (mGLThread != null) {
            mGLThread.onResume();
        }
    }

    public void onPause() {
        if (mGLThread != null) {
            mGLThread.onPause();
        }
    }

    public void onDestroy() {
        if (mGLThread != null) {
            mGLThread.onDestroy();
        }
    }
}

 

package com.dandy.module.gles.textureview;

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 android.graphics.SurfaceTexture;
import android.opengl.GLUtils;

import com.dandy.helper.android.LogHelper;
import com.dandy.helper.gles.IGLESRenderer;
import com.dandy.helper.java.PendingThreadAider;

/**
 * <pre>
 * TextureView中要用的GLThread
 * <a href="http://www.cnblogs.com/kiffa/archive/2013/02/21/2921123.html">一个很好的EGL相关学习参考</a>
 * 一般在Android中使用OpenGL ES,总是会从GLSurfaceView和Renderer开始,只需要提供一个合适的SurfaceHolder,就可以完成整个环境初始化,并进行绘制。
 * GLSurfaceView和Renderer事实上只是在本文描述的基础上封装了一些便利的功能,便于开发者开发,比如渲染同步、状态控制、主(渲染)循环等
 * </pre>
 * 
 * @author flycatdeng
 */
class GLESTVThread extends Thread {
    private static final String TAG = "GLESTVThread";
    private SurfaceTexture mSurfaceTexture;
    private EGL10 mEgl;
    private EGLDisplay mEglDisplay = EGL10.EGL_NO_DISPLAY;// 显示设备
    private EGLSurface mEglSurface = EGL10.EGL_NO_SURFACE;
    private EGLContext mEglContext = EGL10.EGL_NO_CONTEXT;
//    private GL mGL;
    private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
    private static final int EGL_OPENGL_ES2_BIT = 4;
    private IGLESRenderer mRenderer;
    private PendingThreadAider mPendingThreadAider = new PendingThreadAider();
    private boolean mNeedRenderring = true;
    private Object LOCK = new Object();
    private boolean mIsPaused = false;

    public GLESTVThread(SurfaceTexture surface, IGLESRenderer renderer) {
        mSurfaceTexture = surface;
        mRenderer = renderer;
    }

    @Override
    public void run() {
        LogHelper.d(TAG, LogHelper.getThreadName());
        initGLESContext();
        mRenderer.onSurfaceCreated();
        while (mNeedRenderring) {
            mPendingThreadAider.runPendings();// 执行未执行的,或要执行的事件。(后期可以开放以便模仿GLSurfaceView的queueEvent(Runnable r))
            mRenderer.onDrawFrame();// 绘制
            // 一帧完成之后,调用eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)来显示
            mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);// 这一句不能少啊,少了就GG了,一片空白
            // 1.凡是onPause都要停止,2.如果是onResume的状态,如果是循环刷新则会继续下一次循环,否则会暂停等待调用requestRender()
            if (mIsPaused) {
                pauseWhile();
            } else if (mRendererMode == GLESTextureView.RENDERMODE_WHEN_DIRTY) {
                pauseWhile();
            }
        }
        destoryGLESContext();
    }

    private void initGLESContext() {
        LogHelper.d(TAG, LogHelper.getThreadName());
        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)) {// //version中存放EGL 版本号,int[0]为主版本号,int[1]为子版本号
            throw new RuntimeException("eglInitialize failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError()));
        }

        // 构造需要的特性列表
        int[] configAttribs = { //
        EGL10.EGL_BUFFER_SIZE, 32,//
                EGL10.EGL_ALPHA_SIZE, 8, // 指定Alpha大小,以下四项实际上指定了像素格式
                EGL10.EGL_BLUE_SIZE, 8, // 指定B大小
                EGL10.EGL_GREEN_SIZE, 8,// 指定G大小
                EGL10.EGL_RED_SIZE, 8,// 指定RGB中的R大小(bits)
                EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,// 指定渲染api类别,这里或者是硬编码的4,或者是EGL14.EGL_OPENGL_ES2_BIT
                EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, EGL10.EGL_NONE// 总是以EGL10.EGL_NONE结尾
        };

        int[] numConfigs = new int[1];
        EGLConfig[] configs = new EGLConfig[1];
        // eglChooseConfig(display, attributes, configs, num, configNum);
        // 用于获取满足attributes的所有config,参数1、2其意明显,参数3用于存放输出的configs,参数4指定最多输出多少个config,参数5由EGL系统写入,表明满足attributes的config一共有多少个
        if (!mEgl.eglChooseConfig(mEglDisplay, configAttribs, configs, 1, numConfigs)) {// 获取所有满足attributes的configs,并选择一个
            throw new RuntimeException("eglChooseConfig failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError()));
        }

        int[] contextAttribs = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};// attrib_list,目前可用属性只有EGL_CONTEXT_CLIENT_VERSION, 1代表OpenGL ES 1.x,
                                                                                // 2代表2.0。同样在Android4.2之前,没有EGL_CONTEXT_CLIENT_VERSION这个属性,只能使用硬编码0x3098代替
        mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, // share_context,是否有context共享,共享的contxt之间亦共享所有数据。EGL_NO_CONTEXT代表不共享
                contextAttribs);// 创建context
        mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, configs[0], mSurfaceTexture,// 负责对Android Surface的管理
                null// Surface属性
                );// 获取显存,create a new EGL window surface
        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 pauseWhile() {
        synchronized (LOCK) {
            try {
                LOCK.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void destoryGLESContext() {
        LogHelper.d(TAG, LogHelper.getThreadName());
        mEgl.eglDestroyContext(mEglDisplay, mEglContext);
        mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
        mEglContext = EGL10.EGL_NO_CONTEXT;
        mEglSurface = EGL10.EGL_NO_SURFACE;
    }

    public void onPause() {
        mRenderer.onPause();
        mIsPaused = true;
    }

    public void onResume() {
        mRenderer.onResume();
        mIsPaused = false;
        requestRender();
    }

    public void onSurfaceChanged(final int width, final int height) {
        mPendingThreadAider.addToPending(new Runnable() {// 在GL线程中执行

                    @Override
                    public void run() {
                        mRenderer.onSurfaceChanged(width, height);
                    }
                });
    }

    private int mRendererMode = GLESTextureView.RENDERMODE_CONTINUOUSLY;

    public void setRenderMode(int mode) {
        mRendererMode = mode;
    }

    public void requestRender() {
        synchronized (LOCK) {
            LOCK.notifyAll();
        }
    }

    public void onDestroy() {
        mNeedRenderring = false;
        mRenderer.onDestroy();
        destoryGLESContext();
    }
}

 

demo:https://github.com/flycatdeng/GLTextureViewDemo

 

一个Activity里两个GLSurfaceView无法同时渲染,所以如果要分多个区渲染的话,这个TextureView倒也可以去用用

http://stackoverflow.com/questions/4991095/how-can-i-use-multiple-glsurfaceview-components-in-the-same-layout

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的 OpenGL ES 2.0 线性插值的示例代码,用于在两个顶点之间进行颜色的线性插值渲染: ```java import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.Matrix; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; public class MyRenderer implements GLSurfaceView.Renderer { private float[] vertices = { -0.5f, -0.5f, 0.0f, // 左下角顶点 0.5f, -0.5f, 0.0f, // 右下角顶点 0.0f, 0.5f, 0.0f // 顶部顶点 }; private float[] colors = { 1.0f, 0.0f, 0.0f, 1.0f, // 左下角顶点颜色 (红色) 0.0f, 1.0f, 0.0f, 1.0f, // 右下角顶点颜色 (绿色) 0.0f, 0.0f, 1.0f, 1.0f // 顶部顶点颜色 (蓝色) }; private int program; private int positionHandle; private int colorHandle; private int mvpMatrixHandle; private float[] projectionMatrix = new float[16]; @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); program = GLES20.glCreateProgram(); GLES20.glAttachShader(program, vertexShader); GLES20.glAttachShader(program, fragmentShader); GLES20.glLinkProgram(program); positionHandle = GLES20.glGetAttribLocation(program, "vPosition"); colorHandle = GLES20.glGetAttribLocation(program, "vColor"); mvpMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix"); GLES20.glUseProgram(program); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); float ratio = (float) width / height; Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1); } @Override public void onDrawFrame(GL10 gl) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); // 将投影矩阵传递给着色器 GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, projectionMatrix, 0); // 启用顶点位置和颜色属性 GLES20.glEnableVertexAttribArray(positionHandle); GLES20.glEnableVertexAttribArray(colorHandle); // 设置顶点位置数据 GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer); // 设置顶点颜色数据 GLES20.glVertexAttribPointer(colorHandle, 4, GLES20.GL_FLOAT, false, 0, colorBuffer); // 执行绘制 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); // 禁用顶点位置和颜色属性 GLES20.glDisableVertexAttribArray(positionHandle); GLES20.glDisableVertexAttribArray(colorHandle); } private int loadShader(int type, String shaderCode) { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; } // 着色器代码 private final String vertexShaderCode = "attribute vec4 vPosition;" + "attribute vec4 vColor;" + "uniform mat4 uMVPMatrix;" + "varying vec4 interpolatedColor;" + "void main() {" + " gl_Position = uMVPMatrix * vPosition;" + " interpolatedColor = vColor;" + "}"; private final String fragmentShaderCode = "precision mediump float;" + "varying vec4 interpolatedColor;" + "void main() {" + " gl_FragColor = interpolatedColor;" + "}"; } ``` 注意,上述代码是一个简化的示例,只绘制了一个三角形,并将顶点的颜色进行了线性插值。你可以根据实际需求进行更复杂的插值操作和渲染效果的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值