Cómo dibujar formas

Después de definir formas que se dibujarán con OpenGL, es probable que quieras dibujarlas. Cómo dibujar formas con OpenGL ES 2.0 requiere un poco más de código de lo que te imaginas, porque la API proporciona un un gran control de la canalización de procesamiento gráfico.

En esta lección, se explica cómo dibujar las formas que definiste en la lección anterior usando OpenGL ES 2.0.

Inicializa formas

Antes de empezar a dibujar, debes inicializar y cargar las formas que planeas hacer. A menos que el la estructura (las coordenadas originales) de las formas que usas en tu programa cambian durante el curso de ejecución, debes inicializarlos en el método onSurfaceCreated() de tu para la eficiencia de memoria y procesamiento.

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

Cómo dibujar una forma

Dibujar una forma definida usando OpenGL ES 2.0 requiere una cantidad significativa de código, ya que se Debe proporcionar muchos detalles a la canalización de renderización de gráficos. Específicamente, debes definir lo siguiente:

  • Sombreador de vértices: Código de gráficos de OpenGL ES para procesar los vértices de una forma.
  • Sombreador de fragmentos: Código de OpenGL ES para renderizar la cara de una forma con colores o texturas.
  • Programa: Es un objeto de OpenGL ES que contiene los sombreadores que quieres usar para dibujar. una o más formas.

Necesitas al menos un sombreador de vértices para dibujar una forma y un sombreador de fragmentos para colorear esa forma. Estos sombreadores deben compilarse y, luego, agregarse a un programa de OpenGL ES, que luego se usa para dibujar la forma. Este es un ejemplo de cómo definir sombreadores básicos que puedes usar para dibujar una forma en el Clase 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;" +
        "}";

    ...
}

Los sombreadores contienen código OpenGL Shading Language (GLSL) que debe compilarse antes de su uso en el entorno de OpenGL ES. Para ello, crea un método de utilidades en tu clase de procesador:

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

Para dibujar tu forma, debes compilar el código del sombreador y agregarlo a un programa de OpenGL ES y, luego, vincular el programa. Haz esto en el constructor del objeto dibujado para que solo se realice una vez.

Nota: Compilar sombreadores de OpenGL ES y vincular programas es costoso. en términos de ciclos de CPU y tiempo de procesamiento, por lo que debes evitar hacer esto más de una vez. Si lo haces no conoce el contenido de los sombreadores en el tiempo de ejecución, deberías compilar el código de modo que solo se crean una vez y, luego, se almacenan en caché para usarlas más adelante.

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

Ya estás listo para agregar las llamadas reales que dibujan la forma. Dibujar formas con OpenGL ES requiere que especifiques varios parámetros para indicarle a la canalización de renderización lo que deseas de dibujar y cómo dibujarlo. Dado que las opciones de dibujo pueden variar según la forma, es una buena idea tener tu de formas contienen su propia lógica de dibujo.

Para dibujar la forma, crea un método draw(). Este código establece la posición y valores de color al sombreador de vértices y al sombreador de fragmentos de la forma y, luego, ejecuta el dibujo .

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 vez que tengas todo el código implementado, dibujar este objeto solo requiere una llamada a la draw() desde el método onDrawFrame() de tu procesador:

Kotlin

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

    mTriangle.draw()
}

Java

public void onDrawFrame(GL10 unused) {
    ...

    mTriangle.draw();
}

Cuando ejecutes la aplicación, debería verse de la siguiente manera:

Figura 1: Triángulo dibujado sin una proyección ni una vista de cámara

Hay algunos problemas en este código de ejemplo. En primer lugar, no vas a impresionar a tus amigos. En segundo lugar, el triángulo está un poco aplastado y cambia de forma cuando modificas la pantalla. orientación del dispositivo. La razón por la que la forma está sesgada es que la naturaleza no se corrigieron los vértices para las proporciones del área de la pantalla donde Se muestra GLSurfaceView. Para solucionar ese problema, puedes usar una proyección y una cámara. en la siguiente lección.

Por último, el triángulo es estacionario, lo cual es un poco aburrido. En la Agrega movimiento para crear esta forma. rotar y hacer un uso más interesante de la canalización de gráficos de OpenGL ES.