音视频

Android OpenGL ES(三)-平面图形

2018-05-12  本文已影响227人  deep_sadness

上一章Android OpenGL ES(二)-正交投影
的学习,我们已经能够画正常的图片图形了,这章我们会继续来绘制正方形和圆的这样的平面图形和绘制纹理。

平面图形


前两章,其实我们已经明白了绘制平面图形的套路了。
接下来我们按照套路继续画其他的图形。

正方形

因为OpenGL只提供给我们画三角形的方式,所以想要正方形的话,其实就是画两个三角形拼在一起。

一:使用GL_TRIANGLE_STRIP的方式

1. 修改矩阵的数组。将其改成正方形
按照上图中的顺序定制我们的矩阵数组。

 private static float SQUARE_COLOR_COORDS[] = {
            //Order of coordinates: X, Y, Z, R,G,B,
            -0.5f, 0.5f, 0.0f, 1.f, 0f, 0f,  //  0.top left RED
            -0.5f, -0.5f, 0.0f, 0.f, 0f, 1f, //  1.bottom left Blue
            0.5f, 0.5f, 0.0f, 1f, 1f, 1f,   //  3.top right WHITE
            0.5f, -0.5f, 0.0f, 0.f, 1f, 0f,  //  2.bottom right GREEN
    };

2. 调用onDrawFrame内调用我们的代码

  //在OnDrawFrame中进行绘制
    @Override
    public void onDrawFrame(GL10 gl) {
        super.onDrawFrame(gl);

        //传递给着色器
        GLES20.glUniformMatrix4fv(uMatrix, 1, false, mProjectionMatrix, 0);

        //1.使用三角形带的方式
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0, VERTEX_COUNT);
    }

3. 结果

正方形.png

二:使用GL_TRIANGLES_FAN的方式

将传入的顶点作为扇面绘制,ABCDEF绘制ABC、ACD、ADE、AEF四个三角形.

1. 更新正方形的矩阵数组

//0,1,2 是一个三角形 //0,2,3 是一个三角形
private static float SQUARE_COLOR_COORDS[] = {
            //Order of coordinates: X, Y, Z, R,G,B,
            -0.5f, 0.5f, 0.0f, 1.f, 0f, 0f,  //  0.top left RED
            0.5f, 0.5f, 0.0f, 1f, 1f, 1f,   //  1.top right WHITE
            0.5f, -0.5f, 0.0f, 0.f, 1f, 0f,  //  2.bottom right GREEN
            -0.5f, -0.5f, 0.0f, 0.f, 0f, 1f, //  3.bottom left Blue
    };

2. 更新绘制的方法

GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, VERTEX_COUNT);

3. 结果

正方形2.png

三:使用GL_TRIANGLES和顶点矩阵数组加位置矩阵数组的方式

这种方法就是根据我们的数组,自己来定义绘制的顺序来,完成绘制两个三角形完成正方形的任务。

1. 更新数组的数据

  //正方形的点1
    private static float SQUARE_COLOR_COORDS[] = {
            //Order of coordinates: X, Y, Z, R,G,B,
            -0.5f, 0.5f, 0.0f, 1.f, 0f, 0f,  //  0.top left RED
            -0.5f, -0.5f, 0.0f, 0.f, 0f, 1f, //  1.bottom right Blue
            0.5f, 0.5f, 0.0f, 1f, 1f, 1f,   //  3.top right WHITE
            0.5f, -0.5f, 0.0f, 0.f, 1f, 0f,  //  2.bottom left GREEN
    };

    /*
     创建一个遍历的点的顺序.
    1,0,2,1 一个三角形
    1,2,3,1 另一个三角
    */
    private static short SQUARE_INDEX[] = {
            1 , 0, 2, 1, 2, 3
    };

2. 添加indexBuffer
我们同样需要为我们新添加的位置数组的分配内存,让OpenGL来读取。

//        /*
//        新增-为位置添加内存空间
//         */
        mIndexBuffer = ByteBuffer
                .allocateDirect(SQUARE_INDEX.length * Constant.BYTES_PER_SHORT)
                .order(ByteOrder.nativeOrder())
                .asShortBuffer()
                .put(SQUARE_INDEX);
        mIndexBuffer.position(0);

3. 修改绘制的方法

  //使用indexBuffer的方式
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, SQUARE_INDEX.length, GLES20.GL_UNSIGNED_SHORT, mIndexBuffer);

4. 结果

indexBuffer正方形.png

正方形小节

这里我们一共使用三种方式进行绘制


绘制圆形。是通过绘制切分的三角形来形成的。三角形切分的越细,越接近圆。

1.更新代表圆形的矩阵数组

按照上图和我们的切分点,计算每一个点的坐标,放到数组里面。构造出来的数组属性是X,Y,Z,R,G,B

