OpenGL 绘制简单三角形

OpenGL 绘制简单三角形

前段时间比较忙,研究别的东西去了,最近才想起来,关于OpenGL的实际运用还没有写,现在补回来。

准备

但凡在Android环境下使用OpenGL,首先需要一个GLSurfaceView实例。接下来大致分为两步:

  • 设置OpenGL版本号,通常使用2.0兼容性最好,具体版本区别自行百度。

    glSurfaceView.setEGLContextClientVersion(2);
  • 设置Renderer

    • 顾名思义Renderer为渲染器,其本质是一个接口,具有三个方法分别是onSurfaceCreatedonSurfaceChangedonDrawFrame。所有的OpenGL渲染显示的相关操作都需要在这几个方法中进行,也就是整个渲染流程的主体的。
    • 渲染根据刷新逻辑分为两种模式,通过glSurfaceView.setRenderMode()设置,分别是RENDERMODE_WHEN_DIRTYRENDERMODE_CONTINUOUSLY。前者为提醒刷新,后者为不断刷新,这个很好理解,简单来说OpenGL渲染是通过不断调用onDrawFrame方法进行刷新的,当为RENDERMODE_WHEN_DIRTY模式时需要手动调用glSurfaceView.requestRender()时才会进行下一次的刷新,而另一种则会不断的自动刷新。考虑到性能,通常会选择第一种刷新方式。

绘制

这里根据Renderer接口的三个方法,将绘制分为三步进行。

onSurfaceCreated

该方法会在OpenGL准备完成之后被调用,只会被调用一次,因此绘制需要的参数都需要在这里进行初始化。将OpenGL的渲染比作画画,onSurfaceCreated为准备画纸,接下来就需要对画面进行定点描边,然后准备好画的颜色。

  • 定点描边

    任何图形都是由顶点连接而成的,在OpenGL中也是一样,首先需要确定顶点的位置,因为其坐标轴为笛卡尔坐标并且极值为1,因此以浮点类型float进行存放。

    float triangleVertex[] = {
              0f, 0.5f, 0.0f, // top
              -0.5f, -0.25f, 0.0f, // bottom left
              0.5f, -0.25f, 0.0f  // bottom right };

    坐标确定之后,为了能够传给OpenGL使用,需要将其装换成底层能识别的Buffer对象。

                  //申请空间 因为一个float占4个字节
                  ByteBuffer bb = ByteBuffer.allocateDirect(triangleVertex.length * 4);
                  //转换本地内存可以识别的数据类型
                  bb.order(ByteOrder.nativeOrder());
                  //将坐标数据转换为FloatBuffer,用以传入给OpenGL
                  mVertexBuffer = bb.asFloatBuffer();
                  mVertexBuffer.put(triangleVertex);
                  mVertexBuffer.position(0);
  • 准备颜料

    当轮廓已经确定好了,接下来就是对其进行上色以及填充了,这里就要引入着色器。对着色器不懂的可以去看OpenGL的基本介绍或者看我之前的几篇相关博客。着色器分为顶点着色器片元着色器,在编写好着色器语言后创建并拿到对应的着色器句柄。

      public static int uLoadShader(int shaderType, String source) {
          //分配Shader句柄
          int shader = GLES20.glCreateShader(shaderType);
          if (0 != shader) {
              //为Shader绑定源码
              GLES20.glShaderSource(shader, source);
              //编译
              GLES20.glCompileShader(shader);
              int[] compiled = new int[1];
              //获得Shader的编译情况
              GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
              if (compiled[0] == 0) {
                  glError(1, "Could not compile shader:" + shaderType);
                  glError(1, "GLES20 Error:" + GLES20.glGetShaderInfoLog(shader));
                  GLES20.glDeleteShader(shader);
                  shader = 0;
              }
          }
          return shader;
      }
      //创建Shader
      int vertexShader = uLoadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
      int fragmentShader = uLoadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

    最后创建一个空的Program对象,并将着色器设置给该对象。

      //创建一个空的Program
      mProgram = GLES20.glCreateProgram();
      //将着色器设置给程序
      GLES20.glAttachShader(mProgram, vertexShader);
      GLES20.glAttachShader(mProgram, fragmentShader);
      //连接到程序
      GLES20.glLinkProgram(mProgram);

onSurfaceChanged

这个方法相对来说比较简单,在视屏尺寸改变时会被调用,通常用来更新和设置显示尺寸就行了。

      public void onSurfaceChanged(GL10 gl10, int width, int height) {
      //设置视图大小
      GLES20.glViewport(0, 0, width, height);
      }

onDrawFrame

类似View的onDraw方法,具体的绘制就在这里面进行啦。由于该方法负责刷新动图,因此第一步通常是先清屏,将上次画的清除掉,然后使用onSurfaceCreated准备好的参数进行具体的绘画。

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        //将程序加入到OpenGLES2.0环境
        GLES20.glUseProgram(mProgram);

        //获取顶点着色器的vPosition成员句柄
        int mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        //获取片元着色器的vColor成员的句柄
        int mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

        //启用三角形顶点的句柄
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //准备三角形的坐标数据
        GLES20.glVertexAttribPointer(mPositionHandle, COORS_PER_VERTEX,
        GLES20.GL_FLOAT, false,
        vertexStride, mVertexBuffer);

        //设置绘制三角形的颜色
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);
        //绘制三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
        //禁止顶点数组的句柄
        GLES20.glDisableVertexAttribArray(mPositionHandle);

注释我已经写的很详细了,这里可能有人不懂既然着色器已经是颜料了,那为什么代码里还会有设置颜料的步骤。那是因为着色器其实一种类似C的编程语言,里面也有自己的常量和变量,这里设置的颜色其实也就是设置我们定义的着色器语言里面所定义的一个颜色变量。有兴趣的可以看我写的GLSL相关文章。以下是具体的着色器语言:

    private String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "void main() {" +
                    "  gl_Position = vPosition;" +
                    "}";

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

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

结语

以上就是绘制一个简单图形的具体流程,都是根据本人的切身体会进行分析阐述的,如果有说错的地方还望指教。

猜你喜欢

转载自blog.csdn.net/ccw0054/article/details/78606184