Setelah menentukan bentuk yang akan digambar dengan OpenGL, Anda mungkin ingin menggambarnya. Menggambar bentuk dengan OpenGL ES 2.0 membutuhkan kode yang sedikit lebih banyak dari yang Anda bayangkan, karena API menyediakan memiliki kontrol yang besar atas pipeline rendering grafis.
Tutorial ini menjelaskan cara menggambar bentuk yang telah Anda tetapkan di pelajaran sebelumnya menggunakan OpenGL ES 2.0 API.
Menginisialisasi bentuk
Sebelum menggambar, Anda harus menginisialisasi dan memuat bentuk yang akan digambar. Kecuali
struktur (koordinat asli) dari bentuk yang Anda gunakan dalam perubahan program Anda selama pelatihan
eksekusi, Anda harus melakukan inisialisasi pada
Metode onSurfaceCreated()
Anda
{i>renders<i} untuk memori
dan efisiensi pemrosesan.
Kotlin
class MyGLRenderer : GLSurfaceView.Renderer { ... private lateinit var mTriangle: Triangle private lateinit var mSquare: Square override fun onSurfaceCreated(unused: GL10, config: EGLConfig) { ... // initialize a triangle mTriangle = Triangle() // initialize a square mSquare = Square() } ... }
Java
public class MyGLRenderer implements GLSurfaceView.Renderer { ... private Triangle mTriangle; private Square mSquare; public void onSurfaceCreated(GL10 unused, EGLConfig config) { ... // initialize a triangle mTriangle = new Triangle(); // initialize a square mSquare = new Square(); } ... }
Menggambar bentuk
Menggambar bentuk yang ditentukan menggunakan OpenGL ES 2.0 memerlukan jumlah kode yang signifikan, karena Anda harus memberikan banyak detail ke pipeline rendering grafis. Secara khusus, Anda harus menentukan berikut ini:
- Vertex Shader - Kode grafis OpenGL ES untuk merender verteks bentuk.
- Fragment Shader - Kode OpenGL ES untuk merender tampilan bentuk dengan warna atau tekstur.
- Program - Objek OpenGL ES yang berisi shader yang ingin Anda gunakan untuk menggambar satu atau beberapa bentuk.
Anda memerlukan minimal satu vertex shader untuk menggambar bentuk dan satu fragment shader untuk mewarnai bentuk tersebut.
Shader ini harus dikompilasi lalu ditambahkan ke program OpenGL ES, yang kemudian digunakan untuk menggambar
bentuknya. Berikut adalah contoh cara mendefinisikan shader dasar yang bisa Anda gunakan untuk menggambar bentuk di
Class Triangle
:
Kotlin
class Triangle { private val vertexShaderCode = "attribute vec4 vPosition;" + "void main() {" + " gl_Position = vPosition;" + "}" private val fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}" ... }
Java
public class Triangle { private final String vertexShaderCode = "attribute vec4 vPosition;" + "void main() {" + " gl_Position = vPosition;" + "}"; private final String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}"; ... }
Shader berisi kode OpenGL Shading Language (GLSL) yang harus dikompilasi sebelum menggunakannya lingkungan OpenGL ES. Untuk mengompilasi kode ini, buat metode utilitas di class perender:
Kotlin
fun loadShader(type: Int, shaderCode: String): Int { // create a vertex shader type (GLES20.GL_VERTEX_SHADER) // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) return GLES20.glCreateShader(type).also { shader -> // add the source code to the shader and compile it GLES20.glShaderSource(shader, shaderCode) GLES20.glCompileShader(shader) } }
Java
public static int loadShader(int type, String shaderCode){ // create a vertex shader type (GLES20.GL_VERTEX_SHADER) // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) int shader = GLES20.glCreateShader(type); // add the source code to the shader and compile it GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; }
Untuk menggambar bentuk, Anda harus mengompilasi kode shader, menambahkannya ke program OpenGL ES dan kemudian menautkan program tersebut. Lakukan ini di konstruktor objek yang digambar, sehingga tindakan ini hanya dilakukan sekali.
Catatan: Mengompilasi shader OpenGL ES dan program penautan akan mahal dalam hal siklus CPU dan waktu pemrosesan, jadi Anda harus menghindari melakukan ini lebih dari sekali. Jika Anda melakukan tidak mengetahui isi shader saat runtime, Anda harus membangun kode sedemikian rupa dibuat sekali dan kemudian di-cache untuk digunakan nanti.
Kotlin
class Triangle { ... private var mProgram: Int init { ... val vertexShader: Int = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode) val fragmentShader: Int = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode) // create empty OpenGL ES Program mProgram = GLES20.glCreateProgram().also { // add the vertex shader to program GLES20.glAttachShader(it, vertexShader) // add the fragment shader to program GLES20.glAttachShader(it, fragmentShader) // creates OpenGL ES program executables GLES20.glLinkProgram(it) } } }
Java
public class Triangle() { ... private final int mProgram; public Triangle() { ... int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); // create empty OpenGL ES Program mProgram = GLES20.glCreateProgram(); // add the vertex shader to program GLES20.glAttachShader(mProgram, vertexShader); // add the fragment shader to program GLES20.glAttachShader(mProgram, fragmentShader); // creates OpenGL ES program executables GLES20.glLinkProgram(mProgram); } }
Pada tahap ini, Anda siap menambahkan panggilan sebenarnya yang menggambar bentuk Anda. Menggambar bentuk dengan OpenGL ES mengharuskan Anda menentukan beberapa parameter untuk memberi tahu pipeline rendering apa yang Anda inginkan menggambar dan cara menggambarnya. Karena opsi gambar dapat bervariasi tergantung bentuknya, sebaiknya buat class bentuk berisi logika gambarnya sendiri.
Buat metode draw()
untuk menggambar bentuk. Kode ini menetapkan posisi dan
nilai warna ke shader verteks dan shader fragmen, lalu menjalankan penggambaran
.
Kotlin
private var positionHandle: Int = 0 private var mColorHandle: Int = 0 private val vertexCount: Int = triangleCoords.size / COORDS_PER_VERTEX private val vertexStride: Int = COORDS_PER_VERTEX * 4 // 4 bytes per vertex fun draw() { // Add program to OpenGL ES environment GLES20.glUseProgram(mProgram) // get handle to vertex shader's vPosition member positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition").also { // Enable a handle to the triangle vertices GLES20.glEnableVertexAttribArray(it) // Prepare the triangle coordinate data GLES20.glVertexAttribPointer( it, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer ) // get handle to fragment shader's vColor member mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor").also { colorHandle -> // Set color for drawing the triangle GLES20.glUniform4fv(colorHandle, 1, color, 0) } // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount) // Disable vertex array GLES20.glDisableVertexAttribArray(it) } }
Java
private int positionHandle; private int colorHandle; private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX; private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex public void draw() { // Add program to OpenGL ES environment GLES20.glUseProgram(mProgram); // get handle to vertex shader's vPosition member positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); // Enable a handle to the triangle vertices GLES20.glEnableVertexAttribArray(positionHandle); // Prepare the triangle coordinate data GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); // get handle to fragment shader's vColor member colorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); // Set color for drawing the triangle GLES20.glUniform4fv(colorHandle, 1, color, 0); // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // Disable vertex array GLES20.glDisableVertexAttribArray(positionHandle); }
Setelah semua kode ini diterapkan, menggambar objek ini hanya memerlukan panggilan ke
Metode draw()
dari dalam metode onDrawFrame()
perender Anda:
Kotlin
override fun onDrawFrame(unused: GL10) { ... mTriangle.draw() }
Java
public void onDrawFrame(GL10 unused) { ... mTriangle.draw(); }
Saat Anda menjalankan aplikasi, maka akan terlihat seperti ini:
Ada beberapa masalah terkait contoh kode ini. Pertama-tama, hal ini tidak akan membuat Anda
teman-teman Anda. Kedua, segitiga sedikit terjepit dan berubah bentuk saat Anda mengubah layar
orientasi perangkat. Alasan bentuk miring adalah karena kenyataan bahwa bentuk
verteks belum dikoreksi untuk proporsi area layar di mana
GLSurfaceView
ditampilkan. Anda dapat memperbaiki masalah tersebut menggunakan proyeksi dan kamera
lihat di pelajaran berikutnya.
Terakhir, segitiga ini tidak bergerak, yang terkesan agak membosankan. Di kolom Pelajaran Menambahkan gerakan, Anda akan membuat bentuk ini memutar dan membuat penggunaan pipeline grafis OpenGL ES yang lebih menarik.