为了账号安全,请及时绑定邮箱和手机立即绑定

OpenGLES Android篇零基础系列(一):OpenGLES2.x可渲染管道基本流程

转载请注明出处

前言

OpenGL ES是OpenGL的一个子集,是针对手机、PDA和游戏主机等嵌入式设备而设计的。该API由Khronos集团定义推广,Khronos是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。
因此OpenGL ES作为第三方库被应用在android中。
到目前为止,OpenGL ES已经发展有了3个版本,OpenGL ES 1.0 , OpenGL ES 2.0 , OpenGL ES 3.0。其中OpenGL ES 1.0 是以OpenGL 1.3规范为基础,OpenGL ES 2.0 是以OpenGL 2.0 为基础,OpenGL ES 3.0是移动设备专用,以OpenGL 4.3为标准。

值得注意的是,在OpenGL2.0以前,OpenGL用的都是固定管道。之后就改为可渲染管道了,
所谓固定管道,就是固定工序,固定流程,以开关的形式开发出来供开发者调用,要实现什么功能,开发者得自己去考虑这些开关之间的组合形式,效率很低。
而渲染管道,就弥补了固定管道的缺点,它自由度高,可通过编程来实现功能,效率在大提高。因此这也是为什么在2.xAPI中删除了以前的一些固定管道的API。

因此,本文将结合一个例子,具体看一下可渲染管道怎么编程。

正文:如何利用OpenGL ES 2.0可渲染管道来创建简单的三角图形

第一步:创建GLSurfaceView对象

【说明】:
GLSurfaceView 是为OpenGL专门定义的一个类,它继承自SurfaceView。因此包含SurfaceView的一切特性。同时,它也增加了用于OpenGL特有的一些方法,比如:setRender(Render render)等。
想探究GLSurfaceView渲染过程的同学,可以去官网看看。

获取GLSurfaceView对象的途径有2种,可以直接在xml中写,也可以在代码中new。
本文我们选用后一种,eg:

public class SunnyOpenGLActivity extends FragmentActivity {
    private GLSurfaceView mGLSurfaceView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initView();
        setContentView(mGLSurfaceView);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mGLSurfaceView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mGLSurfaceView.onPause();
    }

    private void initView() {
        mGLSurfaceView = new GLSurfaceView(this);
    }

第二步:自定义Render类实现GLSurfaceView.Render接口

这一步是最重要的一步,因为着色器的生成,矩阵的变换,纹理的加载等都是在这里实现的,GLSurfaceView.Render有三个重要的待实现的接口:
onSurfaceCreated(GL10 glUnused,EGLConfig config);
onSurfaceChanged(GL10 glUnused,int width,int height);
onDrawFrame(GL10 glUnused);

以上三个方法回调的时机分别如下:

方法 登时调用
onSurfaceCreated 当GLSurfaceView 实例生成时回调;
onSurfaceChanged 当手机横/竖屏切换时回调;
onDrawFrame 是由系统自动回调的,它会以一定的帧频率来调用重绘View。

当设置GLSurfaceView的渲染模式为GLSurfaceView.RENDERMODE_CONTINUOUSLY或不设置时,系统就会主动渲染,就会回调onDrawFrame()方法,
如果设置为 RENDERMODE_WHEN_DIRTY ,则还得手动调用requestRender(),才会渲染。

自定义SunnyGLRender类如下:

public class SunnyGLRender implements GLSurfaceView.Renderer {
    private int mProgram;
    private int maPostionHandle;

    private FloatBuffer mTriangleVB;
    //定义顶点(vertex)着色器命令语句
    private final String vertexShaderCode =
            "uniform mat4 uMVPMatrix; \n" +
            "attribute vec4 vPosition; \n" +
                    "void main(){              \n" +
                    " gl_Position = vPosition; \n" +
                    "}                         \n";

    //片元着色器
    private final String fragmentShaderCode =
            "precision mediump float;\n" +
                    "void main(){ \n" +
                    " gl_FragColor = vec4 (0.63671875,0.76953125,0.22265625,1.0);\n" +//RGBA
                    "}\n";

    //应用投影与相机视图
    private int muMVPMatrixHandle;
    //用于存储变换矩阵结果的总变换矩阵[4*4]
    private float[] mMVPMatrix = new float[16];
    //[4*4]的视图变换矩阵
    private float[] mVMatrix = new float[16];
    //[4*4]的投影变换矩阵
    private float[] mProjMatrix = new float[16];
    //[4*4]的模型变换矩阵
    private float[] mMMatrix = new float[16];

