投影とカメラビューを適用する

OpenGL ES 環境では、投影とカメラビューを使用して、物理的オブジェクトを目で見るのとよく似た方法で描画されたオブジェクトを表示できます。この物理的な表示のシミュレーションは、描画されたオブジェクト座標を数学的に変換して行います。

  • 投影 - この変換では、表示される GLSurfaceView の幅と高さに基づいて、描画オブジェクトの座標が調整されます。この計算を行わないと、OpenGL ES によって描画されるオブジェクトは、ビュー ウィンドウの不均一な比率によって偏りになります。通常、投影変換は、OpenGL ビューの比率がレンダラの onSurfaceChanged() メソッドで確定または変更されている場合にのみ計算する必要があります。OpenGL ES 投影と座標マッピングの詳細については、描画されたオブジェクトの座標のマッピングをご覧ください。
  • カメラビュー - この変換では、仮想カメラの位置に基づいて描画されたオブジェクトの座標が調整されます。OpenGL ES は実際のカメラ オブジェクトを定義するのではなく、描画されたオブジェクトの表示を変換してカメラをシミュレートするユーティリティ メソッドを用意していることに注意してください。カメラビューの変換は、GLSurfaceView の確立時に 1 回だけ計算されるか、ユーザーの操作やアプリケーションの機能に基づいて動的に変化する可能性があります。

このレッスンでは、投影とカメラビューを作成し、GLSurfaceView で描画された図形に適用する方法について説明します。

射影を定義する

投影変換のデータは、GLSurfaceView.Renderer クラスの onSurfaceChanged() メソッドで計算されます。次のサンプルコードでは、GLSurfaceView の高さと幅を取得し、それを使用して Matrix.frustumM() メソッドで射影変換 Matrix を入力しています。

Kotlin

// vPMatrix is an abbreviation for "Model View Projection Matrix"
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()

    // this projection matrix is applied to object coordinates
    // in the onDrawFrame() method
    Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
}

Java

// vPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] vPMatrix = new float[16];
private final float[] projectionMatrix = new float[16];
private final float[] viewMatrix = new float[16];

@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
    GLES20.glViewport(0, 0, width, height);

    float ratio = (float) width / height;

    // this projection matrix is applied to object coordinates
    // in the onDrawFrame() method
    Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}

このコードは投影行列 mProjectionMatrix に入力されます。これを次のセクションで説明する onDrawFrame() メソッドのカメラビュー変換と組み合わせることができます。

注: 通常、描画オブジェクトに投影変換を適用するだけでは、何も表示されません。通常は、画面に何かが表示されるようにするには、カメラビュー変換も適用する必要があります。

カメラビューの定義

レンダラの描画プロセスの一環としてカメラビュー変換を追加することで、描画されたオブジェクトを変換するプロセスを完了します。次のサンプルコードでは、Matrix.setLookAtM() メソッドを使用してカメラビュー変換を計算し、以前に計算された投影行列と結合しています。結合された変換行列は描画されたシェイプに渡されます。

Kotlin

override fun onDrawFrame(unused: GL10) {
    ...
    // Set the camera position (View matrix)
    Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, 3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)

    // Calculate the projection and view transformation
    Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0)

    // Draw shape
    triangle.draw(vPMatrix)

Java

@Override
public void onDrawFrame(GL10 unused) {
    ...
    // Set the camera position (View matrix)
    Matrix.setLookAtM(viewMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

    // Calculate the projection and view transformation
    Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0);

    // Draw shape
    triangle.draw(vPMatrix);
}

投影とカメラ変換の適用

プレビュー セクションに示されている投影とカメラビューの変換行列を組み合わせて使用するには、まず Triangle クラスで以前に定義した頂点シェーダーに行列変数を追加します。

Kotlin

class Triangle {

    private val vertexShaderCode =
            // This matrix member variable provides a hook to manipulate
            // the coordinates of the objects that use this vertex shader
            "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;" +
            "}"

    // Use to access and set the view transformation
    private var vPMatrixHandle: Int = 0

    ...
}

Java

public class Triangle {

    private final String vertexShaderCode =
        // This matrix member variable provides a hook to manipulate
        // the coordinates of the objects that use this vertex shader
        "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;" +
        "}";

    // Use to access and set the view transformation
    private int vPMatrixHandle;

    ...
}

次に、グラフィック オブジェクトの draw() メソッドを変更して、結合された変換行列を受け取ってシェイプに適用します。

Kotlin

fun draw(mvpMatrix: FloatArray) { // pass in the calculated transformation matrix

    // get handle to shape's transformation matrix
    vPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")

    // Pass the projection and view transformation to the shader
    GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0)

    // Draw the triangle
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount)

    // Disable vertex array
    GLES20.glDisableVertexAttribArray(positionHandle)
}

Java

public void draw(float[] mvpMatrix) { // pass in the calculated transformation matrix
    ...

    // get handle to shape's transformation matrix
    vPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

    // Pass the projection and view transformation to the shader
    GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0);

    // Draw the triangle
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

    // Disable vertex array
    GLES20.glDisableVertexAttribArray(positionHandle);
}

投影とカメラビューの変換を正しく計算して適用すると、グラフィック オブジェクトは正しい比率で描画され、次のようになります。

図 1. 投影とカメラビューが適用された三角形の描画。

シェイプを正しい比率で表示するアプリケーションが完成しました。次に、シェイプにモーションを追加します。