Xác định hình dạng

Việc có thể xác định các hình dạng cần vẽ trong bối cảnh khung hiển thị OpenGL ES là bước đầu tiên để tạo đồ hoạ cao cấp cho ứng dụng. Bạn có thể gặp chút khó khăn khi vẽ bằng OpenGL ES mà không cần biết một số điều cơ bản về cách OpenGL ES cần bạn xác định các đối tượng đồ hoạ.

Bài học này giải thích về hệ thống toạ độ OpenGL ES liên quan đến màn hình thiết bị Android, những kiến thức cơ bản về việc xác định hình dạng, mặt hình dạng, cũng như xác định hình tam giác và hình vuông.

Định nghĩa tam giác

OpenGL ES cho phép bạn xác định các đối tượng được vẽ bằng cách sử dụng toạ độ trong không gian ba chiều. Vì vậy, trước khi có thể vẽ tam giác, bạn phải xác định toạ độ của tam giác đó. Trong OpenGL, cách thông thường để làm việc này là xác định một mảng đỉnh gồm các số dấu phẩy động cho các toạ độ. Để đạt được hiệu quả tối đa, bạn sẽ ghi các toạ độ này vào ByteBuffer. Các toạ độ này được chuyển vào quy trình đồ hoạ OpenGL ES để xử lý.

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

Theo mặc định, OpenGL ES giả định một hệ toạ độ trong đó [0,0,0] (X,Y,Z) chỉ định tâm của khung GLSurfaceView, [1,1,0] là góc trên cùng bên phải của khung và [-1,-1,0] là góc dưới cùng bên trái khung hình. Để xem hình minh hoạ về hệ toạ độ này, hãy xem Hướng dẫn cho nhà phát triển OpenGL ES.

Lưu ý rằng các toạ độ của hình này được xác định theo thứ tự ngược chiều kim đồng hồ. Thứ tự vẽ rất quan trọng vì nó xác định mặt nào là mặt trước của hình dạng (mà bạn thường muốn vẽ) và mặt sau (bạn có thể chọn không vẽ) bằng tính năng mặt trước của hình dạng. Để biết thêm thông tin về khuôn mặt và việc chọn lọc, hãy xem hướng dẫn cho nhà phát triển OpenGL ES.

Định nghĩa hình vuông

Việc xác định tam giác khá dễ dàng trong OpenGL, nhưng nếu bạn muốn phức tạp hơn một chút thì sao? Giả sử một hình vuông? Có nhiều cách để thực hiện việc này, nhưng một đường dẫn điển hình để vẽ hình dạng như vậy trong OpenGL ES là sử dụng 2 tam giác được vẽ cùng nhau:

Hình 1. Vẽ một hình vuông bằng hai tam giác.

Một lần nữa, bạn nên xác định các đỉnh theo thứ tự ngược chiều kim đồng hồ cho cả hai tam giác biểu thị hình dạng này và đặt các giá trị vào ByteBuffer. Để tránh việc xác định 2 toạ độ mà mỗi tam giác chia sẻ hai lần, hãy sử dụng danh sách bản vẽ để cho quy trình đồ hoạ OpenGL ES biết cách vẽ các đỉnh này. Dưới đây là mã cho hình dạng này:

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

Ví dụ này giúp bạn xem trước những gì cần thiết để tạo các hình dạng phức tạp hơn bằng OpenGL. Nhìn chung, bạn sẽ sử dụng tập hợp tam giác để vẽ các đối tượng. Trong bài học tiếp theo, bạn sẽ tìm hiểu cách vẽ các hình dạng này trên màn hình.