04、Android OpenGL学习-定义形状

165 阅读3分钟

能够定义要在 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 创建更复杂的形状。通常,您使用三角形集合来绘制对象。在下一课中,您将学习如何在屏幕上绘制这些形状。