Dopo aver definito le forme da disegnare con OpenGL, probabilmente vorrai disegnarle. Disegno di forme con OpenGL ES 2.0 richiede un po' più di codice di quanto si possa immaginare, perché l'API fornisce avere un notevole controllo sulla pipeline di rendering grafico.
Questa lezione spiega come disegnare le forme definite nella lezione precedente con la modalità OpenGL API ES 2.0.
Inizializza forme
Prima di iniziare a disegnare, devi inizializzare e caricare le forme che intendi disegnare. A meno che
la struttura (le coordinate originali) delle forme che usi nel programma cambia durante il corso
di esecuzione, dovresti inizializzarle nel
onSurfaceCreated()
metodo di
renderer per l'efficienza di memoria ed elaborazione.
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(); } ... }
Tracciare una forma
Disegnare una forma definita con OpenGL ES 2.0 richiede una notevole quantità di codice, devi fornire molti dettagli sulla pipeline di rendering grafico. Nello specifico, devi definire seguenti:
- Vertex Shader: codice grafico OpenGL ES per il rendering dei vertici di una forma.
- Fragment Shader: codice OpenGL ES per il rendering del volto di una forma con colori o texture.
- Programma: un oggetto OpenGL ES che contiene gli oscuratori che vuoi utilizzare per disegnare una o più forme.
Hai bisogno di almeno uno shaker vertex per disegnare una forma e uno shaker di frammenti per colorare quella forma.
Questi componenti devono essere compilati e poi aggiunti a un programma OpenGL ES, che viene quindi utilizzato per disegnare
la forma. Ecco un esempio di come definire gli screenr di base che puoi utilizzare per disegnare una forma
Triangle
corso:
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;" + "}"; ... }
Gli Shader contengono codice GLSL (OpenGL Shading Language) che deve essere compilato prima di essere utilizzato nell'ambiente OpenGL ES. Per compilare il codice, crea un metodo di utilità nella tua classe di renderer:
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; }
Per disegnare la forma, è necessario compilare il codice dello shaker e aggiungerlo a un programma OpenGL ES. e poi collegare il programma. Esegui questa operazione nel costruttore dell'oggetto disegnato, in modo da farlo solo una volta sola.
Nota:la compilazione di Shar OpenGL ES e il collegamento dei programmi è costosa. in termini di cicli della CPU e tempo di elaborazione, quindi dovresti evitare di ripetere questa operazione più di una volta. Se sì non conoscete i contenuti dei vostri shaker in fase di runtime, dovete creare il codice in modo che vengono creati una volta e memorizzati nella cache per un utilizzo futuro.
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); } }
A questo punto, tutto è pronto per aggiungere le chiamate effettive che tracciano la tua forma. Disegno di forme con OpenGL ES richiede di specificare diversi parametri per indicare alla pipeline di rendering ciò che vuoi di disegnare e come disegnarlo. Poiché le opzioni di disegno possono variare in base alla forma, è consigliabile che le classi di forma contengono una propria logica di disegno.
Crea un metodo draw()
per tracciare la forma. Questo codice imposta la posizione
i valori di colore allo shaker vertex e allo shaker dei frammenti della forma, quindi esegue il disegno
personalizzata.
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); }
Una volta creato il codice, per disegnare l'oggetto occorre semplicemente una chiamata
draw()
dal metodo onDrawFrame()
del renderer:
Kotlin
override fun onDrawFrame(unused: GL10) { ... mTriangle.draw() }
Java
public void onDrawFrame(GL10 unused) { ... mTriangle.draw(); }
Quando esegui l'applicazione, il codice dovrebbe avere un aspetto simile al seguente:
Si sono verificati alcuni problemi in questo esempio di codice. Prima di tutto, non stupirà il vostro
amici. In secondo luogo, il triangolo è un po' schiacciato e cambia forma quando cambi schermata.
l'orientamento del dispositivo. Il motivo per cui la forma è inclinata è che l'oggetto
i vertici non siano stati corretti per le proporzioni dell'area dello schermo in cui
GLSurfaceView
visualizzato. Puoi risolvere il problema usando una proiezione e una fotocamera
nella prossima lezione.
Infine, il triangolo è stazionario, il che è un po' noioso. Nella nella lezione Aggiungi il movimento, crei questa forma ruotare e utilizzare in modo più interessante la pipeline grafica OpenGL ES.