一、前期基础知识储备
笔者计划写三篇文章来详细分析OpenGL ES基础的同时也是入门关键的三个点:

①OpenGL ES是什么?与OpenGL的关系是什么?——概念部分

②使用OpenGLES绘制2D/3D图形的第一步:定义图形;——运用部分

③使用OpenGLES绘制出②步骤中定义好的图形:——运用部分,难点所在

通过这三篇文章的分析,就像给万丈高楼垫定了基石,万丈高楼平地起,后面利用OpenGLES做各种效果,各种变换都是建立在这三步的图形编程理解之上的。

今天开始第三节——绘制图形部分的分析,重难点所在!

在前面的两篇文章《在Android中使用OpenGL ES进行开发第(一)课:概念先行》《在Android中使用OpenGL ES进行开发第(二)课:定义图形》中,笔者详细分析了OpenGL ES2.0相关的重要概念和实现了一个三角形的顶点坐标的定义,那么接下来,本节文章就来讲第二篇文章中定义好的图形具体绘制出来,这也是笔者这三篇文章的重难点所在。

正如第一篇文章中提及的,使用OpenGL ES2.0的时候,尽管写最简单的程式(如基本作图、三角形、矩形或动作translate、rotate、scale等),一定要写shader(着色器/渲染器)才能运作,原本这些在OpenGL ES1.x时是系统做的事,现在要人力进行实现,因而提高了开发的难度。

二、上代码,具体实现
第一步:渲染器类中初始化图形—简单;

public class MyGLRenderer2 implements GLSurfaceView.Renderer {

...
private Triangle mTriangle;

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// initialize a triangle
mTriangle = new Triangle();
}
...
}
第二步:为draw()方法做准备—创建着色器对象(shader),放入程式(Program)中,将程式链接至GLES;

着色器相关概念(重点):我们在第一篇文章中花了大量笔墨来介绍着色器(shader),那么着色器到底是什么?

①顶点着色器(Vertex Shader):用来渲染图形顶点的 OpenGL ES 代码(glsl语言编写);生成每个顶点最终位置,针对每个顶点都会执行一次;一旦最终位置确定了,OpenGL就可以把这些可见顶点的集合组装成点、直线和三角形;

②片元着色器(Fragment Shader):使用颜色或纹理(texture)渲染图形表面的 OpenGL ES 代码(glsl语言编写);为组成点、直线和三角形的每个片元生成最终颜色/纹理,针对每个片元都会执行一次;一个片元是一个小的、单一颜色的长方形区域,类似于计算机屏幕上的一个像素;

(一旦最终颜色生成,OpenGL就会把它们写到一块称为帧缓冲区的内存块中,然后Android就会把这个帧缓冲区显示在屏幕上)

③程式(Program):一个OpenGL ES 对象,包含了你希望用来绘制图形所要用到的着色器,最后顶点着色器和片元着色器都要放入到程式中,然后才能使用;简单来说就是将两个着色器变为一个对象。

以上三个,你需要至少一个顶点着色器(Vertex Shader)来定义一个图形顶点,以及一个片元着色器(Fragment Shader)为该图形上色。这些着色器必须被编译然后再添加到一个OpenGLES Program当中,并利用这个 progrem 来绘制形状。

这段代码在原本的OpenGL ES1.X中是由系统实现的,所以写起来1.x的代码比较简单,但是在2.x中我们采用人力的方式实现,虽然有些复杂,但是,通过编写顶点及片元着色器程序,来完成一些顶点变换和纹理颜色计算工作,可以实现更加灵活、精细化的计算与渲染。

熟悉着色器概念之后,那么我们代码具体实现分三步走:

①图形类中,创建两个GLSL代码段——顶点着色器代码段+片元着色器代码;

OpenGL ES实现3D绘图和普通的2D绘图即view利用canvas来绘制不一样,OpenGL需要加载GLSL程式,让GPU进行绘制。所以需要定义shader代码,并在初始化的时候加载。

public class Triangle {

/**
* 顶点着色器代码
* attribute变量(属性变量)只能用于顶点着色器中
* uniforms变量(一致变量)用来将数据值从应用程其序传递到顶点着色器或者片元着色器。 。
* varying变量(易变变量)是从顶点着色器传递到片元着色器的数据变量。
* gl_Position (必须)为内建变量,表示变换后点的空间位置。
*/
private final String vertexShaderCode =
"attribute vec4 vPosition;" + // 应用程序传入顶点着色器的顶点位置
"void main() {" +
" gl_Position = vPosition;" + // 设置此次绘制此顶点位置
"}";

/**
* 片元着色器代码
*/
private final String fragmentShaderCode =
"precision mediump float;" + // 设置工作精度
"uniform vec4 vColor;" + // 应用程序传入着色器的颜色变量
"void main() {" +
" gl_FragColor = vColor;" + // 颜色值传给 gl_FragColor内建变量,完成片元的着色
"}";
...
}
OpenGL最本质的概念之一就是着色器,它是图形硬件设备所执行的一类特殊函数。理解着色器最好的办法就是把它看做是专为图形处理单元(即GPU)编译的一种小型程序。OpenGL在其内部包含了所有的编译器工具,可以直接从着色器源码创建GPU所需要的编译代码并执行。

