OpenGl入门

OpenGl入门

什么是OpenGL

Open Graphics Library,图形领域的工业标准,是一套跨平台的、专业的、图形变成软件接口。它用于二维、三维图像,是一个功能强大的调用方便的底层图形库。

OpenGL与硬件无关,可以在不同的平台比如Windows、Linux、Mac、Andorid、IOS之间进行移植,因此也得到了广泛的应用。

Android 中使用OpenGl ES

GLSurfaceView,它继承自SurfaceView,它内嵌的sufface专门负责OpenGl渲染,管理Surface与EGL,允许自定义渲染器,让渲染器在独立的线程中运作,和UI线程分离,并且支持按需渲染(on-demand)和连续渲染(continuous)。

OpenGL是一个跨平台操作GUP的API,但是OpenGL需要本地视窗系统进行交互,这几需要一个中间层,EGL就是连接OpenGL ES和本地窗口的接口,引入EGL就是为了屏蔽不同平台上的区别。

CPU和GPU的区别

  • CPU是机器的大脑,中央处理器,用来发号施令
  • GPU的结构主要包括运算器(ALU),控制单元(CU),寄存器(Register),高速缓存器(Cache)和他们之间通讯的数据,控制及状态的总线

计算单元主要执行算数运算,位移和转换

存储单元主要用来保存运算中产生的数据以及指令等

控制单元则对指令译码,发出要完成每条指令要执行的各个操作的控制信号。

CPU中控制器和缓存器比较多,GPU中计算单元比较多。GPU无法单独工作,必须由CPU进行控制调用才能工作,CPU可以单独作用。

Android中渲染步骤

UI对象–>CPU处理为纹理–>通过OpenGLES接口调用GPU

–>GPU对图像进行栅格化–>硬件时钟–>垂直同步–>投射到屏幕

一维 点 顶点

二维 线 2个顶点

三维 面 3个顶点

在OpenGl的世界中中是又很多的3个顶点组成。

  1. 一个3D图片根据像素可以形成无数个顶点,顶点的连线叫做纹理(顶点着色器)
  2. 根据纹理形成一个一个像素(就像PS中对文字进行像素处理)(光栅化)
  3. 对像素进行着色(片元着色器)
  4. 上色之后交给GPU渲染(OpenGl渲染)

在使用OpenGl的时候,第二个和第四个是由系统完成的,我们需要手动写第一个和第三个,告诉系统有多少顶点和怎么着色

练习:

使用OpenGl绘制一个三角形

新建一个项目,Andorid系统中已经有OpenGl,不用导入第三方库,不过需要在AndroidManifest.xml中声明需要使用OpenGl


OpenGl 使用套路

  • 创建顶点数组
  • 使用gl语言写顶点着色器和片元着色器
  • 将java声明是顶点数组,颜色数组,传给gl语言中的变量
  • GPU开始渲染

自定义一个View继承自GLSurfaceView

public class GLView extends GLSurfaceView {

    public GLView(Context context) {
        super(context);
    }

    public GLView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setEGLContextClientVersion(2);
        setRenderer(new GLRander(this));
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }
}

定义一个渲染器,定义一个三角形的类Triangle,绘制的方法交给它自己来做,因为还可能绘制别的图形。

public class GLRander implements GLSurfaceView.Renderer {
    protected View mView;
    Triangle mTriangle;
    public GLRander(View view) {
        mView = view;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(0, 0, 0, 0);
        mTriangle = new Triangle();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        mTriangle.onSurfaceChanged(gl, width, height);
    }

    /**
     * 该方法不断被调用
     */
    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
        mTriangle.onDrawFrame(gl);
    }
}

开始绘制。

顶点着色器用来确定需要绘制的图形的顶点,片元着色器用来给它上色。

顶点着色器和片元着色器都是使用着色器语言(GLSL)语言编写,编写的程序可以直接推送给GPU执行。

public class Triangle {

    /**
     * 三个顶点
     */
    static float triangleCoords[] = {
            0.5f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
    };

    /**
     * 管道
     */
    private FloatBuffer vertexBuffer;

    /**
     * 顶点着色器
     */
    private String vertextShaderCode = "attribute vec4 vPosition;\n" +
            "uniform mat4 vMatrix;\n" +
            "void main(){" +
            "gl_Position=vMatrix*vPosition;" +
            "}";

    /**
     * 片元着色器
     */
    private final String fragmentShaderCode = "precision mediump float;\n" +
            "uniform  vec4 vColor;\n" +
            "void main(){\n" +
            "gl_FragColor=vColor;\t\n" +
            "}";

    int mProgram;

    public Triangle(){
        ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer=bb.asFloatBuffer();
//        把顶点  推送 给GPU
        vertexBuffer.put(triangleCoords);
        vertexBuffer.position(0);

        //创建顶点着色器 并在GPU中编译
        int shader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
        GLES20.glShaderSource(shader,vertextShaderCode);
        //编译顶点着色器
        GLES20.glCompileShader(shader);

        //创建片元着色器 并在GPU中编译
        int fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
        GLES20.glShaderSource(fragmentShader,fragmentShaderCode);
        //编译片元着色器
        GLES20.glCompileShader(fragmentShader);

        //将顶点着色器和片元着色器放到统一的管理器中进行管理
        mProgram = GLES20.glCreateProgram();
        GLES20.glAttachShader(mProgram,shader);
        GLES20.glAttachShader(mProgram,fragmentShader);

        //连接到着色器程序
        GLES20.glLinkProgram(mProgram);
    }
    public void onSurfaceChanged(GL10 gl, int width, int height) {
         //计算宽高比
        float ratio=(float)width/height;
        //投影矩阵
        Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 120);
        //相机
        Matrix.setLookAtM(mViewMatrix, 
                ////摄像机的坐标
                0, 0, 0, 7,
                //目标物的中心坐标
                0f, 0f, 0f,
                //相机方向
                0f, 1f, 0f);
        //计算变换矩阵
        Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);
    }

    /**
     * rgba
     */
    float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
    private float[] mViewMatrix=new float[16];
    private float[] mProjectMatrix=new float[16];
    private float[] mMVPMatrix=new float[16];
    public void onDrawFrame(GL10 gl) {
        //开始渲染
        //拿到总的程序
        GLES20.glUseProgram(mProgram);
        //拿到vPosition的地址
        int mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        //设置缩放
        int mMatrixHandler = GLES20.glGetUniformLocation(mProgram, "vMatrix");
        GLES20.glUniformMatrix4fv(mMatrixHandler,1,false,mMVPMatrix,0);
        //允许对这个地址进行读写
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //给顶点着色器赋值
        GLES20.glVertexAttribPointer(mPositionHandle,3,
                GLES20.GL_FLOAT,false,3*4,vertexBuffer);

        //给片元着色器赋值
        int mColorHandle = GLES20.glGetUniformLocation(mProgram,"vColor");
        GLES20.glUniform4fv(mColorHandle,1,color,0);
        //开始绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,3);
        //关闭读写
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
}

结果: