OpenGL 学习系列——基础的绘制流程

发表于:2018-5-08 17:13

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:glumes    来源:51Testing软件测试网采编

  创建 OpenGL 程序和着色器链接
  接下来就是创建 OpenGL 程序并加着色器加进来。
    public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
   // 创建 OpenGL 程序 ID
          final int programObjectId = glCreateProgram();
          if (programObjectId == 0) {
              return 0;
          }
          // 链接上 顶点着色器
          glAttachShader(programObjectId, vertexShaderId);
          // 链接上 片段着色器
          glAttachShader(programObjectId, fragmentShaderId);
          // 链接着色器之后,链接 OpenGL 程序
          glLinkProgram(programObjectId);
          final int[] linkStatus = new int[1];
          // 验证链接结果是否失败
          glGetProgramiv(programObjectId, GL_LINK_STATUS, linkStatus, 0);
          if (linkStatus[0] == 0) {
           // 失败则删除 OpenGL 程序
              glDeleteProgram(programObjectId);
              return 0;
          }
          return programObjectId;
      }
  首先通过glCreateProgram程序创建 OpenGL 程序,然后通过glAttachShader将着色器程序 ID 添加上 OpenGL 程序,接下来通过glLinkProgram链接 OpenGL 程序,最后通过glGetProgramiv来验证链接是否失败。
  验证 OpenGL 程序
  链接了 OpenGL 程序后,就是验证 OpenGL 是否可用。
   public static boolean validateProgram(int programObjectId) {
          glValidateProgram(programObjectId);
          final int[] validateStatus = new int[1];
          glGetProgramiv(programObjectId, GL_VALIDATE_STATUS, validateStatus, 0);
          return validateStatus[0] != 0;
      }
  通过glValidateProgram函数验证,并再次通过glGetProgramiv函数验证是否失败。
  确定使用 OpenGL 程序
  当一切完成后,就是确定使用该 OpenGL 程序了。
  // 创建 OpenGL 程序过程
   public static int buildProgram(Context context, int vertexShaderSource, int fragmentShaderSource) {
          int program;
          int vertexShader = compileVertexShader(
                  TextResourceReader.readTextFileFromResource(context, vertexShaderSource));
          int fragmentShader = compleFragmentShader(
                  TextResourceReader.readTextFileFromResource(context, fragmentShaderSource));
          program = linkProgram(vertexShader, fragmentShader);
          validateProgram(program);
          return program;
      }
  // 创建完毕后,确定使用
      mProgram = ShaderHelper.buildProgram(context, R.raw.point_vertex_shader
                  , R.raw.point_fragment_shader);
          glUseProgram(mProgram);

  将上述的过程合并起来,buildProgram函数返回的 OpenGL 程序 ID 即可,通过glUseProgram函数表示使用该 OpenGL 程序。
  绘制
  完成了 OpenGL 程序的编译,就是最后的绘制了,再回到渲染器 Renderer里面。
  public class PointRenderer extends BaseRenderer {
      private Point mPoint;
      public PointRenderer(Context mContext) {
          super(mContext);
      }
      @Override
      public void onSurfaceCreated(GL10 gl, EGLConfig config) {
          super.onSurfaceCreated(gl, config);
          glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
          // 在 onSurfaceCreated 里面初始化,否则会报线程错误
          mPoint = new Point(mContext);
          // 绑定相应的顶点数据
          mPoint.bindData();
      }
      
      @Override
      public void onSurfaceChanged(GL10 gl, int width, int height) {
          // 确定视口大小
          glViewport(0, 0, width, height);
      }
      @Override
      public void onDrawFrame(GL10 gl) {
          // 清屏
          glClear(GL_COLOR_BUFFER_BIT);
          // 绘制
          mPoint.draw();
      }
  }
  在onSurfaceCreated函数里面做初始化工作,绑定数据等;在onSurfaceChanged方法里面确定视图大小,在onDrawFrame里面执行绘制。
  为了简化渲染流程,把所有的操作都放在放在要渲染的对象里面去了,声明一个 Point 对象,代表要绘制的点。
  public class Point extends BaseShape {
      // 着色器中定义的变量,在 Java 层绑定并赋值
      private static final String U_COLOR = "u_Color";
      private static final String A_POSITION = "a_Position";
      private int aColorLocation;
      private int aPositionLocation;
      
      float[] pointVertex = {
              0f, 0f
      };
      public Point(Context context) {
          super(context);
          mProgram = ShaderHelper.buildProgram(context, R.raw.point_vertex_shader
                  , R.raw.point_fragment_shader);
          glUseProgram(mProgram);
          vertexArray = new VertexArray(pointVertex);
          POSITION_COMPONENT_COUNT = 2;
      }
      @Override
      public void bindData() {
          //绑定值
          aColorLocation = glGetUniformLocation(mProgram, U_COLOR);
          aPositionLocation = glGetAttribLocation(mProgram, A_POSITION);
          // 给绑定的值赋值,也就是从顶点数据那里开始读取,每次读取间隔是多少
          vertexArray.setVertexAttribPointer(0, aPositionLocation, POSITION_COMPONENT_COUNT,
                  0);
      }
      @Override
      public void draw() {
          // 给绑定的值赋值
          glUniform4f(aColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);
          glDrawArrays(GL_POINTS, 0, 1);
      }
  }
  在 Point 的构造函数中,编译并使用 OpenGL 程序,而在 bindData函数中,通过glGetUniformLocation和glGetAttribLocation函数绑定了我们在 OpenGL 中声明的变量u_Color和a_Position,要注意的是,attribute类型和uniform类型所对应的方法是不同的,最后通过给POSITION_COMPONENT_COUNT变量赋值,指定每个顶点数据的个数为 2 。
  绑定了变量之后,接下来就是给他们赋值了,对于uniform类型变量,由于是固定值,所以直接调用glUniform4f方法给其赋值就好了,而attribute类型变量,则需要对应顶点数据中的值了,vertexArray.setVertexAttribPointer方法就是完成这个任务的。
   // 给某个顶点数据绑定值,并 Enable 使能
      public void setVertexAttribPointer(int dataOffset, int attributeLocation, int componentCount, int stride) {
          floatBuffer.position(dataOffset);
          glVertexAttribPointer(attributeLocation, componentCount, GL_FLOAT, false, stride, floatBuffer);
          glEnableVertexAttribArray(attributeLocation);
          floatBuffer.position(0);
      }
  在setVertexAttribPointer方法中,使用了glVertexAttribPointer方法来绑定值,它的参数释义如下所示:
  通过glEnableVertexAttribArray方法来开启使用即可。
  最后通过glDrawArrays方法来执行最后的绘制,GL_POINTS代表绘制的类型,而参数0,1则代表绘制的点的范围,它是一个左闭右开的区间。
  以上步骤就完成了一个点的绘制,如图所示:
  具体代码详情,可以参考我的 Github 项目:
  github.com/glumes/Andr…
  小结
  使用 OpenGL 进行绘制的原理,也就是按照 GPU 的渲染管线流程,提供了顶点数据之后,执行顶点着色器,然后执行片段着色器,最后映射到手机屏幕上。
  而作为可编程的阶段,我们就是在顶点着色器和片段着色器中做我们想要的处理,编写了着色器代码之后,通过编译链接成 OpenGL 程序。然后给 OpenGL 中设定的变量绑定对应的值,从顶点数据何处开始读取值。到这里,一切准备工作就做完了。
  最后就在在渲染器 Renderer 中开始绘制了。



22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号