Menerapkan tampilan kamera dan proyeksi

Dalam lingkungan OpenGL ES, proyeksi dan tampilan kamera memungkinkan Anda untuk menampilkan objek yang digambar dengan cara yang lebih menyerupai tampilan objek fisik yang dilihat dengan mata Anda. Simulasi tampilan fisik ini dilakukan dengan transformasi matematis dari koordinat objek yang digambar:

  • Proyeksi - Transformasi ini menyesuaikan koordinat objek yang digambar berdasarkan lebar dan tinggi GLSurfaceView dan tempat ditampilkannya. Tanpa penghitungan ini, objek yang digambar oleh OpenGL ES akan mengalami kemiringan karena proporsi yang tidak seimbang dari jendela tampilan. Transformasi proyeksi biasanya cukup dihitung setelah proporsi tampilan OpenGL ditetapkan atau diubah dalam metode onSurfaceChanged() perender Anda. Untuk informasi selengkapnya tentang pemetaan koordinat dan proyeksi OpenGL ES, lihat Memetakan koordinat objek yang digambar.
  • Tampilan Kamera - Transformasi ini menyesuaikan koordinat objek yang digambar berdasarkan posisi kamera virtual. Perlu diperhatikan bahwa OpenGL ES tidak menentukan objek kamera yang sebenarnya, tetapi memberikan metode utilitas yang menyimulasikan kamera dengan mengubah tampilan objek yang digambar. Transformasi tampilan kamera mungkin hanya dihitung sekali saat Anda membuat GLSurfaceView, atau mungkin berubah secara dinamis berdasarkan tindakan pengguna atau fungsi aplikasi Anda.

Tutorial ini menjelaskan cara membuat proyeksi dan tampilan kamera serta menerapkannya ke bentuk yang digambar di GLSurfaceView Anda.

Menentukan proyeksi

Data untuk transformasi proyeksi dihitung dalam metode onSurfaceChanged() class GLSurfaceView.Renderer Anda. Kode contoh berikut mengambil tinggi dan lebar GLSurfaceView dan menggunakannya untuk mengisi Matrix transformasi proyeksi menggunakan metode Matrix.frustumM():

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);
    }
    

Kode ini mengisi matriks proyeksi, mProjectionMatrix yang kemudian dapat digabungkan dengan transformasi tampilan kamera dalam metode onDrawFrame(), yang ditampilkan di bagian berikutnya.

Catatan: Dengan menerapkan transformasi proyeksi saja ke objek gambar biasanya akan menghasilkan tampilan yang sangat kosong. Secara umum, Anda juga harus menerapkan transformasi tampilan kamera agar ada sesuatu yang muncul di layar.

Menentukan tampilan kamera

Selesaikan proses transformasi objek yang digambar dengan menambahkan transformasi tampilan kamera sebagai bagian dari proses menggambar di perender Anda. Pada kode contoh berikut, transformasi tampilan kamera dihitung menggunakan metode Matrix.setLookAtM() lalu digabungkan dengan matriks proyeksi yang sudah dihitung sebelumnya. Matriks transformasi gabungan kemudian diteruskan ke bentuk yang digambar.

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);
    }
    

Menerapkan transformasi proyeksi dan kamera

Untuk menggunakan proyeksi gabungan dan matriks transformasi tampilan kamera yang ditampilkan di bagian pratinjau, pertama-tama tambahkan variabel matriks ke vertex shader yang sudah ditentukan sebelumnya di class 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;

        ...
    }
    

Selanjutnya, ubah metode draw() objek grafis Anda untuk menerima matriks transformasi gabungan dan terapkan ke bentuk:

Kotlin

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

        // get handle to shape's transformation matrix
        vPMatrixHandle = GLES20.glGetUniformLocation(program, "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(program, "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);
    }
    

Setelah menghitung dan menerapkan transformasi tampilan kamera dan proyeksi dengan benar, objek grafis akan digambar dengan proporsi yang tepat serta akan terlihat seperti berikut:

Gambar 1. Segitiga digambar dengan tampilan kamera dan proyeksi yang diterapkan.

Setelah memiliki aplikasi yang menampilkan bentuk dalam proporsi yang tepat, sekarang saatnya untuk menambahkan motion ke bentuk Anda.