    public float mAngle;

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        //GLES20:为OpenGL ES2.0版本,相应的
        //GLES30:OpenGL ES3.0
        //黑色背景
        GLES20.glClearColor(0.5f,0.5f,0.5f,1.0f);
        //glClear:清除缓冲区标志,这里为:清除颜色缓冲及深度缓冲,把整个窗口清除为黑色背景
        GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
        initShapes();
        int vertextShader = loadShader(GLES20.GL_VERTEX_SHADER,vertexShaderCode);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentShaderCode);
        //申请特定的着色器,program != 0 申请成功
        mProgram = GLES20.glCreateProgram();
        if(mProgram != 0 ){
        GLES20.glAttachShader(mProgram,vertextShader);
        GLES20.glAttachShader(mProgram,fragmentShader);
        //连接着色器
        GLES20.glLinkProgram(mProgram);
           int[] linkStatus = new int[1];
            //查看着色器连接情况
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] != GLES20.GL_TRUE) {
                //连接失败
                Log.e(TAG, "Could not link program: ");
                Log.e(TAG, GLES20.glGetProgramInfoLog(program));
                GLES20.glDeleteProgram(program);
                program = 0;
            }
           //获取着特定着色器vPosition参数。
        maPostionHandle = GLES20.glGetAttribLocation(mProgram,"vPosition");

       }
    }

    /**
     * 初始化三角形的一些参数
     */
    private void initShapes() {
    /**
     * X,Y,Z三轴顶点坐标
     * U,V也即S,T,是图片,视频等以纹理的形式加载到GLSurfaceView中时的坐标,即纹理坐标
     * U,V纹理坐标是无方向的
     */
     //第一种写法:顶点坐标与纹理坐标写一起
        float trianlgeCoords[] = {
            //X,Y,Z,U,V
            -1.0f, -0.5f, 0, 0.0f, 0.5f,
            1.0f, -0.5f, 0, 1.0f, 0.5f,
            0.0f,  1.0f, 0, 0.5f,  0.0f
        };

   //第二种写法:顶点坐标与纹理坐标分开写:(因为Z为0,只考虑二维平面,)
     float veticsCoor[] = {
     //X,Y
     -1.0f,-0.5f,
     1.0f,-0.5f,
     0.0f,1.0f,
     };
     float textureCoor[] = {
     //U,V
     0.0f,0.5f,
     1.0f,0.5f,
     0.5f,0.0,
     };

//把顶点坐标存入Buffer中
        ByteBuffer vbb = ByteBuffer.allocateDirect(trianlgeCoords.length * 4);
        vbb.order(ByteOrder.nativeOrder());
        mTriangleVB = vbb.asFloatBuffer();
        mTriangleVB.put(trianlgeCoords);
        mTriangleVB.position(0);
    }

    /**
     * 加载指定着色器
     * @param type
     * @param shaderCode
     * @return
     */
    private int loadShader(int type,String shaderCode){

        int shader = GLES20.glCreateShader(type);
        if(shader != 0 ){
        //加载着色器脚本程序(即本例的String 变量命令语句)
        //我们也可以把顶点着色器与片元着色器以文件的形式加载
         GLES20.glShaderSource(shader,shaderCode);
         //编译着色器脚本程序
         GLES20.glCompileShader(shader);
         //因为GLSL不能像我们写Java或C等,可以debug或Log调试,
         //这里我们就只能通过其返回的值,来判断是否编译通过,
         //如果编译失败,则检查GLSL是否写正确。
         //在写GLSL时,一定要检查参数类型,比如:vec2等2个浮点数的类型是否写成vec3或vec4等了,
         int[] compiled = new int[1];
            //查看编译结果
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
            if (compiled[0] == 0) {
                //编译失败,释放申请的着色器
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        } 
        return shader;
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
    //给定窗口大小
        GLES20.glViewport(0, 0, width, height);

    //
        float ratio = (float) width/height;
        //调用此方法来计算生成透视投影矩阵
        Matrix.frustumM(mProjMatrix,0,-ratio,ratio,-1,1,3,7);
        //当Sucrface改变时,获取指定着色器的uMVPMatrix参数
        muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram,"uMVPMatrix");
        //设定相机的视角
        //调用此方法产生摄像机9参数位置矩阵
        Matrix.setLookAtM(mVMatrix,0,
        0,0,-3, //相机的x,y,z坐标
        0,0,0,//目标对应的x,y,z坐标
        0,1.0f,1.0f//相机的视觉向量(upx,upy,upz,三个向量最终的合成向量的方向为相机的方向)
        );

    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // Redraw background color

        //把左矩阵投影矩阵与右矩阵视图矩阵变换后的结果存储在总矩阵mMVPMatrix中
        Matrix.multiplyMM(mMVPMatrix,0,mProjMatrix,0,mVMatrix,0);

        //为三角形创建一个旋转动作
        /*long time = SystemClock.uptimeMillis() % 4000L;
        mAngle = 0.090f * ((int)time);*/
        //创建一个绕x,y,z轴旋转一定角度的矩阵
        Matrix.setRotateM(mMMatrix,0,mAngle,0,0,1.0f);
        Matrix.multiplyMM(mMVPMatrix,0,mVMatrix,0,mMMatrix,0);
        Matrix.multiplyMM(mMVPMatrix,0,mProjMatrix,0,mMVPMatrix,0);
        GLES20.glUniformMatrix4fv(muMVPMatrixHandle,1,false,mMVPMatrix,0);

        //把Program用到OpenGL环境中
        GLES20.glUseProgram(mProgram);
        //准备画三角形的数据
             //为指定着色器取出的参数赋值   GLES20.glVertexAttribPointer(maPostionHandle,3,GLES20.GL_FLOAT,false,12,mTriangleVB);
             //使用这个从指定着色器中取出的参数的值
        GLES20.glEnableVertexAttribArray(maPostionHandle);

        //开始画
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,3);

    }
}

第三步:权限声明

经过以上二步,程序还不能运行,得在AndroidManifest.xml中声明相应权限。

<!-- Tell the system this app requires OpenGL ES 2.0. -->
    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

运行程序 就可以看到三角形了。

下一节

参考资料

Android OpenGL ES2.0 开发文档
Android OpenGL ES 应用(二) 纹理
浅学OpenGLES2.0
google ApiDemo(这个自己看官方Demo就好了,就没有贴出下载地址了)

点击查看更多内容
6人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
75
获赞与收藏
235

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消