能够定义要在 OpenGL ES 视图上下文中绘制的形状是为您的应用程序创建高端图形的第一步。如果不了解有关 OpenGL ES 希望您如何定义图形对象的一些基本知识,使用 OpenGL ES 进行绘图可能会有些棘手。
本课程介绍了相对于 Android 设备屏幕的 OpenGL ES 坐标系、定义形状、形状面以及定义三角形和正方形的基础知识。
定义一个三角形
OpenGL ES 允许您使用三维空间中的坐标定义绘制的对象。所以,在你画一个三角形之前,你必须定义它的坐标。在 OpenGL 中,典型的做法是为坐标定义一个浮点数的顶点数组。为了获得最大效率,您将这些坐标写入一个 ByteBuffer,然后传递到 OpenGL ES 图形管道进行处理。
// 此数组中每个顶点的坐标数
const val COORDS_PER_VERTEX = 3
var triangleCoords = floatArrayOf( // 按逆时针顺序
0.0f, 0.622008459f, 0.0f, // top
-0.5f, -0.311004243f, 0.0f, // bottom left
0.5f, -0.311004243f, 0.0f // bottom right
)
class Triangle {
// 使用红色、绿色、蓝色和 alpha(不透明度)值设置颜色
val color = floatArrayOf(0.63671875f, 0.76953125f, 0.22265625f, 1.0f)
private var vertexBuffer: FloatBuffer =
// (坐标值的数量 * 每个浮点数 4 个字节)
ByteBuffer.allocateDirect(triangleCoords.size * 4).run {
// 使用设备硬件的本机字节顺序
order(ByteOrder.nativeOrder())
// 从 ByteBuffer 创建一个浮点缓冲区
asFloatBuffer().apply {
// 将坐标添加到 FloatBuffer
put(triangleCoords)
// 设置缓冲区读取第一个坐标
position(0)
}
}
}
默认情况下,OpenGL ES 采用坐标系,其中 [0,0,0] (X,Y,Z) 指定 GLSurfaceView 框架的中心,[1,1,0] 是框架的右上角,[- 1,-1,0] 是框架的左下角。有关此坐标系的说明,请参阅 OpenGL ES 开发人员指南。
请注意,此形状的坐标是按逆时针顺序定义的。绘制顺序很重要,因为它定义了哪一侧是形状的正面(您通常希望绘制)和背面(您可以选择不使用 OpenGL ES 剔除面功能绘制)。有关面孔和剔除的更多信息,请参阅 OpenGL ES 开发人员指南。
定义一个正方形
在 OpenGL 中定义三角形非常简单,但如果您想稍微复杂一点怎么办?比如说,正方形?有多种方法可以做到这一点,但在 OpenGL ES 中绘制这种形状的典型方法是使用两个绘制在一起的三角形:
同样,您应该为表示此形状的两个三角形按逆时针顺序定义顶点,并将值放入 ByteBuffer 中。为了避免定义每个三角形共享的两个坐标两次,使用绘图列表告诉OpenGL ES 图形管道如何绘制这些顶点。这是此形状的代码:
// 此数组中每个顶点的坐标数
const val COORDS_PER_VERTEX = 3
var squareCoords = floatArrayOf(
-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
)
class Square2 {
private val drawOrder = shortArrayOf(0, 1, 2, 0, 2, 3) // 绘制顶点的顺序
// 为形状坐标初始化顶点字节缓冲区
private val vertexBuffer: FloatBuffer =
// (# 坐标值 * 每个浮点数 4 个字节)
ByteBuffer.allocateDirect(squareCoords.size * 4).run {
order(ByteOrder.nativeOrder())
asFloatBuffer().apply {
put(squareCoords)
position(0)
}
}
// 为绘图列表初始化字节缓冲区
private val drawListBuffer: ShortBuffer =
// (# 坐标值 * 每个短 2 个字节)
ByteBuffer.allocateDirect(drawOrder.size * 2).run {
order(ByteOrder.nativeOrder())
asShortBuffer().apply {
put(drawOrder)
position(0)
}
}
}
此示例让您了解如何使用 OpenGL 创建更复杂的形状。通常,您使用三角形集合来绘制对象。在下一课中,您将学习如何在屏幕上绘制这些形状。