图形编程

OpenGL ES---绘制二维图形

2018-03-04  本文已影响263人  风雪围城

背景

 绘制 3D 图,总觉得是一件很炫酷的事。虽然在项目中一直没有用到过,但是还是想找个时间,实践一下。
 绘制二维图形,尽管使用 OpenGL 有它的优势,但是还是感觉有点杀鸡用牛刀的意 思。这里主要是借助对 二维图形 的绘制过程,解释相关概念。

什么是 OpenGL ES

 首先,来说明一下 OpenGL ES for Embedded Systems(OpenGL ES)。它是 OpenGL 的子集,用以渲染 2D 、3D 矢量图的跨语言、跨平台的API,这个 API 通常会和 GPU 交互,完成硬件加速渲染。
 Android 平台支持不同版本 OpenGL ES 的 API。其中:

 为了获取更广泛的设备支持,通常会基于 OpenGL ES 2.0 做开发。本文也是基于该版本展开。

Android 平台提供的基础

 Android 框架层提供了两个对象以使用 OpenGL ES API 操作图像:GLSurfaceView 和 GLSurfaceView.Renderer。

GLSurfaceView

 GLSurfaceView 继承自 SurfaceView,拥有专用的 surface 以展示 OpenGL 渲染。它提供了一下特性:

//设置一下模式,为被动刷新
glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

简单的使用方式如下所示:

//直接创建一个 GLSurfaceView,当然,也可以通过布局文件创建
glSurfaceView = new GLSurfaceView(this) ;
//使用 OpenGL ES 2.0 context.
glSurfaceView.setEGLContextClientVersion(2);
//设置我们自定义的 renderer
glSurfaceView.setRenderer(new MyRenderer());
setContentView(glSurfaceView);
GLSurfaceView.Renderer

 负责进行帧渲染。提供的回调函数有:

管线渲染过程

 要明白这个过程,首先要知道什么是管线。所谓管线,就是在显卡上执行的将数据源转换投射到屏幕像素点上的过程。也就是将我们通过顶点定义的形状,显示到屏幕上。

管线渲染-1

如上图所示,

  1. 定义顶点;
  2. 通过 vertexShader着色器 告知 GPU 顶点的位置等属性;
  3. 通过图元装配,生成要绘制的形状;
  4. 光栅化处理,将所有的点转化为片元(fragment);
  5. 通过 fragmentShader着色器 为片元上色;
  6. 将片元投射到屏幕上的像素上。

更加形象一点的过程如下所示:


管线渲染-2

 注意,其中 vertexShader 和 fragmentShader 是通过 GLSL 语言定义的,并直接运行在 GPU 上。

VertexShader

顶点着色器,主要用于确定顶点位置,由 GLSL 语言定义,对于每个顶点都会执行该程序。通常用法如下:

//通过 GLSL 定义 VertexShader
private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                     "uniform mat4 u_Matrix;"+
                    "void main() {" +
                   // "gl_Position = vPosition;" +
                     "gl_Position = u_Matrix * vPosition;" +
                    "gl_PointSize = 10.0;"+
                    "}";

 @Override
 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    ...
//取出位置索引
        aPositionLocation = GLES20.glGetAttribLocation(program,"vPosition") ;
 //将位置索引和我们定义的数据源 vertexes 进行绑定,将 vertexes 中的每个顶点拿出来赋值到 vPosition ,并分别执行上面定义的 GLSL 程序
       GLES20.glVertexAttribPointer(aPositionLocation,2,GLES20.GL_FLOAT,false,0,vertexes);
        GLES20.glEnableVertexAttribArray(aPositionLocation);
}
FragmentShader

片元着色器,目的就是告诉 GPU 每个片段的最终颜色应该是什么。对于基于图元的每个片段,片段着色器都会被调用一次。因此,如果一个三角形被映射到 10000 个片段,片段着色器就会被调用 10000次。

 private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";
   @Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//取出颜色索引
      aColorLocation = GLES20.glGetUniformLocation(program,"vColor") ;
}

@Override
public void onDrawFrame(GL10 gl) {
//给颜色赋值
        GLES20.glUniform4f(aColorLocation,  0f,1,1, 1.0f);
//绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
}

完整代码如下:

public class MyRenderer implements GLSurfaceView.Renderer {

    private FloatBuffer vertexes ;

    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                     "uniform mat4 u_Matrix;"+
                    "void main() {" +
                     "gl_Position = u_Matrix * vPosition;" +
                    "gl_PointSize = 10.0;"+
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";

    private int aPositionLocation ;
    private int aColorLocation ;
    private int uMatrixLocation;
    private final float[] projectionMatrix = new float[16];

    private void createVertexes(){
        float [] vertexesArray = new float[]{
                0,1,
                -1,-1,
                1,-1
        } ;
        vertexes.clear();
        vertexes.put(vertexesArray) ;
    }

    private void init(){
//创建本地内存,以便将我们定义的顶点放进去,供设备访问
//堆内存上的数据,GPU是无法直接访问的
        vertexes =  ByteBuffer.allocateDirect(6*4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer() ;
        //创建 顶点 坐标
        createVertexes();
    }

    public MyRenderer(){
        init();
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(1f,1f,0f,0f);

        int vertexShader = ShaderHelper.compileVertexShader(vertexShaderCode) ;
        int fragmentShader = ShaderHelper.compileFragmentShader(fragmentShaderCode) ;
        int program =  ShaderHelper.linkProgram(vertexShader, fragmentShader);

        if (LoggerConfig.ON) {
            ShaderHelper.validateProgram(program);
        }

        GLES20.glUseProgram(program);
        aPositionLocation = GLES20.glGetAttribLocation(program,"vPosition") ;
        aColorLocation = GLES20.glGetUniformLocation(program,"vColor") ;
        uMatrixLocation = GLES20.glGetUniformLocation(program, "u_Matrix");
        vertexes.position(0) ;
        GLES20.glVertexAttribPointer(aPositionLocation,2,GLES20.GL_FLOAT,false,0,vertexes);
        GLES20.glEnableVertexAttribArray(aPositionLocation);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        // 根据屏幕方向设置投影矩阵
        float ratio= width > height ? (float)width / height : (float)height / width;
        if (width > height) {
            // 横屏
            Matrix.orthoM(projectionMatrix, 0, -ratio, ratio, -1, 1, 0, 5);
        } else {
            Matrix.orthoM(projectionMatrix, 0, -1, 1, -ratio, ratio, 0, 5);
        }
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // Clear the rendering surface.
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        // Assign the matrix
        GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, projectionMatrix, 0);
        GLES20.glUniform4f(aColorLocation,  0f,1,1, 1.0f);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
    }
}

下面对以上代码块做几点说明:

glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

小结

 本节主要借用二维图形的绘制,讲解了 OpenGL ES 在 Android 应用中使用的相关概念。下面想讲述一下坐标变换。

参考链接:
https://developer.android.com/training/graphics/opengl/environment.html
http://www.cs.ucr.edu/~shinar/courses/cs130-spring-2012/schedule.html
https://www.zhihu.com/question/29163054
https://en.wikibooks.org/wiki/GLSL_Programming/OpenGL_ES_2.0_Pipeline

上一篇 下一篇

猜你喜欢

热点阅读