Traccia forme

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:

Figura 1. Triangolo disegnato senza una proiezione o un'inquadratura della fotocamera.

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.