学习OpenGL ES for Android(一)

OpenGL是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API),而在嵌入式和移动平台的版本是OpenGL ES。Android最初就支持OpenGL ES的1.0版本,到现在已经支持到最新的3.2版本,下面的支持变化图

当然这个版本支持不是绝对的,还有看硬件是否支持,例如genymotion模拟器只有OpenGL ES 2.0版本,如果你使用了高版本的API会导致崩溃。OpenGL ES 1.x版本和2.0版本语法差异巨大,3.x版本向前兼容2.0版本。因此我们学习的版本是2.0既可兼容近100%的设备。

版本支持声明

可以在AndroidManifest.xml中加入下面这行使用特性的声明,Google Play将会过滤掉不支持指定OpenGL ES版本的用户,拒绝他们安装。

<!-- 需要OpenGL ES 2.0 -->
<uses-feature android:glEsVersion="0x00020000" android:required="true" />

版本号的高16位表示主要版本,低16位表示次版本;那么如果需要3.0,3.1则可以如下配置

<!-- 需要OpenGL ES 3.0 -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
<!-- 需要OpenGL ES 3.1 -->
<uses-feature android:glEsVersion="0x00030001" android:required="true" />

也可以在代码中判断gles的版本,version同样传入版本号即可(例如0x20000)

    public static boolean checkOpenGL(Activity activity, int version) {
        ActivityManager am = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
        if (am != null) {
            return am.getDeviceConfigurationInfo().reqGlEsVersion >= version;
        }
        return false;
    }

基础

两个基本的类GLSurfaceViewGLSurfaceView.Renderer
GLSurfaceView:继承自SurfaceView,用来显示渲染的图像。如果想操作你的图像,需要扩展触摸监听事件来处理。
GLSurfaceView.Renderer:GLSurfaceView的内部接口类,主要负责渲染图像。

GLSurfaceView主要方法:

  • setEGLContextClientVersion:设置OpenGL ES的版本,只能设置主要版本(例如:1,2,3),不能设置次要版本。
  • setEGLContextFactory:设置OpenGL ES的版本构建器,默认的构建器是根据版本设置的,可以自定义成版本自适应。
  • setRenderer:设置Renderer,如果不设置,那么界面显示的就是一片空白。
  • setRenderMode:设置Renderer的模式,有这么两种:1、RENDERMODE_WHEN_DIRTY(仅在创建时或调用requestRender时才会渲染内容);2、RENDERMODE_CONTINUOUSLY(不停的渲染)。
  • onPause:暂停渲染,在页面不再显示时可以调用,减少性能开销,例如在Activity的onStop时。
  • onResume:恢复渲染,类似onPause。
  • requestRender:请求渲染,通常是RENDERMODE_WHEN_DIRTY模式时使用,必须在setRenderer后才能使用。
  • queueEvent:插入一个Runnable任务到后台渲染线程上执行,必须在setRenderer后才能使用。
  • setDebugFlags:设置debug模式,主要有两种:1、DEBUG_CHECK_GL_ERROR(当GL调用glError()方法后如果发生异常会打印。主要用来跟踪OpenGL错误。);2、DEBUG_LOG_GL_CALLS(打印所有GL的verbose级别的日志);

GLSurfaceView.Renderer的主要方法:

  • onSurfaceCreated(GL10 gl, EGLConfig config):当Surface创建或重新创建时,这时可以进行初始化。
  • onSurfaceChanged(GL10 gl, int width, int height):Surface尺寸改变时,返回当前surface宽高,可以进行下一步操作。
  • onDrawFrame(GL10 gl):渲染绘制当前一帧时会调用。

OpenGL类:在包android.opengl下,主要有GLES20(OpenGL ES 2.0版本),GLES30,GLES31,GLES32和。

现在我们创建一个自定义的SurfaceView

public class BaseGLView extends GLSurfaceView {
    public BaseGLView(Context context) {
        this(context, null);
    }

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

    protected void setDebug() {
        setDebugFlags(BuildConfig.DEBUG ? DEBUG_LOG_GL_CALLS : DEBUG_CHECK_GL_ERROR);
    }

    protected void init() {
        // 设置版本
        setEGLContextClientVersion(2);
        // 设置Renderer
        setRenderer(new BaseRenderer());
        // 设置渲染模式(默认RENDERMODE_CONTINUOUSLY)
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }
}

新建一个自定义的Renderer类

public class BaseRenderer implements GLSurfaceView.Renderer {
    private int bg = Color.BLACK;

    public BaseRenderer() {
    }

