Zdefiniuj kształty

Zdefiniowanie kształtów do rysowania w kontekście widoku OpenGL ES to pierwszy etap tworzenia wysokiej jakości grafiki dla aplikacji. Rysowanie za pomocą OpenGL ES może być nieco trudne, jeśli nie trzeba znać kilku podstawowych kwestii o tym, jak OpenGL ES oczekuje definiowania obiektów graficznych.

Ta lekcja przedstawia układ współrzędnych OpenGL ES względem ekranu urządzenia z Androidem, podstawowe zasady definiowania kształtu i płaszczyzn oraz definiowania trójkątów i kwadratów.

Zdefiniuj trójkąt

OpenGL ES umożliwia definiowanie rysowanych obiektów przy użyciu współrzędnych w przestrzeni trójwymiarowej. Zanim więc narysujesz trójkąt, musisz określić jego współrzędne. W trybie OpenGL typowym sposobem jest zdefiniowanie tablicy wierzchołkowej liczb zmiennoprzecinkowych dla współrzędnych. Aby uzyskać maksymalną wydajność, zapisujesz te współrzędne w obiekcie ByteBuffer, który jest przekazywany do potoku graficznego OpenGL ES w celu przetworzenia.

Kotlin

// number of coordinates per vertex in this array
const val COORDS_PER_VERTEX = 3
var triangleCoords = floatArrayOf(     // in counterclockwise order:
        0.0f, 0.622008459f, 0.0f,      // top
        -0.5f, -0.311004243f, 0.0f,    // bottom left
        0.5f, -0.311004243f, 0.0f      // bottom right
)

class Triangle {

    // Set color with red, green, blue and alpha (opacity) values
    val color = floatArrayOf(0.63671875f, 0.76953125f, 0.22265625f, 1.0f)

    private var vertexBuffer: FloatBuffer =
            // (number of coordinate values * 4 bytes per float)
            ByteBuffer.allocateDirect(triangleCoords.size * 4).run {
                // use the device hardware's native byte order
                order(ByteOrder.nativeOrder())

                // create a floating point buffer from the ByteBuffer
                asFloatBuffer().apply {
                    // add the coordinates to the FloatBuffer
                    put(triangleCoords)
                    // set the buffer to read the first coordinate
                    position(0)
                }
            }
}

Java

public class Triangle {

    private FloatBuffer vertexBuffer;

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;
    static float triangleCoords[] = {   // in counterclockwise order:
             0.0f,  0.622008459f, 0.0f, // top
            -0.5f, -0.311004243f, 0.0f, // bottom left
             0.5f, -0.311004243f, 0.0f  // bottom right
    };

    // Set color with red, green, blue and alpha (opacity) values
    float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

    public Triangle() {
        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
                // (number of coordinate values * 4 bytes per float)
                triangleCoords.length * 4);
        // use the device hardware's native byte order
        bb.order(ByteOrder.nativeOrder());

        // create a floating point buffer from the ByteBuffer
        vertexBuffer = bb.asFloatBuffer();
        // add the coordinates to the FloatBuffer
        vertexBuffer.put(triangleCoords);
        // set the buffer to read the first coordinate
        vertexBuffer.position(0);
    }
}

Domyślnie OpenGL ES zakłada układ współrzędnych, w którym [0,0,0] (X,Y,Z) określa środek ramki GLSurfaceView, [1,1,0] to prawy górny róg ramki, a [-1,-1,0] to lewy dolny róg klatki. Przykład tego układu współrzędnych znajdziesz w przewodniku dla programistów OpenGL ES.

Zauważ, że współrzędne tego kształtu są określone w kierunku przeciwnym do ruchu wskazówek zegara. Kolejność rysowania jest ważna, ponieważ określa, która strona jest przednią ścianą kształtu, którą zwykle chcesz narysować, oraz tylną ścianę, której nie można rysować za pomocą funkcji wygładzania OpenGL ES. Więcej informacji o twarzach i identyfikowaniu znajdziesz w przewodniku dla programistów OpenGL ES.

Zdefiniuj kwadrat

W trybie OpenGL definiowanie trójkątów jest proste, ale co jeśli chcesz uzyskać nieco bardziej złożone elementy? Powiedzmy, kwadrat? Można to zrobić na wiele sposobów, ale typową metodą rysowania takiego kształtu w Open OpenGL ES jest użycie 2 połączonych ze sobą trójkątów:

Rysunek 1. Rysowanie kwadratu z dwóch trójkątów.

Ponownie zdefiniuj wierzchołki w lewo dla obu trójkątów reprezentujących ten kształt i podaj ich wartości w polu ByteBuffer. Aby uniknąć dwukrotnego zdefiniowania dwóch współrzędnych wspólnych dla każdego trójkąta, użyj listy rysunków, aby przekazać potokowi graficznemu OpenGL ES instrukcje rysowania tych wierzchołków. Oto kod tego kształtu:

Kotlin

// number of coordinates per vertex in this array
const val COORDS_PER_VERTEX = 3
var squareCoords = floatArrayOf(
        -0.5f,  0.5f, 0.0f,      // top left
        -0.5f, -0.5f, 0.0f,      // bottom left
         0.5f, -0.5f, 0.0f,      // bottom right
         0.5f,  0.5f, 0.0f       // top right
)

class Square2 {

    private val drawOrder = shortArrayOf(0, 1, 2, 0, 2, 3) // order to draw vertices

    // initialize vertex byte buffer for shape coordinates
    private val vertexBuffer: FloatBuffer =
            // (# of coordinate values * 4 bytes per float)
            ByteBuffer.allocateDirect(squareCoords.size * 4).run {
                order(ByteOrder.nativeOrder())
                asFloatBuffer().apply {
                    put(squareCoords)
                    position(0)
                }
            }

    // initialize byte buffer for the draw list
    private val drawListBuffer: ShortBuffer =
            // (# of coordinate values * 2 bytes per short)
            ByteBuffer.allocateDirect(drawOrder.size * 2).run {
                order(ByteOrder.nativeOrder())
                asShortBuffer().apply {
                    put(drawOrder)
                    position(0)
                }
            }
}

Java

public class Square {

    private FloatBuffer vertexBuffer;
    private ShortBuffer drawListBuffer;

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;
    static float squareCoords[] = {
            -0.5f,  0.5f, 0.0f,   // top left
            -0.5f, -0.5f, 0.0f,   // bottom left
             0.5f, -0.5f, 0.0f,   // bottom right
             0.5f,  0.5f, 0.0f }; // top right

    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

    public Square() {
        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
        // (# of coordinate values * 4 bytes per float)
                squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);

        // initialize byte buffer for the draw list
        ByteBuffer dlb = ByteBuffer.allocateDirect(
        // (# of coordinate values * 2 bytes per short)
                drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);
    }
}

Ten przykład pokazuje, jak można tworzyć bardziej złożone kształty za pomocą OpenGL. Ogólnie do rysowania obiektów wykorzystuje się zbiory trójkątów. Na następnej lekcji dowiesz się, jak rysować na ekranie.