水到渠成,何须强求?静待花开,一切都会好的。
- Android OpenGLES开发:EGL环境搭建
- Android OpenGLES2.0开发(一):艰难的开始
- Android OpenGLES2.0开发(二):环境搭建
- Android OpenGLES2.0开发(三):绘制一个三角形
- Android OpenGLES2.0开发(四):矩阵变换和相机投影
- Android OpenGLES2.0开发(五):绘制正方形和圆形
- Android OpenGLES2.0开发(六):着色器语言GLSL
- Android OpenGLES2.0开发(七):纹理贴图之显示图片
- Android OpenGLES2.0开发(八):Camera预览
- Android OpenGLES2.0开发(九):图片滤镜
- Android OpenGLES2.0开发(十):FBO离屏渲染
- Android OpenGLES2.0开发(十一):渲染YUV
我们在前面的章节已经绘制了最基本的图形三角形,这一篇我们来画一个正方形和圆形。有了前面的基础,绘制正方形和圆形实际上就是手到擒来的事。
绘制正方形和圆形
来思考下正方形如何绘制?我们知道三角形是最基本的图形,那么正方形应该就是由两个三角形组成的,答案是肯定的。那么圆形如何绘制,其实就是正多边形的绘制,从原点绘制无数个三角形得到圆形。
正方形
正方形的构建比较简单,可以用两个三角形组成。当然,你也可以用很多很多三角形去合成一个正方形,只要你乐意。如下图所示,我们可以按照123组成的三角形和134组成的三角形,两个拼合成一个正方形。
1. 顶点数据定义
我们将Triangle
类拷贝一份修改类名称为Square
,并修改顶点数据如下:
public class Square {
...
// 正方形四个顶点坐标
static float squareCoords[] = {
-0.5f, 0.5f, 0.0f, // top left
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f, // bottom right
0.5f, 0.5f, 0.0f, // top right
};
// 绘制顶点的顺序,3个数为一组
private short drawOrder[] = {0, 1, 2, 0, 2, 3};
private ShortBuffer mDrawIndexBuffer;
...
public Square() {
// 初始化形状坐标的顶点字节缓冲区
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
mVertexBuffer = bb.asFloatBuffer();
mVertexBuffer.put(squareCoords);
mVertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
mDrawIndexBuffer = dlb.asShortBuffer();
mDrawIndexBuffer.put(drawOrder);
mDrawIndexBuffer.position(0);
}
}
2. 绘制正方形
我们只需要修改GLES20.glDrawArrays
方法改为GLES20.glDrawElements
通过索引绘制即可,将顶点绘制的索引数据传入
// 索引法绘制
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, mDrawIndexBuffer);
圆形
圆形的构建,相对复杂一点,我们可以把圆形看成一个正多边形,边越多,圆越平滑。如下图所示,分别为正六边形、正八边形、正十六边形和正一百边形。
private float[] createPositions(float radius, int n) {
ArrayList<Float> data = new ArrayList<>();
data.add(0.0f); //设置圆心坐标
data.add(0.0f);
data.add(0.0f);
float angDegSpan = 360f / n;
for (float i = 0; i < 360 + angDegSpan; i += angDegSpan) {
data.add((float) (radius * Math.sin(i * Math.PI / 180f)));
data.add((float) (radius * Math.cos(i * Math.PI / 180f)));
data.add(0.0f);
}
float[] f = new float[data.size()];
for (int i = 0; i < f.length; i++) {
f[i] = data.get(i);
}
return f;
}
1. 定义顶点数据
我们将Triangle
类拷贝一份修改类名称为Circle
,并修改顶点数据如下:
public class Circle {
private float circleCoords[];
public Circle() {
circleCoords = createPositions(0.5f, 60);
vertexCount = circleCoords.length / COORDS_PER_VERTEX;
// 初始化形状坐标的顶点字节缓冲区
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
circleCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(circleCoords);
vertexBuffer.position(0);
}
}
2. 绘制圆形
现在我们要绘制圆形了,但是我们发现没有定义绘制的顺序。我们要定义几十个三角形的绘制顺序工作量无疑是巨大的,有没有更方便的方式?
GLES20.glDrawArrays
方法帮我们提供了多种绘制的方式,第一个参数表示绘制方式,第二个参数表示偏移量,第三个参数表示顶点个数
绘制方式如下:
int GL_POINTS //将传入的顶点坐标作为单独的点绘制
int GL_LINES //将传入的坐标作为单独线条绘制,ABCDEFG六个顶点,绘制AB、CD、EF三条线
int GL_LINE_STRIP //将传入的顶点作为折线绘制,ABCD四个顶点,绘制AB、BC、CD三条线
int GL_LINE_LOOP //将传入的顶点作为闭合折线绘制,ABCD四个顶点,绘制AB、BC、CD、DA四条线。
int GL_TRIANGLES //将传入的顶点作为单独的三角形绘制,ABCDEF绘制ABC,DEF两个三角形
int GL_TRIANGLE_FAN //将传入的顶点作为扇面绘制,ABCDEF绘制ABC、ACD、ADE、AEF四个三角形
int GL_TRIANGLE_STRIP //将传入的顶点作为三角条带绘制,ABCDEF绘制ABC,BCD,CDE,DEF四个三角形
从上面的方式我们可以找到,绘制圆形我们采用GL_TRIANGLE_FAN
方式就可以轻松实现
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, vertexCount);
再谈绘制
GL_TRIANGLE_STRIP
GL_TRIANGLE_STRIP的方式绘制连续的三角形,比直接用GL_TRIANGLES的方式绘制三角形少好多个顶点,效率会高很多。另外,GL_TRIANGLE_STRIP并不是只能绘制连续的三角形构成的物体,我们只需要将不需要重复绘制的点重复两次即可。比如,传入ABCDEEFFGH坐标,就会得到ABC、BCD、CDE以及FGH四个三角形
GL_TRIANGLE_FAN
扇面绘制是以第一个为零点进行绘制,通常我们绘制圆形,圆锥的锥面都会使用到,值得注意的是,最后一个点的左边应当与第二个点重合,在计算的时候,起点角度为0度,终点角度应包含360度。
看到这里我们实际上绘制正方形也同样可以使用该种方式,这样就不需要传顶点索引坐标了
顶点法和索引法
上述提到的绘制,使用的是GLES20.glDrawArrays
,也就是顶点法,是根据传入的定点顺序进行绘制的。还有一个方法进行绘制GLES20.glDrawElements
,称之为索引法,是根据索引序列,在顶点序列中找到对应的顶点,并根据绘制的方式,组成相应的图元进行绘制。
顶点法拥有的绘制方式,索引法也都有。相对于顶点法在复杂图形的绘制中无法避免大量顶点重复的情况,索引法可以相对顶点法减少很多重复顶点占用的空间。所以复杂图形的情况下推荐使用索引法。
复杂图形中索引法优于顶点法?我们举个例子,如要绘制一个正方体,正方体6个面,每个面两个三角形,每个三角形三个顶点(3+3)*6=36个点,刨除重复的4*6=24也需要定义24个点。而索引法只需要定义好正方体的8个顶点,然后用定义好索引即可。
最后
本章我们实现了正方形和圆形的绘制,让我们对OpenGL ES
绘制图像有了更深的了解。正如我们在Android OpenGLES2.0开发(三):绘制一个三角形中提到的,绘制三角形是一切的基础,之后的文章都是在该基础上做一个微调。相信你通过这几篇文章已经对OpenGL ES
初窥门径,希望在未来的篇章中我们一起登堂入室。
git地址:github.com/xiaozhi003/…