注:任何一种OpenGL程序本质上都可以被分为两部分:CPU端运行的部分,采用C++、Java之类的语言编写;以及GPU端运行的部分,使用GLSL语言编写。

更多着色器代码解析内容,感兴趣的读者可以参考1文章、2文章、3文章

②渲染器类中,创建辅助方法,用于编译①中创建的两个代码段。

该辅助方法中,我们传入两个参数,第一个参数是着色器的类型,包含两个顶点着色器(GLES20.GL_VERTEX_SHADER)和片元着色器(GLES20.GL_FRAGMENT_SHADER),第二个参数就是①中定义好的两个着色器代码段。

public class MyGLRenderer2 implements GLSurfaceView.Renderer
...

/**
* 加载并编译着色器代码
* 渲染器类型type={GLES20.GL_VERTEX_SHADER, GLES20.GL_FRAGMENT_SHADER}
* 渲染器代码 GLSL
*/
public static int loadShader(int type, String shaderCode){

int shader = GLES20.glCreateShader(type);
//加载shader代码 glShaderSource 和 glCompileShader
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);

return shader;
}
}
调用glCreateShader()方法-传入渲染器类型参数type,创建对应的着色器对象;

调用glShaderSource()方法-传入着色器对象和字符串shaderCode定义的源代码,将二者关联起来;

调用glCompileShader()方法-传入着色器对象,对其进行编译。

③图形类中,我们传入具体的参数到②中定义的方法里面,得到两个着色器的对象,然后将两个着色器对象放入到程式(Program)中。最后将程式与GLES链接好。

public class Triangle() {
...

private final int mProgram;

public Triangle() {
...

// 加载编译顶点渲染器
int vertexShader = MyGLRenderer2.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);

// 加载编译片元渲染器
int fragmentShader = MyGLRenderer2.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);

// 创建空的程式 - create empty OpenGL ES Program
mProgram = GLES20.glCreateProgram();

// attach shader 代码 - add the vertex shader to program
GLES20.glAttachShader(mProgram, vertexShader);

// attach shader 代码 - add the fragment shader to program
GLES20.glAttachShader(mProgram, fragmentShader);

// 链接GLSL程式 - creates OpenGL ES program executables
GLES20.glLinkProgram(mProgram);
}
}
做完以上两个步骤,就为图形类中定义draw()方法做好了准备—draw()方法真是千呼万唤始出来啊!下一步中,我们将开始做最后的拼接,并把数据链接到OpenGL中。

第三步:图形类中创建draw()方法,拿到链接至GLES的程式(Program),设置形状的顶点位置和表面的颜色值;

下面的代码为形状的顶点着色器和形状着色器设置了位置和颜色值,然后执行绘制函数:

public class Triangle {

// 绘制形状的顶点数量
private static final int COORDS_PER_VERTEX = 3;

...

private int mPositionHandle; //变量 用于存取attribute修饰的变量的位置编号
private int mColorHandle; //变量 用于存取uniform修饰的变量的位置编号

private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

public void draw() {
// 使用GLSL程式 - Add program to OpenGL ES environment
GLES20.glUseProgram(mProgram);

// 获取shader代码中的变量索引 get handle to vertex shader's vPosition member
// Java代码中需要获取shader代码中定义的变量索引,用于在后面的绘制代码中进行赋值
// 变量索引在GLSL程式生命周期内(链接之后和销毁之前)都是固定的,只需获取一次
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

// 绑定vertex坐标值 调用glVertexAttribPointer()告诉OpenGL,它可以在
// 缓冲区vertexBuffer中获取vPosition的数据
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);

// 启用vertex Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);

// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);

// 通过 GLES20.glDrawArrays 或者 GLES20.glDrawElements 开始绘制
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
draw()方法里面的代码是不是也非常复杂,没关系,我们捋一捋就可以看清楚,整段代码实际上可以分为三部分:

①1行代码-在draw()方法中拿到链接至GLES中的程式(Program);

②3行代码-从程式中取出顶点着色器,开始对顶点的位置进行设置;

③3行代码-从程式中取出片元着色器,开始对图形表面颜色进行设置。

第四步:渲染器类中调用图形类中定义好的draw()方法,进行具体绘制

public class MyGLRenderer2 implements GLSurfaceView.Renderer {

@Override
public void onDrawFrame(GL10 gl) {
...
mTriangle.draw();
}
}
总结:到此为止,笔者的OpenGL ES2.0的入门三课就已经结束了,覆盖面是有的,但是并没有仔细深入,其实原本打算也是通过这三篇文章来普及一下基础的OpenGL ES2.0用法,后续还会继续写提高篇的文章。(写着写着,发现想写的还有很多,之后会再加上)

在Android中使用OpenGL ES进行开发第(三)节:绘制图形的更多相关文章

