Android 之 OpenGLES(二) 绘制四边形(正方形)

上篇文章,介绍了glsl语言的基本语法,现在我们用这些语法写个顶点着色器,和片元着色器。结合api绘制一个四边形。

为什么是四边形呢?别人的文章都是从三角形开始的,因为我想介绍绘制方式,用三角形感觉不好介绍,于是就直接来四边形。

一、基本知识

基本图元

因为在OpenGL中呢,只有点、线和三角形这三种图元,要想画一个正方形,把两个三角形拼起来即可。当然绘制其他更复杂图型的时侯会借助别的工具去构建。

OpenGLES世界坐标

屏幕中心点位原点,x轴向右,y轴向上,z轴向屏幕外,方向为正,反之为负

二、开始撸码

先说一下用opengl绘制的步骤吧:

  • 初始化GLSurfaceView
  • 编写顶点着色器和片元着色器
  • 实现Renderer接口
    1. onSurfaceCreated方法:初始化顶点颜色,加载着色器,初始化纹理等操作
    2. onSurfaceChanged方法:设置视口,投影
    3. onDrawFrame方法:传值给着色器(传入顶点,颜色,纹理等),
      绘制

先开始第一步,初始化GLSurfaceView

new_gl_surface.setEGLContextClientVersion(2)//设置版本,这里使用OpenGLES 2
new_gl_surface.setRenderer(NewGLRenderer())//设置自定义渲染器
new_gl_surface.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY//设置为主动渲染

创建顶点着色器和片元着色器,用glsl语言写的(上一篇文章有glsl的基本语法)
我在这里简单解释一下,为什么这么写,可以看到main方法,跟java差不多,这是程序的入口。然后定义一个顶点变量名为aPosition,后面的代码我们会给这个变量赋值(其实就是要绘制正方形的点的坐标数据),然后和矩阵uMatrix相乘(转换成3D),赋值到内置变量gl_Position 上。而这里还有vColor和aColor,aColor是java里传进来的颜色的数据,vColor是连接片元着色器的变量,将java传来的数据传到片元着色器里边,最后赋值给内置变量gl_FragColor(这是opengl的基本套路)

//顶点
val ver="attribute vec4 aPosition;" +
        "uniform mat4 uMatrix;" +
        "varying  vec4 vColor;" +
        "attribute vec4 aColor;" +
        "void main() {" +
        "  gl_Position = uMatrix*aPosition;" +
        "  vColor=aColor;" +
        "}";
//片元
val frag = "precision mediump float;" +//片元一定要指定精度
    "varying vec4 vColor;" +
    "void main() {" +
      "  gl_FragColor = vColor;" +
    "}";

然后新建类NewGLRenderer继承Renderer,实现其三个方法
初始化顶点,加载着色器

override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
   val verArr = floatArrayOf(
                    -0.6f, 0.6f,  0.0f, //左上角坐标xyz 
                    0.6f, 0.6f, 0.0f, // 右上角
                    -0.6f, -0.6f,  0.0f,//左下角
                    0.6f, -0.6f,   0.0f)
   verBuffer = util.getFloadBuffer(verArr)
   //颜色数据
   val colorArr = floatArrayOf(
                    0f, 1f, 0f, 1f,//左上角颜色,rgba
                    0f, 1f, 0f, 1f,
                    0f, 1f, 0f, 1f,
                    1f, 0f, 0f, 1f)

    colorBuffer = util.getFloadBuffer(colorArr)
    
//加载顶点和片元着色器
val verShader = loadShader(GLES20.GL_VERTEX_SHADER, ver)
val fragShader = loadShader(GLES20.GL_FRAGMENT_SHADER, frag)
var program = GLES20.glCreateProgram()
if (program != 0) {
    GLES20.glAttachShader(program, verShader)
    GLES20.glAttachShader(program, fragShader)
    GLES20.glLinkProgram(program)
    //检查有没有加载出错
    val linkStatus = IntArray(1)
    GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0)
    if (linkStatus[0] == 0) {
        Log.e("gl_util-------", "link  program   error")
        GLES20.glDeleteProgram(program)
        program = 0;
    }
}

//得到着色器 的变量的引用
 positionHandle = GLES20.glGetAttribLocation(program, "aPosition")
 colorHandle = GLES20.glGetAttribLocation(program, "aColor")
 matrixHandle = GLES20.glGetUniformLocation(program, "uMatrix")
}

