06、Android OpenGL学习-应用投影和相机视图

121 阅读3分钟

在 OpenGL ES 环境中,投影和相机视图允许您以更类似于您用眼睛看到物理对象的方式显示绘制的对象。这种物理观察的模拟是通过绘制对象坐标的数学变换完成的:

  • 投影 - 此转换根据显示对象的 GLSurfaceView 的宽度和高度调整绘制对象的坐标。如果没有这种计算,OpenGL ES 绘制的对象会因视图窗口的不等比例而倾斜。通常仅当在渲染器的 onSurfaceChanged() 方法中建立或更改 OpenGL 视图的比例时才需要计算投影变换。有关 OpenGL ES 投影和坐标映射的更多信息,请参阅绘制对象的映射坐标
  • 相机视图 - 此转换根据虚拟相机位置调整绘制对象的坐标。请务必注意,OpenGL ES 并未定义实际的相机对象,而是提供了通过转换绘制对象的显示来模拟相机的实用方法。当您建立 GLSurfaceView 时,相机视图转换可能只计算一次,或者可能会根据用户操作或应用程序的功能动态更改。

本课介绍如何创建投影和相机视图并将其应用于 GLSurfaceView 中绘制的形状。

定义投影

投影变换的数据是在 GLSurfaceView.Renderer 类的 onSurfaceChanged() 方法中计算的。以下示例代码采用 GLSurfaceView 的高度和宽度,并使用 Matrix.frustumM() 方法使用它来填充投影变换矩阵:

// vPMatrix 是“模型视图投影矩阵”的缩写
private val vPMatrix = FloatArray(16)
private val projectionMatrix = FloatArray(16)
private val viewMatrix = FloatArray(16)

override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
    GLES20.glViewport(0, 0, width, height)

    val ratio: Float = width.toFloat() / height.toFloat()

    // 此投影矩阵应用于 onDrawFrame() 方法中的对象坐标
    Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
}

此代码填充投影矩阵 mProjectionMatrix,然后您可以将其与 onDrawFrame() 方法中的相机视图转换结合使用,这将在下一节中显示。

注意:仅对绘图对象应用投影变换通常会导致非常空的显示。通常,您还必须应用相机视图转换才能让任何内容显示在屏幕上。

定义相机视图

通过在渲染器中添加相机视图转换作为绘制过程的一部分来完成转换绘制对象的过程。在下面的示例代码中,相机视图变换是使用 Matrix.setLookAtM() 方法计算的,然后与之前计算的投影矩阵相结合。然后将组合的变换矩阵传递给绘制的形状。

override fun onDrawFrame(unused: GL10) {
    ...
    // 设置相机位置(矩阵)
    Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, 3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)

    // 计算投影和视图变换
    Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0)

    // 绘制形状
    triangle.draw(vPMatrix)

应用投影和相机变换

为了使用预览部分中显示的组合投影和相机视图变换矩阵,首先将矩阵变量添加到先前在 Triangle 类中定义的顶点着色器:

class Triangle {

    private val vertexShaderCode =
            // 这个矩阵成员变量提供了一个钩子来操作使用这个顶点着色器的对象的坐标
            "uniform mat4 uMVPMatrix;" +
            "attribute vec4 vPosition;" +
            "void main() {" +
            // the matrix must be included as a modifier of gl_Position
            // Note that the uMVPMatrix factor *must be first* in order
            // for the matrix multiplication product to be correct.
            "  gl_Position = uMVPMatrix * vPosition;" +
            "}"

    // 用于访问和设置视图转换
    private var vPMatrixHandle: Int = 0

    ...
}

接下来,修改图形对象的 draw() 方法以接受组合变换矩阵并将其应用于形状:

fun draw(mvpMatrix: FloatArray) { // 传入计算出的变换矩阵

    // 获取形状变换矩阵的句柄
    vPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")

    // 将投影和视图变换传递给着色器
    GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0)

    // 画三角形
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount)

    // 禁用顶点数组
    GLES20.glDisableVertexAttribArray(positionHandle)
}

一旦您正确计算并应用了投影和相机视图变换,您的图形对象就会以正确的比例绘制,并且应该如下所示:

现在您已经有了一个可以按正确比例显示您的形状的应用程序,是时候为您的形状添加动作了。