  1. 在Android中使用OpenGL ES进行开发第(二)节:定义图形

    一.前期基础知识储备笔者计划写三篇文章来详细分析OpenGL ES基础的同时也是入门关键的三个点: ①OpenGL ES是什么?与OpenGL的关系是什么?——概念部分 ②使用OpenGLES绘制2D ...

  2. 在Android中使用OpenGL ES进行开发第(一)节:概念先行

    一.前期基础是知识储备笔者计划写三篇文章来详细分析OpenGL ES基础的同时也是入门关键的三个点: ①OpenGL ES是什么?与OpenGL的关系是什么?——概念部分 ②使用OpenGL ES绘制 ...

  3. 在Android中使用OpenGL ES开发第(五)节:GLSL基础语法

    一.前期基础储备笔者之前的四篇文综述了Android中使用OpenGL ES绘制基本图形和实现了简单的相机预览,初次接触OpenGL ES开发的读者可能对其中新的概念比较迷惑,尤其是其中的顶点着色器( ...

  4. 在Android中使用OpenGL ES开发第(四)节:相机预览

    笔者之前写了三篇Android中使用OpenGL ES入门级的文章,从OpenGL ES的相关概念出发,分析了利用OpenGL ES实现3D绘图的重要的两个步骤:定义形状和绘制形状,简单的绘制了一个三 ...

  5. 如何使用Android中的OpenGL ES媒体效果

    引自:http://www.2cto.com/kf/201506/404366.html Android的媒体效果框架允许开发者可以很容易的应用多种令人印象深刻的视觉效果到照片或视频之上.作为这个媒体 ...

  6. Android 中建立一个OpenGL ES的开发环境

    转自: http://wiki.eoe.cn/page/Building_an_OpenGL_ES_Environment.html 负责人:zhangql原文链接:http://docs.eoean ...

  7. OpenGL ES应用开发实践指南:iOS卷

    <OpenGL ES应用开发实践指南:iOS卷> 基本信息 原书名:Learning OpenGL ES for iOS:A Hands-On Guide to Modern 3D Gra ...

  8. OpenGL ES学习笔记(三)——纹理

    首先申明下,本文为笔者学习<OpenGL ES应用开发实践指南(Android卷)>的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载. <OpenGL ES学习笔记( ...

  9. EGL接口介绍-----Android OpenGL ES底层开发

    引自:http://www.cnitblog.com/zouzheng/archive/2011/05/30/74326.html EGL 是 OpenGL ES 和底层 Native 平台视窗系统之 ...

随机推荐

  1. mybatis相关知识积累

    mybatis Statement Statement对象用于将 SQL 语句发送到数据库中. 实际上有三种 Statement 对象,它们都作为在给定连接上执行 SQL语句的包容器: Stateme ...

  2. archive_lag_target参数

    需求,由于一套生产环境归档日志切换频率过低,建议修改参数,使其间隔一定时间周期自动切换生成归档日志; SQL>; THREAD# SEQUENCE# TO_CHAR(COMPLETION_TIM ...

  3. iframe滚动条充当浏览器滚动条

    在做博客项目的时候,使用了iframe,方便根据选择的文章类别切换显示的内容,但是文章一般都有很多,通过bootstrap的媒体列表的方式显示的话,iframe是一定会出现滚动条的,特别是我添加了一个 ...

  4. 初试Maven

    1 [检查]确认已经安装jdk,已经环境变量中配置JAVA_HOME,已经修改Path 2 [下载]从http://maven.apache.org/download.cgi下载所需要的版本,笔者使用 ...

  5. vue之双向绑定

    Vue的一大核心是双向绑定,在2.0中采用数据劫持,用Object.defineProperty实现,但作者已声明在3.0中会采用proxy实现   Object.defineProperty是什么? ...

  6. 【leetcode】566. Reshape the Matrix

    原题 In MATLAB, there is a very useful function called 'reshape', which can reshape a matrix into a ne ...

  7. 爬虫如何发现更多的url呢,怎么动态收集新的url连接

    大家在做爬虫采集数据的时候很多都会遇到增量采集的问题,有些时候是通过过滤url来进行的,有些是通过爬取网页后再进行分析判断, 以上这些过程也许大部分做爬虫的都会这么做,各位有没有想过, 除了以上的常用 ...

  8. 7.MapReduce操作Hbase

    7 HBase的MapReduce   HBase中Table和Region的关系,有些类似HDFS中File和Block的关系.由于HBase提供了配套的与MapReduce进行交互的API如 Ta ...

  9. python3 虚拟环境

    一.python中的虚拟环境 1.虚拟环境:局部的,独立的python环境,完全模拟系统全局python环境的使用 二.安装 http://virtualenv.pypa.io/en/latest/u ...

  10. mysql workbench使用技巧,使用workbench导出部分表

    最近在刚开始用workbench导出数据的时候,需要导出部分表数据,找来半天找不到,原来是选中库之后,不要要点右边的字母,然后表才显示出来 点左边的对勾的话,右边的表是不会显示出来的!