util的方法

 fun getFloadBuffer(data: FloatArray): FloatBuffer {
        val buffer = ByteBuffer.allocateDirect(data.size * 4)
        buffer.order(ByteOrder.nativeOrder())
        val floatBuffer = buffer.asFloatBuffer()
        floatBuffer.put(data)
        floatBuffer.position(0)
        return floatBuffer
    }

设置视口,和投影,因为OpenGL是3D的世界,需要把3D的景物呈现到屏幕中,则需要矩阵的变换操作,这个可以先这样写,要想深入的同学可以看这个文章

override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
    //设置视口,跟view的大小相同
    GLES20.glViewport(0, 0,width, height);
    val r = width.toFloat() / height
    //设置透视投影
    // Matrix.frustumM(projectionArray, 0,
    //        -r, r,//Left,right
    //       -1f, 1f,
    //       1f, 10f);
    //正交投影
    Matrix.orthoM(projectionArray,0,-r,r,-1f,1f,1f,10f)
    //设置相机
    Matrix.setLookAtM(cameraArray, 0,
            0f, 0f, 1f, //相机位置
            0f, 0f, 0f,//观察点
            0f, 1f, 0f);//UP向量在xyz轴上的分量
    //将两个矩阵相乘,赋值到 mMatrix中
    Matrix.multiplyMM(mMatrix,0,projectionArray,0,cameraArray,0)
}

最后是复制和绘制

 override fun onDrawFrame(gl: GL10?) {
          //清除颜色和和深度 缓存
          GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT or GLES20.GL_COLOR_BUFFER_BIT)
         //使用某套着色器程序
         GLES20.glUseProgram(program);
         GLES20.glUniformMatrix4fv(matrixHandle,1,false,mMatrix,0)
         //给画笔设置顶点数据
         GLES20.glVertexAttribPointer(positionHandle,//GLSL的顶点变量id
                  3, //xyz三个值组成
                GLES20.GL_FLOAT,//顶点类型
                 false,//(当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE)。)
                0, //连续顶点属性之间的偏移量。(如果为0,那么顶点属性会被理解为:它们是紧密排列在一起的。初始值为0。)
                verBuffer);//顶点数据
		//给画笔设置顶点颜色数据
		GLES20.glVertexAttribPointer(colorHandle,//GLSL的顶点变量id
       			 4, //RGBA三个值组成
      			  GLES20.GL_FLOAT,//顶点类型
      			  false,
      			  0, //偏移量
       			 colorBuffer);//顶点颜色数据
		//开启顶点和顶点颜色绘制
		GLES20.glEnableVertexAttribArray(positionHandle);
		GLES20.glEnableVertexAttribArray(colorHandle);
		//绘制,使用数组法绘制
		GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,//图元(绘制方式,,,三角形绘制)
        0,//从数组缓存中的哪一位开始绘制
        4);//数组的顶点数据个数
        }

这里使用了数组法(glDrawArrays)绘制,除了这个还有索引法(glDrawElements)可以绘制,我会在下一篇文章使用介绍,这里先解释先数组法的第一个参数的含义吧

 GL_POINTS       //将传入的顶点坐标作为单独的点绘制
 GL_LINES        //将传入的坐标作为单独线条绘制,ABCDEFG六个顶点,绘制AB、CD、EF三条线
 GL_LINE_STRIP   //将传入的顶点作为折线绘制,ABCD四个顶点,绘制AB、BC、CD三条线
 GL_LINE_LOOP    //将传入的顶点作为闭合折线绘制,ABCD四个顶点,绘制AB、BC、CD、DA四条线。
 GL_TRIANGLES    //将传入的顶点作为单独的三角形绘制,ABCDEF绘制ABC,DEF两个三角形
 GL_TRIANGLE_FAN    //将传入的顶点作为扇面绘制,ABCDEF绘制ABC、ACD、ADE、AEF四个三角形
 GL_TRIANGLE_STRIP   //将传入的顶点作为三角条带绘制,ABCDEF绘制ABC,BCD,CDE,DEF四个三角形

我们这里使用的是GL_TRIANGLE_STRIP,上面含义已经解释了很清楚了,这里提一下,如果是用GL_TRIANGLES来绘制的话,则需要6个顶点,因为两个三角形。大家可以试一下。(这也是我为什么要先绘制正方形的原因,要是三角形的话怎么样都是三个顶点就行了)

最後是效果图,我们最后一个顶的颜色改了,所以出现的是下面的效果

Android 之 OpenGLES(二) 绘制四边形(正方形)_第1张图片

你可能感兴趣的:(opengl,android)