irpas技术客

Android GlSurfaceView总结及代码示例讲解_丞恤猿_android glsurfaceview

网络投稿 2430

##.GlSurfaceView 简介: GlSurfaceView继承自SurfaceView,额外提供了一些能力: ?? ?? ? ? ?1.支持用OpenGl对图像渲染后显示到Surface上, ?? ??? ?? ?2.提供了渲染器类Render,渲染器中有Surface的生命周期回调接口,以及每一帧图像的渲染接口。 ? ? ? ? ? ?3.提供了单独的GlThrad线程,设置渲染器后,该线程会启动,初始化EGL环境,并在线程中根据状态变化做Render中各个API接口的回调。 ##.OpenGl简单: ?? ?OpenGl是一个图形处理的标准接口库,它是跨平台的。 ?? ?手机由于性能相对较弱,难以支持OpenGl的全部功能,所以Android中提供OpenGl的子集OpenGl ES。 ?? ?Android中为图像提供OpenGl渲染能力并显示的控件是GlSurfaceView。 ?? ?在GlSurfaceView中GLThread会创建EGL环境,EGL是OpenGl和Android本地窗口系统之间的接口,OpenGl通过EGL来实现界面绘制。 ?? ?而GlSurfaceView会自动将EGL环境配置好,将OpenG渲染l处理后的图像绘制到界面上。 ##.EGL简介: ? ?OpenGl是一套跨平台的接口,它与各个平台本地窗口系统之间的交互,是借助于一个中间控制层,这个中间控制层就是EGL。 EGL也有自己的一套标准API,由各个平台的系统来完成其具体实现。 ? ? ?EGL是OpenGL和本地窗口体系进行联系的桥梁,负责管理OpenGL的运行状态、渲染图像到本地窗口或缓冲区等功能。 ? ?在Android系统中也是一样,OpenGL的每一步处理,都需要依赖于EGL提供的这些相关功能支持,所以必须先创建EGL环境,才能正常进行OpenGL处理。 ? ?不过Android中GLSurfaceView会自己在GLThread中完成EGL环境的初始化,使用GLSurfaceView时,开发者并不需要自己来初始化EGL环境。 #.内部机制分析: GLThread:调用SurfaceView的setRender()后,SurfaceView内部会创建GLThread线程,并在线程内部 创建EGL环境。 ? ?? GLThread的核心代码在 guardedRun()中,里面是一个while(true){...}死循环,进行EGL环境的管理并根据条件判断调用 Render的相关方法, 如Render的 onSurfaceCreated(), onSurfaceChanged()和onDrawFrame()方法等。因为OpenGl ES的绘制依赖EGL环境,所以必须在这个线程中进行。 EglHelper:负责为GLThread创建EGL环境。 GLSurfaceView:继承了SurfaceView的所有特性,负责提供Surface并管理Surface的状态。 ##.重要API &&.设置渲染器 TestRender render = new TestRender(); //为GlSurfaceView设置渲染器Render //这一步很关键,当设置渲染器后,会触发GlThread线程启动,然后创建EGL环境, // 并在线程中根据条件判断,来对应调用Render的各个接口 mGlSurfaceView.setRenderer(render); &&.设置渲染模式 /*设置GLThread的渲染方式,该值会影响GLThread内部while(true){}死循环内部的条件判断 1.Render.RENDERMODE_WHEN_DIRTY:表示被动渲染,需要手动调用相应方法来触发渲染。 ??? 设置该值,GLThread在死循环死循环中不会自动调用Render.onDrawFrame() ??? 只有在调用requestRender()或者onResume()等方法后才会改变死循环中的判断条件,调用一次Render.onDrawFrame() 2.Render.RENDERMODE_CONTINUOUSLY:表示持续渲染,该值为GLThread的默认渲染模式 ??? 设置该值时,GLThread在死循环中会不断的自动调用Render.onDrawFrame()。因此会比较消耗手机性能。*/ //当需要固定的刷新频率时,例如30帧/s,应该用Render.RENDERMODE_WHEN_DIRTY模式,然后手动控制刷新。 //注意:必须在设置Render之后才能设置,否则会报错 setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); &&.当调用requestRender()时,会手动在GLThread中触发一次Render.onDrawFrame()调用,进行一次绘制操作。 &&.由于操作OpenGL环境只能通过GL线程,所以GLSurfaceView为我们提供了queueEvent(Runnable r)这个方法,GLThread在内部死循环while(true){…}中,会调用这个传入的Runnable的run()方法。 ? ? &&.onPause()/onResume() 这两个方法实际回去调用GLThread的onPause()/onResume()。 onPause()会停止GLThread中的绘制,不再回调Render中的回到接口。 onResume()会恢复GLThread中的绘制,恢复回调Render中的回到接口。 (看源码,onPause()时GLThread会销毁EGL环境,onResume()时又会恢复EGL环境。) #.代码示例与讲解(见代码注释) public class TestGlSurfaceView extends GLSurfaceView { ??? public TestGlSurfaceView(Context context) { ??????? this(context, null); ??? } ??? public TestGlSurfaceView(Context context, AttributeSet attrs) { ??????? super(context, attrs); ??????? // 使用OpenGL ES 2.0 ??????? setEGLContextClientVersion(2); ??? } ??? @Override ??? public void setRenderer(Renderer renderer) { ??????? super.setRenderer(renderer); ??????? /*设置GLThread的渲染方式,该值会影响GLThread内部while(true){}死循环内部的条件判断 ??????? 1.Render.RENDERMODE_WHEN_DIRTY:表示被动渲染,需要手动调用相应方法来触发渲染。 ??????????? 设置该值,GLThread在死循环死循环中不会自动调用Render.onDrawFrame() ??????????? 只有在调用requestRender()或者onResume()等方法后才会改变死循环中的判断条件,调用一次Render.onDrawFrame() ??????? 2.Render.RENDERMODE_CONTINUOUSLY:表示持续渲染,该值为GLThread的默认渲染模式 ??????????? 设置该值时,GLThread在死循环中会不断的自动调用Render.onDrawFrame()。因此会比较消耗手机性能。*/ ??????? //当需要固定的刷新频率时,例如30帧/s,应该用Render.RENDERMODE_WHEN_DIRTY模式,然后手动控制刷新。 ??????? //注意:必须在设置Render之后才能设置,否则会报错 ??????? setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); ??? } ??? /** ???? * 该方法会触发GLThread恢复运行 ???? */ ??? @Override ??? public void onResume() { ??????? super.onResume(); ??? } ??? /** ???? * 该方法会触发GLThread进入等待状态 ???? */ ??? @Override ??? public void onPause() { ??????? super.onPause(); ??? } } //Render中一般是用OpenGl做来图形图像处理,需要了解Android提供的OpenGL ES用法 public class TestRender implements GLSurfaceView.Renderer { ??? private final String TAG = getClass().getSimpleName(); ??? private Triangle mTriangle; ??? private Square?? mSquare; ??? public TestRender(){ ??? } ??? @Override ??? public void onSurfaceCreated(GL10 gl, EGLConfig config) { ??????? ILog.d(TAG, "surfaceCreated()"); ??????? //surface被创建后需要做的处理 ??????? //这里是设置背景颜色 ??????? GLES20.glClearColor(0.0f,0.0f,0.0f,1.0f); ??????? // 初始化一个三角形 ??????? mTriangle = new Triangle(); ??????? // 初始化一个正方形 ??????? mSquare = new Square(); ??? } ?? ?// 渲染窗口大小发生改变或者屏幕方法发生变化时候回调 ??? @Override ??? public void onSurfaceChanged(GL10 gl, int width, int height) { ??????? ILog.d(TAG, "surfaceChanged()"); ? ? ? ??// 设置OpenGL视口的位置与大小, 最终图像会被输出到视口上显示 ?? ??? ?//????? 在此处对应的是在GlSurfaceView的Surface上现实在哪个区域 ?? ??? ?// 该API定义时的坐标方向与OpenGl二维图形中坐标方向相同, ?? ??? ?//????? 以(0,0)为的左下角,向右为x轴正方向,向上为y轴正方向. ?? ??? ?//????? 最终输出的画面会等比例缩放到在这个区域上,但设置的区域超出实际Surface的部分不会显示。 ??????? GLES20.glViewport(0,0,width,height); ??? } ??? @Override ??? public void onDrawFrame(GL10 gl) { ??????? ILog.d(TAG, "onDrawFrame()"); //??????? mTriangle.draw(); ??????? mSquare.draw(); ??? } } public class Triangle { ??? private final String vertexShaderCode = ??????????? "attribute vec4 vPosition;" + ??????????????????? "void main() {" + ??????????????????? "? gl_Position = vPosition;" + ??????????????????? "}"; ??? private final String fragmentShaderCode = ??????????? "precision mediump float;" + ??????????????????? "uniform vec4 vColor;" + ??????????????????? "void main() {" + ??????????????????? "? gl_FragColor = vColor;" + ??????????????????? "}"; ??? private FloatBuffer vertexBuffer; ??? // 数组中每个顶点的坐标数 ??? static final int COORDS_PER_VERTEX = 3; ??? static float triangleCoords[] = {?? // 按照逆时针方向: ??????????? 0.0f,? 0.622008459f, 0.0f, // top ??????????? -0.5f, -0.311004243f, 0.0f, // bottom left ??????????? 0.5f, -0.311004243f, 0.0f? // bottom right ??? }; ??? // 设置颜色RGBA(red green blue alpha) ??? float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f }; ??? private final int mProgram; ??? public Triangle() { ??????? // 为存放形状的坐标,初始化顶点字节缓冲 ??????? ByteBuffer bb = ByteBuffer.allocateDirect( ??????????????? // (坐标数 * 4 )float 占四个字节 ??????????????? triangleCoords.length * 4); ??????? // 使用设备的本点字节序 ??????? bb.order(ByteOrder.nativeOrder()); ??????? // 从ByteBuffer创建一个浮点缓冲 ??????? vertexBuffer = bb.asFloatBuffer(); ??????? // 把坐标加入FloatBuffer中 ??????? vertexBuffer.put(triangleCoords); ??????? // 设置buffer,从第一个坐标开始读 ??????? vertexBuffer.position(0); ??????? int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, ??????????????? vertexShaderCode); ??????? int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, ??????????????? fragmentShaderCode); ??????? // 创建一个空的 OpenGL ES Program ??????? mProgram = GLES20.glCreateProgram(); ??????? // 将vertex shader 添加到 program ??????? GLES20.glAttachShader(mProgram, vertexShader); ??????? // 将fragment shader 添加到 program ??????? GLES20.glAttachShader(mProgram, fragmentShader); ??????? // 创建一个可执行的 OpenGL ES program ??????? GLES20.glLinkProgram(mProgram); ??? } ??? private int mPositionHandle; ??? private int mColorHandle; ??? private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX; ??? private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex ??? public void draw() { ??????? // 将program 添加到 OpenGL ES 环境中 ??????? GLES20.glUseProgram(mProgram); ??????? // 获取指向vertex shader的成员vPosition的句柄 ??????? mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); ??????? // 启用一个指向三角形的顶点数组的句柄 ??????? GLES20.glEnableVertexAttribArray(mPositionHandle); ??????? // 准备三角形的坐标数据 ??????? GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, ??????????????? GLES20.GL_FLOAT, false, ??????????????? vertexStride, vertexBuffer); ??????? // 获取指向fragment shader的成员vColor的句柄 ??????? mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); ??????? // 设置三角形的颜色 ??????? GLES20.glUniform4fv(mColorHandle, 1, color, 0); ??????? // 画三角形 ??????? GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); ??????? // 禁用指向三角形的定点数组 ??????? GLES20.glDisableVertexAttribArray(mPositionHandle); ??? } ??? public int loadShader(int type, String shaderCode){ ??????? // 创建一个vertex shader 类型 (GLES20.GL_VERTEX_SHADER) ??????? // 或者一个 fragment shader 类型(GLES20.GL_FRAGMENT_SHADER) ??????? int shader = GLES20.glCreateShader(type); ??????? // 将源码添加到shader并编译 ??????? GLES20.glShaderSource(shader, shaderCode); ??????? GLES20.glCompileShader(shader); ??????? return shader; ??? } } public class Square { ??? private final String vertexShaderCode = ??????????? "attribute vec4 vPosition;" + ??????????????????? "void main() {" + ??????????????????? "? gl_Position = vPosition;" + ??????????????????? "}"; ??? private final String fragmentShaderCode = ??????????? "precision mediump float;" + ??????????????????? "uniform vec4 vColor;" + ??????????????????? "void main() {" + ??????????????????? "? gl_FragColor = vColor;" + ??????????????????? "}"; ??? private FloatBuffer vertexBuffer; ??? private ShortBuffer drawListBuffer; ??? // 每个顶点的坐标数 ??? static final int COORDS_PER_VERTEX = 3; ??? static float squareCoords[] = { ??????????? -0.5f,? 0.5f, 0.0f,?? // top left ??????????? -0.5f, -0.5f, 0.0f,?? // bottom left ??????????? 0.5f, -0.5f, 0.0f,?? // bottom right ??????????? 0.5f,? 0.5f, 0.0f }; // top right ??? private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // 绘制顶点的顺序 ??? // 设置颜色RGBA(red green blue alpha) //??? float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f }; ??? float color[] = { 0.15671875f, 0.65953125f, 0.84265625f, 1.0f }; ??? private final int mProgram; ??? public Square() { ??????? // initialize vertex byte buffer for shape coordinates ??????? ByteBuffer bb = ByteBuffer.allocateDirect( ??????????????? // (# of coordinate values * 4 bytes per float) ??????????????? squareCoords.length * 4); ??????? bb.order(ByteOrder.nativeOrder()); ??????? vertexBuffer = bb.asFloatBuffer(); ??????? vertexBuffer.put(squareCoords); ??????? vertexBuffer.position(0); ??????? // initialize byte buffer for the draw list ??????? ByteBuffer dlb = ByteBuffer.allocateDirect( ??????????????? // (# of coordinate values * 2 bytes per short) ??????????????? drawOrder.length * 2); ??????? dlb.order(ByteOrder.nativeOrder()); ??????? drawListBuffer = dlb.asShortBuffer(); ??????? drawListBuffer.put(drawOrder); ??????? drawListBuffer.position(0); ??????? int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, ??????????????? vertexShaderCode); ??????? int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, ??????????????? fragmentShaderCode); ??????? // 创建一个空的 OpenGL ES Program ??????? mProgram = GLES20.glCreateProgram(); ??????? // 将vertex shader 添加到 program ??????? GLES20.glAttachShader(mProgram, vertexShader); ??????? // 将fragment shader 添加到 program ??????? GLES20.glAttachShader(mProgram, fragmentShader); ??????? // 创建一个可执行的 OpenGL ES program ??????? GLES20.glLinkProgram(mProgram); ??? } ??? private int mPositionHandle; ??? private int mColorHandle; //??? private final int vertexCount = squareCoords.length / COORDS_PER_VERTEX; ??? private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex ??? private float[] mMVPMatrix=new float[16]; ??? private int mMatrixHandler; ??? public void draw() { ??????? //将程序加入到OpenGLES2.0环境 ??????? GLES20.glUseProgram(mProgram); ??????? //获取变换矩阵vMatrix成员句柄 ??????? mMatrixHandler= GLES20.glGetUniformLocation(mProgram,"vMatrix"); ??????? //指定vMatrix的值 ??????? GLES20.glUniformMatrix4fv(mMatrixHandler,1,false,mMVPMatrix,0); ??????? //获取顶点着色器的vPosition成员句柄 ??????? mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); ??????? //启用三角形顶点的句柄 ??????? GLES20.glEnableVertexAttribArray(mPositionHandle); ??????? //准备三角形的坐标数据 ??????? GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, ??????????????? GLES20.GL_FLOAT, false, ??????????????? vertexStride, vertexBuffer); ??????? //获取片元着色器的vColor成员的句柄 ??????? mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); ??????? //设置绘制三角形的颜色 ??????? GLES20.glUniform4fv(mColorHandle, 1, color, 0); ??????? //绘制三角形 ??????? //??????? GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount); ??????? //索引法绘制正方形 ??????? GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer); ??????? //禁止顶点数组的句柄 ??????? GLES20.glDisableVertexAttribArray(mPositionHandle); ??? } ??? public int loadShader(int type, String shaderCode){ ??????? // 创建一个vertex shader 类型 (GLES20.GL_VERTEX_SHADER) ??????? // 或者一个 fragment shader 类型(GLES20.GL_FRAGMENT_SHADER) ??????? int shader = GLES20.glCreateShader(type); ??????? // 将源码添加到shader并编译 ??????? GLES20.glShaderSource(shader, shaderCode); ??????? GLES20.glCompileShader(shader); ??????? return shader; ??? } } //Activity中的调用 public class TestGlSurfaceViewActivity extends BaseActivity { ??? @Override ??? protected BaseActivityPresenter getPresenter() { ??????? return null; ??? } ??? private TestGlSurfaceView mGlSurfaceView; ??? @Override ??? protected void onCreate(@Nullable Bundle savedInstanceState) { ??????? super.onCreate(savedInstanceState); ??????? setContentView(R.layout.test_gl_surfaceview_activity); ??????? mGlSurfaceView = (TestGlSurfaceView) findViewById(R.id.gl_surface_view); ??????? TestRender render = new TestRender(); ??????? //为GlSurfaceView设置渲染器Render ??????? //这一步很关键,当设置渲染器后,会触发GlThread线程启动,然后创建EGL环境, ??????? // 并在线程中根据条件判断,来对应调用Render的各个接口 ??????? mGlSurfaceView.setRenderer(render); ??? } } 相关笔记: Android SurfaceView总结及代码示例_丞恤猿的博客-CSDN博客 主要参考: 初识:Android中的GLSurfaceView - 简书 ?? ??? ??? ?? ? ? 初识Android OpenGL ES - 简书 (声明:部分图片获取自网络,这里只是用于学习分享,侵删!)


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Android #glsurfaceview