OpenGLES应用开发实践指南——OpenGLES2.0实战第一课

要用OpenGl ES在应用程序中绘制图像,首先你要创建与之对应的视图容器。

当中最直接的方法就是使用GLSurfaceView和GLSurfaceView.Renderer接口。

GLSurfaceView是OpenGL所绘制的图形的视图容器,而GLSurfaceView.Renderer控制在视图上所绘制的图像。想获取更多关于这两个类的信息,请参考OpenGl ES开发指南

GLSurfaceView只是把OpenGL ES图像组合到应用程序的其中一种方法,但对于绘制全屏或接近全屏的图形来说,它是一个很好的选择。开发者如果想把OpenGL ES图像组合成布局中的一小部分,可以查看TextureView。事实上,独立开发者也可以在SurfaceView上创建OpenGL ES视图,但这需要写更多额外的代码。

在Manifest声明OpenGL ES的使用  

要在应用程序中使用 OpenGL ES 2.0 API,你必须在Manifest添加以下的声明:

  1. <uses-feature android:glEsVersion="0x00020000" android:required="true" /> 

如果你的应用程序中使用到了纹理压缩,你必须声明应用程序所支持的压缩格式,以便提醒不支持这些格式的设备不会尝试运行你的应用程序:

  1. <supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" /> 
  2. <supports-gl-texture android:name="GL_OES_compressed_paletted_texture" /> 

想获取更多关于纹理压缩格式的信息,请参考OpenGl ES开发指南。

创建OpenGL ES的Activity  

Android使用到OpenGL ES的应用程序和其他的应用程序的activities一样,都有一个用户界面。当中不同就是,在activity的布局中你使用了什么控件,在很多应用中,你可能会使用TextView,Button和ListView,在使用了OpenGL ES的应用程序,你还可以添加GLSurfaceView。

package com.example.opengldemo;

import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;


public class OpenGLES20Activity extends AppCompatActivity {
    private GLSurfaceView glView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        glView = new MyGLSurfaceView(this);
        setContentView(glView);
    }

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

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

自定义GLSurfaceView:

package com.example.opengldemo;

import android.content.Context;
import android.opengl.GLSurfaceView;

/**
 *  GLSurfaceView 是一个可以绘制OpenGLES图像的专门视图,它本身没有处理功能,
 *
 *  所绘制的图像都是由你所设置的GLSurfaceView.Render来控制的。
 *
 */
public class MyGLSurfaceView extends GLSurfaceView {

    private final MyGLRenderer renderer;

    public MyGLSurfaceView(Context context){
        super(context);

        // 声明你使用的是OpenGlES2.0的API
        setEGLContextClientVersion(2);

        renderer = new MyGLRenderer();

        // Set the Renderer for drawing on the GLSurfaceView
        setRenderer(renderer);
    }
}

GLSurfaceView.Renderer渲染器介绍

GLSurfaceView是一个可以绘制OpenGL ES图像的专门视图,它本身没有处理功能,所绘制的图像都是由你所设置的GLSurfaceView.Renderer来控制的。Render就是整个程序的重点:

package com.example.opengldemo;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;

public class MyGLRenderer implements GLSurfaceView.Renderer {

    private Triangle mTriangle;

    /**
     * shader语言跟C语言很像,它有一个主函数,也叫void main(){}。
     * gl_Position是一个内置变量,用于指定顶点,它是一个点,三维空间的点,所以用一个四维向量来赋值。vec4是四维向量的类型,vec4()是它的构造方法。等等,三维空间,
     * 不是(x, y, z)三个吗?咋用vec4呢?四维是叫做齐次坐标,它的几何意义仍是三维,先了解这么多,记得对于2D的话,第四位永远传1.0就可以了。这里,是指定原点
     * (0, 0, 0)作为顶点,就是说想在原点(正中心)位置画一个点。gl_PointSize是另外一个内置变量,用于指定点的大小。这个shader就是想在原点画一个尺寸为20的点
     *
     */
    private String VERTEX_SHADER =
            "void main() {\n" +
                    "gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" +
                    "gl_PointSize = 200.0;\n" +
                    "}\n";
    private String FRAGMENT_SHADER =
            "void main() {\n" +
                    "gl_FragColor = vec4(1., 0., 0.0, 1.0);\n" +
                    "}\n";
    private int mGLProgram;


    //设置视图的OpenGL ES环境,只需调用一次
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // Set the background frame color
        GLES20.glClearColor(1.0f, 1.0f, 0.0f, 1.0f);  //设置清空屏幕用的颜色,设置背景底色

        /**
         * shader
         * GL ES 2.0与1.0版本最大的区别在于,把渲染相关的操作用一个专门的叫作着色语言的程序来表达,全名叫作OpenGL ES Shading language,它是一个编程语言,与C语言非常类似,
         * 能够直接操作矩阵和向量,运行在GPU之上专门用于图形渲染。它又分为两种,一个叫做顶点着色器(vertex shader),另一个叫做片元着色器(fragment shader)。
         *前者用来指定几何形状的顶点;后者用于指定每个顶点的着色。每个GL程序必须要有一个vertex shader和一个fragment shader,且它们是相互对应的。(相互对应,
         * 意思是vertex shader必须要有一个fragment shader,反之亦然,但并不一定是一一对应)。当然,也是可以复用的,比如同一个vertex shader,可能会多个fragment shader
         * 来表达不同的着色方案。
         *
         */


