Formen zeichnen

Nachdem Sie mit OpenGL gezeichnete Formen definiert haben, möchten Sie diese wahrscheinlich zeichnen. Formen zeichnen mit OpenGL ES 2.0 erfordert etwas mehr Code, als Sie sich vorstellen können, da die API eine Kontrolle über die Grafik-Rendering-Pipeline.

In dieser Lektion erfahren Sie, wie Sie die Formen, die Sie in der vorherigen Lektion definiert haben, mithilfe des OpenGL- ES 2.0 API.

Formen initialisieren

Bevor Sie zeichnen, müssen Sie die Formen, die Sie zeichnen möchten, initialisieren und laden. Es sei denn, die Struktur (die ursprünglichen Koordinaten) der Formen, die Sie in Ihrem Programm verwenden, ändern sich im Laufe des Kurses der Ausführung sollten Sie sie im onSurfaceCreated() Methode Ihres Renderer für Speicher- und Verarbeitungseffizienz.

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

Formen zeichnen

Das Zeichnen einer definierten Form mit OpenGL ES 2.0 erfordert sehr viel Code, da Sie viele Details für die Grafik-Rendering-Pipeline bereitstellen müssen. Insbesondere müssen Sie die Folgendes:

  • Vertex Shader: OpenGL ES-Grafikcode zum Rendern der Eckpunkte einer Form.
  • Fragment Shader: OpenGL ES-Code zum Rendern der Fläche einer Form mit Farben oder Texturen.
  • Programm: Ein OpenGL ES-Objekt, das die Shader enthält, die Sie zum Zeichnen verwenden möchten. eine oder mehrere Formen.

Sie benötigen mindestens einen Scheitel-Shader zum Zeichnen einer Form und einen Fragment-Shader zum Einfärben der Form. Diese Shader müssen kompiliert und dann einem OpenGL ES-Programm hinzugefügt werden, das dann zum Zeichnen von der Form. Hier ist ein Beispiel für die Definition von Basis-Shadern, die Sie zum Zeichnen einer Form in der Klasse 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 enthalten OpenGL Shading Language-Code (GLSL), der vor der Verwendung in OpenGL ES-Umgebung ausführen. Erstellen Sie zum Kompilieren dieses Codes eine Dienstprogrammmethode in Ihrer Renderer-Klasse:

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

Um Ihre Form zu zeichnen, müssen Sie den Shader-Code kompilieren und ihn einem OpenGL ES-Programm hinzufügen Objekt und dann das Programm verknüpfen. Führen Sie diesen Schritt im Konstruktor des gezeichneten Objekts aus. einmal.

Hinweis:Das Kompilieren von OpenGL ES-Shadern und Verknüpfungsprogrammen ist teuer. in Bezug auf CPU-Zyklen und Verarbeitungszeit, daher sollten Sie dies nicht mehrmals tun. Wenn Sie den Inhalt Ihrer Shader zur Laufzeit nicht kennen, sollten Sie Ihren Code so erstellen, werden einmal erstellt und dann für die spätere Verwendung im Cache gespeichert.

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

Jetzt können Sie die tatsächlichen Aufrufe hinzufügen, die Ihre Form zeichnen. Formen zeichnen mit Bei OpenGL ES musst du mehrere Parameter angeben, um der Rendering-Pipeline mitzuteilen, was du möchtest. und wie sie gezeichnet werden. Da die Zeichnungsoptionen je nach Form variieren können, ist es ratsam, Ihre Formklassen enthalten eine eigene Zeichenlogik.

Erstellen Sie eine draw()-Methode zum Zeichnen der Form. Mit diesem Code werden die Position und Farbwerte an den Scheitelpunkt-Shader und den Fragment-Shader der Form zu und führt dann die Zeichnung aus. .

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

Sobald Sie den gesamten Code eingerichtet haben, erfordert das Zeichnen dieses Objekts nur einen Aufruf der Methode draw() in der onDrawFrame()-Methode des Renderers:

Kotlin

override fun onDrawFrame(unused: GL10) {
    ...

    mTriangle.draw()
}

Java

public void onDrawFrame(GL10 unused) {
    ...

    mTriangle.draw();
}

Wenn Sie die Anwendung ausführen, sollte sie in etwa so aussehen:

Abbildung 1: Dreieck ohne Projektion oder Kameraansicht gezeichnet.

Bei diesem Codebeispiel gibt es einige Probleme. Zunächst einmal wird es Ihre Freunde. Zweitens ist das Dreieck etwas zusammengedrückt und ändert seine Form, wenn man den Bildschirm Ausrichtung des Geräts. Die Form ist verzerrt, weil das Objekt Die Eckpunkte wurden nicht für die Proportionen des Bildschirmbereichs korrigiert, in dem die GLSurfaceView wird angezeigt. Dieses Problem können Sie mit einer Projektion und einer Kamera erhalten Sie in der nächsten Lektion.

Schließlich steht das Dreieck fest, was ein wenig langweilig ist. Im Bewegung hinzufügen, Sie erstellen diese Form. und die Nutzung der OpenGL ES-Grafikpipeline interessanter machen.