OpenGL ES 绘制图元:glDrawArrays 和 glDrawElements 介绍

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/afei__/article/details/89203369

一、简介

在 OpenGL ES 2.0 中,使用 glDrawArraysglDrawElements 两个接口绘制图元。
在 OpenGL ES 3.0 中,又新增了 glDrawRangeElementsglDrawElementsInstancedglDrawArraysInstanced 三个接口用于绘制图元。

二、函数介绍

1. glDrawArrays

/**
 * @param mode 渲染的图元模式,有:GL_POINTS、GL_LINES、GL_LINE_LOOP、GL_LINE_STRIP、GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN
 * @param first 起始位置
 * @param count 顶点数量
 */
void glDrawArrays(GLenum mode, GLint first, GLsizei count);

2. glDrawElements

/**
 * @param mode 渲染的图元模式,有:GL_POINTS、GL_LINES、GL_LINE_LOOP、GL_LINE_STRIP、GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN
 * @param count 顶点数量
 * @param type 元素类型,有:GL_UNSIGNED_BYTE、GL_UNSIGNED_SHORT、GL_UNSIGNED_INT
 * @param indices 元素索引数组
 */
void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices);

3. 区别

首先,这两个函数的作用都是从一个数据数组中提取数据,然后渲染图元。

区别在于:glDrawArrays 是直接绘制真实的顶点数据,而 glDrawElements 是按照指定的索引顺序取出真实数据再绘制。

于是对于顶点存在共享的场景时,使用 glDrawElements 对于重复的顶点数据只需要传输一份数据,绘制时通过索引反复的获取其值,最终降低内存占用和内存带宽需求。

三、图元介绍

上面我们知道,渲染的时候需要指定一个渲染的图元模式,下面我们就详细介绍一下这些图元和图元模式。

1. 点精灵

对应的模式为 GL_POINTS,即在每个顶点位置绘制一个点。OpenGL ES 中绘制的点实则是一个方块,顶点位置是方块的中心点,边长在顶点着色器中由内建变量 gl_PointSize 指定。

点的尺寸大小范围可以通过如下方式获取:

GLfloat pointSizeRange[2];
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);

如果我们想自定义点的外形,通常可以使用纹理。一个使用纹理的片段着色器示例如下:

#version 300 es
precision mediump float;

uniform sampler2D u_TextureUnit;
layout(location = 0) out vec4 outColor;

void main() {
    outColor = texture2D(u_TextureUnit, gl_PointCoord);
}

这样我们在外面给 u_TextureUnit 指定纹理的 id 即可,关于纹理的相关使用后续文章再介绍。

还有上例中我们使用到了一个内建变量 gl_PointCoord,它只在绘制点精灵时可以使用,描述了这个点内部的坐标空间,其左上角为 (0, 0),右下角为 (1, 1)。

2. 直线

对应的模式为 GL_LINESGL_LINE_LOOPGL_LINE_STRIP,用指定的顶点绘制相应的线段。
在这里插入图片描述
如图,假设指定的顶点坐标为 (v0, v1, v2, v3),那么

  • GL_LINES 模式下,将绘制 (v0, v1) 和 (v2, v3) 这两条线段
  • GL_LINE_STRIP 模式下,将绘制 (v0, v1)、(v1, v2) 和 (v2, v3) 三条线段
  • GL_LINE_LOOP 模式下,将绘制 (v0, v1)、(v1, v2)、(v2, v3) 和 (v3, v0) 四条线段

线段的宽度使用如下 API 指定:

/**
 * @param width 线宽,以像素数表示,默认的宽度为 1.0
 */
void glLineWidth(GLFloat width);

指定的线宽将被 OpenGL 记住,直到由应用程序更新。

支持的线宽范围可以通过如下方式获取:

GLfloat lineWidthRange[2];
glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange);

3. 三角形

对应的模式为 GL_TRIANGLESGL_TRIANGLE_STRIPGL_TRIANGLE_FAN。三角形图元可谓是最常使用的了。
在这里插入图片描述
如图,假设指定的顶点坐标为上图所示,那么

  • GL_TRIANGLES 模式下,将绘制 (v0, v1, v2) 和 (v3, v4, v5) 这两个三角形。
  • GL_TRIANGLE_STRIP 模式下,将绘制 (v0, v1, v2)、(v2, v1, v3) (注意顺序)和 (v2, v3, v4) 三个三角形。
  • GL_TRIANGLE_FAN 模式下,将绘制 (v0, v1, v2)、(v0, v2, v3) 和 (v0, v3, v4) 三个三角形。

四、3.0 新增的绘制接口介绍

最开始说到在 OpenGL ES 3.0 中新增了几个绘制图元的接口,接下来对其简单了解一下。

1. glDrawRangeElements

/**
 * @param mode 渲染的图元模式,有:GL_POINTS、GL_LINES、GL_LINE_LOOP、GL_LINE_STRIP、GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN
 * @param start 起始索引位置
 * @param end 结束索引位置
 * @param count 顶点数量
 * @param type 元素类型,有:GL_UNSIGNED_BYTE、GL_UNSIGNED_SHORT、GL_UNSIGNED_INT
 * @param indices 元素索引数组
 */
void glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices);

相比于 glDrawElements 接口,它新增了 startend 两个参数,用来指定使用的数据数组的起始和结束位置。其余区别不大。

2. glDrawElementsInstanced 和 glDrawArraysInstanced

当需要绘制大量相似对象时(例如颜色、大小存在不同),以往我们只能向 OpenGL ES 引擎发送许多 API 调用。但是使用几何形状实例化,可以用一次 API 调用多次渲染具有不同属性的同一对象,大大降低 API 调用的 CPU 处理开销。

简单的说,如果我们想做一次批量的渲染,就可以使用 glDrawElementsInstancedglDrawArraysInstanced

函数定义:

/**
 * @param mode 渲染的图元模式,有:GL_POINTS、GL_LINES、GL_LINE_LOOP、GL_LINE_STRIP、GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN
 * @param first 起始位置
 * @param count 顶点数量
 * @param instancecount 绘制的图元实例数量
 */
void glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);

/**
 * @param mode 渲染的图元模式,有:GL_POINTS、GL_LINES、GL_LINE_LOOP、GL_LINE_STRIP、GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN
 * @param count 顶点数量
 * @param type 元素类型,有:GL_UNSIGNED_BYTE、GL_UNSIGNED_SHORT、GL_UNSIGNED_INT
 * @param indices 元素索引的指针
 * @param instancecount 绘制的图元实例数量
 */
void glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount);

相比于 glDrawArraysglDrawElements,它多出了一个 instancecount 参数,表示我们需要绘制的图元数量。例如我们想要一次批量绘制 100 个相似的图元,这个值对应就传 100。

关于多实例绘制想要详细了解的,可以参考其它博客:

博客链接:

https://www.jianshu.com/p/71b7149d9dc1
https://www.cnblogs.com/xin-lover/p/9147905.html

猜你喜欢

转载自blog.csdn.net/afei__/article/details/89203369