private float[] createCircleCoords(Circle circle, int numbersRoundCircle) {
      //先计算总共需要多少个点
      int needNumber = getCircleVertexNum(numbersRoundCircle);
      //创建数组
      float[] circleColorCoord = new float[needNumber * TOTAL_COMPONENT_COUNT];
      //接下来给每个点分配数据

      //对每一组点进行赋值
      for (int numberIndex = 0; numberIndex < needNumber; numberIndex++) {
          int indexOffset = numberIndex * TOTAL_COMPONENT_COUNT;

          if (numberIndex == 0) {   //第一个点。就是圆心
              //位置
              circleColorCoord[indexOffset] = circle.center.x;
              circleColorCoord[indexOffset + 1] = circle.center.y;
              circleColorCoord[indexOffset + 2] = circle.center.z;

              //下面是颜色。给一个白色
              circleColorCoord[indexOffset + 3] = 1.f;
              circleColorCoord[indexOffset + 4] = 1.f;
              circleColorCoord[indexOffset + 5] = 1.f;
          } else if (numberIndex < needNumber - 1) {    //切分圆的点
              //需要根据半径。中心点。来结算
              int angleIndex = numberIndex - 1;
              float angleRadius = (float) (((float) angleIndex / (float) numbersRoundCircle) * Math.PI * 2f);
              float centerX = circle.center.x;
              float centerY = circle.center.y;
              float centerZ = circle.center.z;
              float radius = circle.radius;
              float tempX = (float) (centerX + radius * Math.cos(angleRadius));
              float tempY = (float) (centerY + radius * Math.sin(angleRadius));
              float temp = centerZ + 0;

              //位置

              circleColorCoord[indexOffset] = tempX;
              circleColorCoord[indexOffset + 1] = tempY;
              circleColorCoord[indexOffset + 2] = temp;

              //下面是颜色。给一个白色
              circleColorCoord[indexOffset + 3] = (float) (1.f* Math.cos(angleRadius));
              circleColorCoord[indexOffset + 4] = (float) (1.f* Math.sin(angleRadius));
              circleColorCoord[indexOffset + 5] = 1.f;
          } else { //最后一个点了。重复数据中的二组的位置
              //位置.index为1的点
              int copyTargetIndex = 1;
              //复制点
              circleColorCoord[indexOffset] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT];
              circleColorCoord[indexOffset + 1] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT + 1];
              circleColorCoord[indexOffset + 2] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT + 2];

              circleColorCoord[indexOffset + 3] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT + 3];
              circleColorCoord[indexOffset + 4] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT + 4];
              circleColorCoord[indexOffset + 5] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT + 5];
          }

      }
      return circleColorCoord;
  }

这样就更新好我们的矩阵数组了。

2. 绘制

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, VERTEX_COUNT);

3. 结果

circle.png

小节

画圆就是熟练的应用了正方形的经验。

纹理


除了平面图形。我们还能绘制我们自己的2D纹理。

OpenGL中的纹理可以用来表示图像。照片甚至数学算法生成的分形数据。
每个二维的纹理都由许多小的纹理元素(text1)组成。它们是小块的数据。

理解纹理的坐标

每个二维的纹理都有自己的坐标空间。其范围是从一个拐角(0,0)到另外一个拐角(1,1)。一个纬度叫做S,而另一个拐角叫做T.

二维纹理坐标.png

对比Android系统的Y轴
android系统中的y轴也是向下的。但是纹理坐标是向上的。

纹理的大小
在标准的OpenGL ES 2.0中,纹理不必是正方形。但是每个纬度都应该是2的幂。POT纹理适用于各种情况。
纹理也有一个最大值,但是会根据不同的实现而变化。

理解纹理过滤模式

当我们渲染表面上绘制一个纹理时,那个纹理的纹理元素可能无法精确的映射到OpenGL生成的片段上。由两种情况:缩小或者放大。通过纹理过滤(texture filtering),来控制产生的效果。

最近邻过滤
最近邻过滤.png
双线性过滤

双线性过滤会进行插值。


双线性过滤.png
MIP 贴图

可以生成一组优化过的不同大小的纹理。当生成这组纹理的时候。OpenGL会使用所有的纹理元素生成每个级别的纹理,当过滤纹理时,还要确保所有的纹理元素能被使用。在渲染时,会更具每个片段的纹理元素数量为每个片段选择最合适的级别。

三线性过滤

如果OpenGL在不同的MIP贴图级别中来回切换。当我们用双线性过滤使用MIP贴图时,再起渲染的场景中,在不同级别的切换时,就会看到明显的跳跃。我们可以切换到三线性过滤。告诉OpenGL 两个最邻近的MIP贴图级别之间也要插值。这样每个片段总共要使用8个纹理元素插值。有助于消除每个MIP贴图级别中间的过渡。得到一个更平滑的图像。

过滤模式总结

过滤模式.png

纹理绘制代码

1. 更新着色器
2. 更新代码

纹理小节

总结

总结一下,我们从这第一章节的内容了解到了下面这些使用的知识点:

  1. 绘制正方形的多种方式和绘制圆的方式。熟悉了GL的绘制方法。
  2. 纹理的基础概念
  3. 绘制一个纹理的基本套路

下一章开始,我们会进入Android的相机和OpenGL的结合。
相机部分结束之后,才会到三维图形的部分。

整体的代码位置:https://github.com/deepsadness/OpenGLDemo5

系列文章地址
Android OpenGL ES(一)-开始描绘一个平面三角形
Android OpenGL ES(二)-正交投影
Android OpenGL ES(三)-平面图形
Android OpenGL ES(四)-为平面图添加滤镜
Android OpenGL ES(五)-结合相机进行预览/录制及添加滤镜
Android OpenGL ES(六) - 将输入源换成视频
Android OpenGL ES(七) - 生成抖音照片电影

上一篇下一篇

猜你喜欢

热点阅读