Xác định hình dạng
Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang
Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
Việc có thể xác định hình dạng cần vẽ trong bối cảnh chế độ xem OpenGL ES là bước đầu tiên trong
tạo đồ hoạ cao cấp cho ứng dụng của bạn. Vẽ bằng OpenGL ES có thể hơi phức tạp nếu không
biết một số thông tin cơ bản về cách OpenGL ES muốn bạn xác định đối tượng đồ hoạ.
Bài học này giải thích về hệ toạ độ OpenGL ES liên quan đến màn hình thiết bị Android,
kiến thức cơ bản về việc xác định hình dạng, mặt của hình dạng cũng như xác đị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 vẽ một hình tam giác, bạn phải xác định toạ độ của nó. Trong OpenGL, cách làm thông thường
hàm này xác định một mảng đỉnh gồm các số có dấu phẩy động cho toạ độ. Tối đa
hiệu quả, bạn sẽ viết các toạ độ này vào ByteBuffer
, đượ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) xác định tâm của
khung GLSurfaceView
,
[1,1,0] là góc trên cùng bên phải của khung hình và
[-1,-1,0] là góc dưới cùng bên trái của khung hình. Để biết hình minh hoạ hệ toạ độ này, hãy xem
Nhà phát triển OpenGL ES
hướng dẫn.
Lưu ý rằng toạ độ của hình dạng này được xác định theo thứ tự ngược chiều kim đồng hồ. Bản 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 cách sử dụng bộ chọn OpenGL ES
khuôn mặt. Để biết thêm thông tin về các khuôn mặt và danh sách chọn khuôn mặt, hãy xem
Hướng dẫn cho nhà phát triển OpenGL ES.
Xác định hình vuông
Xác định tam giác khá dễ dàng trong OpenGL, nhưng nếu bạn muốn nhận thêm một chút
phức tạp không? Bạn có muốn chọn hình vuông không? Có một số cách để thực hiện điều này, nhưng cách thức thông thường để vẽ
hình dạng trong OpenGL ES sử dụng hai hình tam giác được vẽ cùng nhau:
Hình 1. Vẽ hình vuông bằng hai hình tam giác.
Xin nhắc lại, 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
xác định hai toạ độ mà mỗi tam giác dùng chung hai lần, hãy sử dụng danh sách vẽ để cho
Quy trình đồ hoạ của OpenGL ES cách vẽ các đỉnh này. Đâ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 cung cấp cho bạn thông tin sơ bộ về những việc cần làm để tạo hình dạng phức tạp hơn bằng OpenGL. Trong
nói chung, bạn sẽ sử dụng các bộ sưu tập tam giác để vẽ các đối tượng. Trong bài học tiếp theo, bạn sẽ học cách vẽ
các hình dạng này trên màn hình.
Nội dung và mã mẫu trên trang này phải tuân thủ các giấy phép như mô tả trong phần Giấy phép nội dung. Java và OpenJDK là nhãn hiệu hoặc nhãn hiệu đã đăng ký của Oracle và/hoặc đơn vị liên kết của Oracle.
Cập nhật lần gần đây nhất: 2025-07-27 UTC.
[[["Dễ hiểu","easyToUnderstand","thumb-up"],["Giúp tôi giải quyết được vấn đề","solvedMyProblem","thumb-up"],["Khác","otherUp","thumb-up"]],[["Thiếu thông tin tôi cần","missingTheInformationINeed","thumb-down"],["Quá phức tạp/quá nhiều bước","tooComplicatedTooManySteps","thumb-down"],["Đã lỗi thời","outOfDate","thumb-down"],["Vấn đề về bản dịch","translationIssue","thumb-down"],["Vấn đề về mẫu/mã","samplesCodeIssue","thumb-down"],["Khác","otherDown","thumb-down"]],["Cập nhật lần gần đây nhất: 2025-07-27 UTC."],[],[],null,["# Define shapes\n\nBeing able to define shapes to be drawn in the context of an OpenGL ES view is the first step in\ncreating high-end graphics for your app. Drawing with OpenGL ES can be a little tricky without\nknowing a few basic things about how OpenGL ES expects you to define graphic objects.\n\nThis lesson explains the OpenGL ES coordinate system relative to an Android device screen, the\nbasics of defining a shape, shape faces, as well as defining a triangle and a square.\n\nDefine a triangle\n-----------------\n\nOpenGL ES allows you to define drawn objects using coordinates in three-dimensional space. So,\nbefore you can draw a triangle, you must define its coordinates. In OpenGL, the typical way to do\nthis is to define a vertex array of floating point numbers for the coordinates. For maximum\nefficiency, you write these coordinates into a [ByteBuffer](/reference/java/nio/ByteBuffer), that is passed into the\nOpenGL ES graphics pipeline for processing. \n\n### Kotlin\n\n```kotlin\n// number of coordinates per vertex in this array\nconst val COORDS_PER_VERTEX = 3\nvar triangleCoords = floatArrayOf( // in counterclockwise order:\n 0.0f, 0.622008459f, 0.0f, // top\n -0.5f, -0.311004243f, 0.0f, // bottom left\n 0.5f, -0.311004243f, 0.0f // bottom right\n)\n\nclass Triangle {\n\n // Set color with red, green, blue and alpha (opacity) values\n val color = floatArrayOf(0.63671875f, 0.76953125f, 0.22265625f, 1.0f)\n\n private var vertexBuffer: FloatBuffer =\n // (number of coordinate values * 4 bytes per float)\n ByteBuffer.allocateDirect(triangleCoords.size * 4).run {\n // use the device hardware's native byte order\n order(ByteOrder.nativeOrder())\n\n // create a floating point buffer from the ByteBuffer\n asFloatBuffer().apply {\n // add the coordinates to the FloatBuffer\n put(triangleCoords)\n // set the buffer to read the first coordinate\n position(0)\n }\n }\n}\n```\n\n### Java\n\n```java\npublic class Triangle {\n\n private FloatBuffer vertexBuffer;\n\n // number of coordinates per vertex in this array\n static final int COORDS_PER_VERTEX = 3;\n static float triangleCoords[] = { // in counterclockwise order:\n 0.0f, 0.622008459f, 0.0f, // top\n -0.5f, -0.311004243f, 0.0f, // bottom left\n 0.5f, -0.311004243f, 0.0f // bottom right\n };\n\n // Set color with red, green, blue and alpha (opacity) values\n float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };\n\n public Triangle() {\n // initialize vertex byte buffer for shape coordinates\n ByteBuffer bb = ByteBuffer.allocateDirect(\n // (number of coordinate values * 4 bytes per float)\n triangleCoords.length * 4);\n // use the device hardware's native byte order\n bb.order(ByteOrder.nativeOrder());\n\n // create a floating point buffer from the ByteBuffer\n vertexBuffer = bb.asFloatBuffer();\n // add the coordinates to the FloatBuffer\n vertexBuffer.put(triangleCoords);\n // set the buffer to read the first coordinate\n vertexBuffer.position(0);\n }\n}\n```\n\nBy default, OpenGL ES assumes a coordinate system where \\[0,0,0\\] (X,Y,Z) specifies the center of\nthe [GLSurfaceView](/reference/android/opengl/GLSurfaceView) frame,\n\\[1,1,0\\] is the top right corner of the frame and\n\\[-1,-1,0\\] is bottom left corner of the frame. For an illustration of this coordinate system, see the\n[OpenGL ES developer\nguide](/develop/ui/views/graphics/opengl/about-opengl#coordinate-mapping).\n\nNote that the coordinates of this shape are defined in a counterclockwise order. The drawing\norder is important because it defines which side is the front face of the shape, which you typically\nwant to have drawn, and the back face, which you can choose to not draw using the OpenGL ES cull\nface feature. For more information about faces and culling, see the\n[OpenGL ES](/develop/ui/views/graphics/opengl/about-opengl#faces-winding) developer guide.\n\nDefine a square\n---------------\n\nDefining triangles is pretty easy in OpenGL, but what if you want to get a just a little more\ncomplex? Say, a square? There are a number of ways to do this, but a typical path to drawing such a\nshape in OpenGL ES is to use two triangles drawn together:\n\n\n**Figure 1.** Drawing a square using two triangles.\n\nAgain, you should define the vertices in a counterclockwise order for both triangles that\nrepresent this shape, and put the values in a [ByteBuffer](/reference/java/nio/ByteBuffer). In order to avoid\ndefining the two coordinates shared by each triangle twice, use a drawing list to tell the\nOpenGL ES graphics pipeline how to draw these vertices. Here's the code for this shape: \n\n### Kotlin\n\n```kotlin\n// number of coordinates per vertex in this array\nconst val COORDS_PER_VERTEX = 3\nvar squareCoords = floatArrayOf(\n -0.5f, 0.5f, 0.0f, // top left\n -0.5f, -0.5f, 0.0f, // bottom left\n 0.5f, -0.5f, 0.0f, // bottom right\n 0.5f, 0.5f, 0.0f // top right\n)\n\nclass Square2 {\n\n private val drawOrder = shortArrayOf(0, 1, 2, 0, 2, 3) // order to draw vertices\n\n // initialize vertex byte buffer for shape coordinates\n private val vertexBuffer: FloatBuffer =\n // (# of coordinate values * 4 bytes per float)\n ByteBuffer.allocateDirect(squareCoords.size * 4).run {\n order(ByteOrder.nativeOrder())\n asFloatBuffer().apply {\n put(squareCoords)\n position(0)\n }\n }\n\n // initialize byte buffer for the draw list\n private val drawListBuffer: ShortBuffer =\n // (# of coordinate values * 2 bytes per short)\n ByteBuffer.allocateDirect(drawOrder.size * 2).run {\n order(ByteOrder.nativeOrder())\n asShortBuffer().apply {\n put(drawOrder)\n position(0)\n }\n }\n}\n```\n\n### Java\n\n```java\npublic class Square {\n\n private FloatBuffer vertexBuffer;\n private ShortBuffer drawListBuffer;\n\n // number of coordinates per vertex in this array\n static final int COORDS_PER_VERTEX = 3;\n static float squareCoords[] = {\n -0.5f, 0.5f, 0.0f, // top left\n -0.5f, -0.5f, 0.0f, // bottom left\n 0.5f, -0.5f, 0.0f, // bottom right\n 0.5f, 0.5f, 0.0f }; // top right\n\n private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices\n\n public Square() {\n // initialize vertex byte buffer for shape coordinates\n ByteBuffer bb = ByteBuffer.allocateDirect(\n // (# of coordinate values * 4 bytes per float)\n squareCoords.length * 4);\n bb.order(ByteOrder.nativeOrder());\n vertexBuffer = bb.asFloatBuffer();\n vertexBuffer.put(squareCoords);\n vertexBuffer.position(0);\n\n // initialize byte buffer for the draw list\n ByteBuffer dlb = ByteBuffer.allocateDirect(\n // (# of coordinate values * 2 bytes per short)\n drawOrder.length * 2);\n dlb.order(ByteOrder.nativeOrder());\n drawListBuffer = dlb.asShortBuffer();\n drawListBuffer.put(drawOrder);\n drawListBuffer.position(0);\n }\n}\n```\n\nThis example gives you a peek at what it takes to create more complex shapes with OpenGL. In\ngeneral, you use collections of triangles to draw objects. In the next lesson, you learn how to draw\nthese shapes on screen."]]