    public BaseRenderer(int bg) {
        this.bg = bg;
    }


    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // 设置背景色
        GLES20.glClearColor(Color.red(bg) / 255.0f, Color.green(bg) / 255.0f,
                Color.blue(bg) / 255.0f, Color.alpha(bg) / 255.0f);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        // 设置显示范围
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // 清屏
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    }
}


这时在Activity中使用setContentView(new BaseGLView(this));就可以了,Activity显示的就是一个黑色背景的布局。源码可以参考代码(https://github.com/jklwan/OpenGLProject/blob/master/sample/src/main/java/com/chends/opengl/view/BaseGLView.java

主要参考资料:
Android官方文档(https://developer.android.com/guide/topics/graphics/opengl
OpenGL ES文档(https://www.khronos.org/opengles/
learnopengl(https://learnopengl-cn.github.io/
《OpenGL ES 2 for Android: A Quick-Start Guide》(英文原版电子书)

Android的文档主要用来入门,参考learnopengl的文档来进行一步步的学习,当然learnopengl是桌面版的文档,和移动版有些区别,需要OpenGL ES文档和其他来学习。

最后是一个自定义的EGLContextFactory,当3.0可用时使用3.0,当3.0版本不可用时使用2.0

private static class ContextFactory implements GLSurfaceView.EGLContextFactory {

        private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
        private BaseListener<Integer> listener;

        public ContextFactory(BaseListener<Integer> listener) {
            this.listener = listener;
        }

        @Override
        public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
            EGLContext context = null;
            Integer version = null;
            try {
                context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT,
                        new int[]{EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE});
            } catch (Exception ex) {
                LogUtil.e(ex);
            }

            if (context == null || context == EGL10.EGL_NO_CONTEXT) {
                LogUtil.d("un support OpenGL ES 3.0 ");
                try {
                    context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT,
                            new int[]{EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE});
                } catch (Exception ex) {
                    LogUtil.e(ex);
                }
            } else {
                version = 3;
            }
            if (context == null || context == EGL10.EGL_NO_CONTEXT) {
                LogUtil.d("un support OpenGL ES 2.0 ");
            } else {
                version = 2;
            }
            if (listener != null) {
                listener.onFinish(version);
            }
            return context;
        }

        @Override
        public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
            if (!egl.eglDestroyContext(display, context)) {
                LogUtil.d("destroyContext false");
            }
        }
    }

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
In Pro OpenGL ES for Android, you'll find out how to harness the full power of OpenGL ES, and design your own 3D applications by building a fully-functional 3D solar system model using Open GL ES! OpenGL has set the standard for 3D computer graphics, and is an essential aspect of Android development. This book offers everything you need to know, from basic mathematical concepts to advanced coding techniques. You'll learn by building a fascinating 3D solar system simulator! After introducing Open GL ES, Pro OpenGL ES for Android explains the basics of 3D math and then orients you to the native Android 3D libraries you'll be using in your own 3D games and the solar system project you'll build using this book. Through the solar system example project, you'll learn how to incorporate a variety of graphic and animation techniques into your applications. You will also discover how the full spectrum of 3D development that awaits, with topics such as lighting, texture-mapping, modeling, shaders, blending modes, and several more advanced concepts. By the time you finish Pro OpenGL ES for Android, you'll have learned all the skills you'll need to build your own incredible 3D applications, based on one of the most powerful 3D libraries available. What you'll learn * The basics of 3D mathematics, and how they are applied in the OpenGL library * How to design and build your 3D worlds * To create 2D interfaces within the 3D world * To develop animation and 3D movement * How to implement 3D shading, coloring, and texturing * The differences between OpenGL and other 3D toolkits * To build a fully-functional 3D solar system simulator using OpenGL ES Who this book is for Experienced Android programmers who want to enter the 3D world of OpenGL ES programming. Table of Contents * Introduction to OpenGL ES and Our 3D Solar System Project * Generating a Basic OpenGL Program * Getting Past the 3D Math * Shading, Lighting and Colors * Materials and Textures * Animation * Creating a User Interface * Blending Modes, Buffer Objects, and Other Cool Stuff * Latest Features of OpenGL ES * Ray Tracing, Hidden Surfaces, and Other Advanced Topics Appendix A: APIs
好的,下面是一个简单的Android OpenGL ES实现3D抛骰子的示例代码: 首先在build.gradle中添加OpenGL ES依赖: ```groovy implementation 'com.android.support:support-v4:28.0.0' implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.google.android.gms:play-services-gcm:16.0.0' implementation 'com.google.android.gms:play-services-ads:17.1.1' implementation 'com.google.android.gms:play-services-auth:16.0.1' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:design:28.0.0' implementation 'com.android.support:cardview-v7:28.0.0' implementation 'com.android.support:recyclerview-v7:28.0.0' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.squareup.okhttp3:okhttp:3.11.0' implementation 'com.squareup.okio:okio:1.14.0' implementation 'com.github.bumptech.glide:glide:4.9.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' implementation 'com.squareup.retrofit2:retrofit:2.5.0' implementation 'com.squareup.retrofit2:converter-gson:2.5.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'io.reactivex.rxjava2:rxjava:2.2.8' ``` 接着创建一个继承自GLSurfaceView的自定义视图: ```java public class DiceView extends GLSurfaceView implements Renderer { private final static int DICE_NUM = 3; // 骰子数量 private final static float DICE_SIZE = 0.5f; // 骰子大小 private final static float DICE_SPEED = 0.1f; // 骰子旋转速度 private Dice[] dices = new Dice[DICE_NUM]; // 骰子数组 private float xAngle = 0; // x轴旋转角度 private float yAngle = 0; // y轴旋转角度 private float zAngle = 0; // z轴旋转角度 public DiceView(Context context) { super(context); setRenderer(this); setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { for (int i = 0; i < DICE_NUM; i++) { dices[i] = new Dice(getContext(), DICE_SIZE, DICE_SPEED); } gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); gl.glEnable(GL10.GL_DEPTH_TEST); gl.glEnable(GL10.GL_TEXTURE_2D); gl.glShadeModel(GL10.GL_SMOOTH); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); float ratio = (float) width / height; gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); } @Override public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); GLU.gluLookAt(gl, 0, 0, 3, 0, 0, 0, 0, 1, 0); gl.glRotatef(xAngle, 1, 0, 0); gl.glRotatef(yAngle, 0, 1, 0); gl.glRotatef(zAngle, 0, 0, 1); for (int i = 0; i < DICE_NUM; i++) { dices[i].draw(gl); } } public void setAngles(float xAngle, float yAngle, float zAngle) { this.xAngle = xAngle; this.yAngle = yAngle; this.zAngle = zAngle; } } ``` 然后创建一个Dice类来表示骰子: ```java public class Dice { private FloatBuffer vertexBuffer; private FloatBuffer textureBuffer; private ByteBuffer indexBuffer; private int[] textures = new int[1]; private int[] diceValues = {1, 2, 3, 4, 5, 6}; private float diceSize; private float diceSpeed; private float angleX = 0; private float angleY = 0; private float angleZ = 0; private float[] vertices = { -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f }; private float[] textureCoords = { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0 }; private byte[] indices = { 0, 1, 2, 0, 2, 3, 0, 3, 7, 0, 7, 4, 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 3, 2, 6, 3, 6, 7, 4, 7, 6, 4, 6, 5 }; public Dice(Context context, float diceSize, float diceSpeed) { this.diceSize = diceSize; this.diceSpeed = diceSpeed; ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); vertexBuffer = vbb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); ByteBuffer tbb = ByteBuffer.allocateDirect(textureCoords.length * 4); tbb.order(ByteOrder.nativeOrder()); textureBuffer = tbb.asFloatBuffer(); textureBuffer.put(textureCoords); textureBuffer.position(0); indexBuffer = ByteBuffer.allocateDirect(indices.length); indexBuffer.put(indices); indexBuffer.position(0); Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.dice); GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); Random random = new Random(); setDiceValue(diceValues[random.nextInt(6)]); } public void draw(GL10 gl) { gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer); gl.glPushMatrix(); gl.glRotatef(angleX, 1, 0, 0); gl.glRotatef(angleY, 0, 1, 0); gl.glRotatef(angleZ, 0, 0, 1); gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_BYTE, indexBuffer); gl.glPopMatrix(); gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); } public void setDiceValue(int value) { angleX = 0; angleY = 0; angleZ = 0; switch (value) { case 1: angleX = 90; break; case 2: angleX = -90; break; case 3: angleY = 90; break; case 4: angleY = -90; break; case 5: angleZ = 90; break; case 6: angleZ = -90; break; } } public void update() { angleX += diceSpeed; angleY += diceSpeed; angleZ += diceSpeed; } } ``` 最后在Activity中使用DiceView即可: ```java public class MainActivity extends AppCompatActivity { private DiceView diceView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diceView = new DiceView(this); setContentView(diceView); } @Override protected void onResume() { super.onResume(); diceView.onResume(); } @Override protected void onPause() { super.onPause(); diceView.onPause(); } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_MOVE: float dx = x - lastX; float dy = y - lastY; diceView.setAngles(dy * 0.5f, dx * 0.5f, 0); diceView.requestRender(); break; } lastX = x; lastY = y; return true; } } ``` 这样就实现了一个简单的Android OpenGL ES实现3D抛骰子的程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值