         **
         *
         *  创建一个着色器分三步:
         *  
         * 1) 创建Shader对象
         *
         * 2) 装载Shader源码
         *
         * 3) 编译Shader
         */
       /**
         * 1)glCreateShader
         * 它创建一个空的shader对象,它用于维护用来定义shader的源码字符串。支持以下两种shader:
         * (1) GL_VERTEX_SHADER: 它运行在可编程的“顶点处理器”上,用于代替固定功能的顶点处理;
         * (2) GL_FRAGMENT_SHADER: 它运行在可编程的“片断处理器”上,用于代替固定功能的片段处理;
         */
        int vertextShaderId = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);

        /**
         * 2)glShaderSource装载源码:
         * shader对象中原来的源码全部被新的源码所代替。
         */
        GLES20.glShaderSource(vertextShaderId,VERTEX_SHADER);

        /**
         * 3)glCompileShader
         * 编译存储在shader对象中的源码字符串,编译结果被当作shader对象状态的一部分被保存起来,可通过
         * glGetShaderiv函数获取编译状态。
         */
        GLES20.glCompileShader(vertextShaderId);

        // 创建一个片着色器,三个步骤介绍同上
        int fragmentShaderId = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
        GLES20.glShaderSource(fragmentShaderId,FRAGMENT_SHADER);
        GLES20.glCompileShader(fragmentShaderId);

        //建立一个空的program对象,shader对象可以被连接到program对像
        mGLProgram = GLES20.glCreateProgram();

        // 把vertex shader添加到program
        GLES20.glAttachShader(mGLProgram,vertextShaderId);

        // 把fragment shader添加到program
        GLES20.glAttachShader(mGLProgram,fragmentShaderId);

        // 做链接,可以理解为把两种shader进行融合,做好投入使用的最后准备工作
        GLES20.glLinkProgram(mGLProgram);

    }

    
    // 每次重绘都会重新调用一次
    public void onDrawFrame(GL10 unused) {
        // //擦除屏幕上的所有颜色,并用 glClearColor 中的颜色填充背景,因为我们要开始新一帧的绘制了,所以先清理,以免有脏数据。
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);  

       // 告诉OpenGL,使用我们在onSurfaceCreated里面准备好了的shader program来渲染
        GLES20.glUseProgram(mGLProgram); 


      // 开始渲染,发送渲染点的指令, 第二个参数是offset,第三个参数是点的个数。目前只有一个点,所以是1。
        GLES20.glDrawArrays(GLES20.GL_POINTS,0,1); 
    }



    //视图的几何发现变化时调用,例如,设备的屏幕的方向发生变化时才会调用
    public void onSurfaceChanged(GL10 unused, int width, int height) { 

       //设置了视口尺寸,告诉 OpenGL 可以用来渲染的 surface 的大小。 
       //(0, 0)是左上角,然后是width和heigh
        GLES20.glViewport(0, 0, width, height);

    }
}

在使用OpenGL ES 2.0时,你必须在你的GLSurfaceView构造器中添加多一句代码,以声明你是使用OpenGL ES 2.0的API:

        // Create an OpenGL ES 2.0 context 

         setEGLContextClientVersion(2); 

备注:如果你使用的是OpenGL ES 2.0的API,请确认你已经在应用程序的manifest进行了声明。

现在我们分析上面的Render渲染器。渲染器中提供了三个方法给Android系统调用,以控制什么可以,以及怎样绘制在GLSurfaceView上。它们分别是:

* onSurfaceCreated()) - 设置视图的OpenGL ES环境,只需调用一次

* onDrawFrame()) - 重新绘制每个视图时调用

* onSurfaceChanged()) - 视图的几何发现变化时调用,例如,设备的屏幕的方向发生变化

 

最后一个概念:着色器 shader


    GL ES 2.0与1.0版本最大的区别在于,把渲染相关的操作用一个专门的叫作着色语言的程序来表达,全名叫作OpenGL ESShading language,它是一个编程语言,与C语言非常类似,能够直接操作矩阵和向量,运行在GPU之上专门用于图形渲染。它又分为两种,一个叫做顶点着色器(vertex shader),另一个叫做片元着色器(fragment shader)。前者用来指定几何形状的顶点;后者用于指定每个顶点的着色。每个GL程序必须要有一个vertex shader和一个fragment shader,且它们是相互对应的。(相互对应,意思是vertex shader必须要有一个fragment shader,反之亦然,但并不一定是一一对应)。当然,也是可以复用的,比如同一个vertex shader,可能会多个fragment shader来表达不同的着色方案。  下面就是一段着色器片段:

//点着色器,定义点的位置和大小    
private String VERTEX_SHADER =
            "void main() {\n" +
                    "gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" +
                    "gl_PointSize = 200.0;\n" +
                    "}\n";

 //片着色器,定义由点组成的点或线或面的属性,比如填充颜色
    private String FRAGMENT_SHADER =
            "void main() {\n" +
                    "gl_FragColor = vec4(1.0, 0., 0.0, 1.0);\n" +
                    "}\n";

shader语言跟C语言很像,它有一个主函数,也叫void main(){}
gl_Position是一个内置变量,用于指定顶点,它是一个点,三维空间的点,所以用一个四维向量来赋值。

vec4是四维向量的类型,vec4()是它的构造方法。等等,三维空间,不是(x, y, z)三个吗?咋用vec4呢?四维是叫做齐次坐标,它的几何意义仍是三维,先了解这么多,记得对于2D的话,第四位永远传1.0就可以了。

这里,是指定原点(0, 0, 0)作为顶点,就是说想在原点(正中心)位置画一个点。由于这里只定义了一个点。并且指定填充颜色为红色,所以它的运行效果就是在手机(0,0,0)正中心画一个红色的大小为200像素的点,效果如图:

好了,我们的第一堂实践课就到这里了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值