Đối tượng Mảng Vertex là gì?


114

Tôi mới bắt đầu học OpenGL hôm nay từ hướng dẫn này: http://openglbook.com/the-book/
Tôi đã đến chương 2, nơi tôi vẽ một hình tam giác và tôi hiểu mọi thứ ngoại trừ VAO (từ viết tắt này có được không?). Hướng dẫn có mã này:

glGenVertexArrays(1, &VaoId);
glBindVertexArray(VaoId);

Mặc dù tôi hiểu rằng mã là cần thiết, nhưng tôi không biết nó làm gì. Mặc dù tôi không bao giờ sử dụng VaoId quá thời điểm này (ngoại trừ để phá hủy nó), mã không hoạt động nếu không có nó. Tôi cho rằng điều này là do nó bắt buộc phải ràng buộc, nhưng tôi không biết tại sao. Mã chính xác này có cần là một phần của mọi chương trình OpenGL không? Hướng dẫn giải thích VAOs là:

Đối tượng Mảng Đỉnh (hoặc VAO) là một đối tượng mô tả cách các thuộc tính đỉnh được lưu trữ trong Đối tượng Bộ đệm Vertex (hoặc VBO). Điều này có nghĩa là VAO không phải là đối tượng thực sự lưu trữ dữ liệu đỉnh, mà là bộ mô tả dữ liệu đỉnh. Các thuộc tính Vertex có thể được mô tả bằng hàm glVertexAttribPointer và hai hàm chị em của nó là glVertexAttribIPointer và glVertexAttribLPointer, cái đầu tiên chúng ta sẽ khám phá bên dưới.

Tôi không hiểu VAO mô tả các thuộc tính đỉnh như thế nào. Tôi đã không mô tả chúng theo bất kỳ cách nào. Nó có lấy thông tin từ glVertexAttribPointer không? Tôi đoán đây phải là nó. VAO có đơn giản là một điểm đến cho thông tin từ glVertexAttribPointer không?

Xin lưu ý thêm, hướng dẫn tôi đang làm theo có được chấp nhận không? Có điều gì tôi nên để ý hoặc một hướng dẫn tốt hơn để làm theo không?

Câu trả lời:


100

"Vertex Array Object" do Tiểu ban OpenGL ARB dành cho Những cái tên ngớ ngẩn mang đến cho bạn.

Hãy coi nó như một đối tượng hình học. (Là một lập trình viên cũ của SGI Performer, tôi gọi chúng là bộ địa lý.) Các biến cá thể / thành viên của đối tượng là con trỏ đỉnh, con trỏ bình thường, con trỏ màu, con trỏ thuộc tính N, ...

Khi VAO bị ràng buộc lần đầu tiên, bạn chỉ định các thành viên này bằng cách gọi

glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer...;
glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer...;

và như thế. Thuộc tính nào được bật và con trỏ bạn cung cấp được lưu trữ trong VAO.

Sau đó, khi bạn liên kết lại VAO, tất cả các thuộc tính và con trỏ đó cũng trở thành hiện tại. Vì vậy, một glBindVertexArraylệnh gọi tương đương với tất cả mã cần thiết trước đó để thiết lập tất cả các thuộc tính. Nó rất tiện lợi để chuyển hình học giữa các hàm hoặc phương thức mà không cần phải tạo cấu trúc hoặc đối tượng của riêng bạn.

(Thiết lập một lần, sử dụng nhiều lần là cách dễ nhất để sử dụng VAO, nhưng bạn cũng có thể thay đổi các thuộc tính chỉ bằng cách liên kết nó và thực hiện nhiều lệnh gọi enable / pointer hơn. VAO không phải là hằng số.)

Thêm thông tin để trả lời các câu hỏi của Patrick:

Mặc định cho một VAO mới được tạo là nó trống (AFAIK). Không có hình học nào, thậm chí không có đỉnh, vì vậy nếu bạn cố gắng vẽ nó, bạn sẽ gặp lỗi OpenGL. Điều này là hợp lý, như trong "khởi tạo mọi thứ thành False / NULL / zero".

Bạn chỉ cần glEnableClientStatekhi bạn thiết lập mọi thứ. VAO ghi nhớ trạng thái bật / tắt cho mỗi con trỏ.

Có, VAO sẽ lưu trữ glEnableVertexAttribArrayglVertexAttrib. Mảng đỉnh cũ, bình thường, màu, ... cũng giống như mảng thuộc tính, đỉnh == # 0, v.v.


62
'"Vertex Array Object" được đưa đến cho bạn bởi Tiểu ban OpenGL ARB dành cho Những cái tên ngớ ngẩn.' Vâng, một cái tên ngớ ngẩn cho một đối tượng lưu trữ các ràng buộc mảng đỉnh .
Nicol Bolas

2
Ngoài ra, VAO có liên quan đến glVertexAttribPointer
Patrick

2
Vui lòng thêm một số thông tin về việc sử dụng các thuộc tính đỉnh chung cho những người đang sử dụng cấu hình cốt lõi.
Oskar

8
@NicolBolas Một cái tên tốt hơn sẽ là VertexArrayMacrohoặc một cái gì đó tương tự.
bobobobo

7
@NicolBolas "Vertex Array Object" là một cái tên khủng khiếp. Đó là về việc liên kết dữ liệu với các thuộc tính . Nó không phải là về mảng các đỉnh, như tên của nó. Không có tham chiếu đến các ràng buộc hoặc thuộc tính trong tên, và vì bản thân "mảng đỉnh" là một khái niệm riêng biệt, nên việc hiểu nó thậm chí còn khó hơn. IMHO, "Đối tượng ràng buộc thuộc tính (Vertex)" dễ hiểu hơn. Ngay cả Geometry Object cũng tốt hơn: Tôi không thích nó, nhưng ít nhất nó không bị quá tải.
AkiRoss

8

Vertex Array Objects giống như macro trong các chương trình xử lý văn bản và những thứ tương tự. Một mô tả tốt được tìm thấy ở đây .

Macro chỉ ghi nhớ các hành động bạn đã làm, chẳng hạn như kích hoạt thuộc tính này, liên kết bộ đệm đó, v.v. Khi bạn gọi glBindVertexArray( yourVAOId ), nó chỉ cần phát lại các liên kết con trỏ thuộc tính và liên kết bộ đệm đó.

Vì vậy, cuộc gọi tiếp theo của bạn để rút ra sử dụng bất cứ điều gì bị ràng buộc bởi VAO.

VAO không lưu trữ dữ liệu đỉnh . Không. Dữ liệu đỉnh được lưu trữ trong một đỉnh bộ đệm hoặc trong một mảng bộ nhớ máy khách.


19
-1: Chúng không giống như macro. Nếu là chúng, thì việc ràng buộc một VAO mới sẽ không vô hiệu hóa các mảng đỉnh được VAO trước đó kích hoạt, trừ khi VAO mới đã "ghi lại" bạn vô hiệu hóa các mảng đó một cách rõ ràng . VAO, giống như tất cả các đối tượng OpenGL, trạng thái giữ chứ không phải lệnh. Các lệnh chỉ đơn giản là thay đổi trạng thái, nhưng các đối tượng đi kèm với thiết lập trạng thái mặc định. Đó là lý do tại sao việc ràng buộc một VAO mới được tạo sẽ luôn vô hiệu hóa tất cả các thuộc tính.
Nicol Bolas

6

Tôi luôn nghĩ về VAO như một mảng bộ đệm dữ liệu được OpenGL sử dụng. Sử dụng OpenGL hiện đại, bạn sẽ tạo một VAO và Vertex Buffer Objects.

nhập mô tả hình ảnh ở đây

//vaoB is a buffer
glGenVertexArrays(1, vaoB); //creates one VAO
glBindVertexArray(vao.get(0));
glGenBuffers(vbo.length, vbo, 0); //vbo is a buffer
glBindVertexArray(vao.get(1));
glGenBuffers(vbo1.length, vbo1, 0); //vbo1 is a buffer
glBindVertexArray(vao.get(2));
glGenBuffers(vbo2.length, vbo2, 0); //vbo2 is a buffer

Bước tiếp theo là liên kết dữ liệu với bộ đệm:

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER,vertBuf.limit()*4, vertBuf, GL_STATIC_DRAW); //vertf buf is a floatbuffer of vertices

Tại thời điểm này OpenGL thấy:

nhập mô tả hình ảnh ở đây

Bây giờ chúng ta có thể sử dụng glVertexAttribPointer để cho OpenGL biết dữ liệu trong bộ đệm đại diện:

glBindBuffer(GL_ARRAY_BUFFER, 0); //bind VBO at 0
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); //each vertex has 3 components of size GL_FLOAT with 0 stride (space) between them and the first component starts at 0 (start of data)

nhập mô tả hình ảnh ở đây

OpenGL bây giờ có dữ liệu trong bộ đệm và biết cách dữ liệu được tổ chức thành các đỉnh. Quy trình tương tự có thể được áp dụng cho tọa độ kết cấu, v.v. nhưng đối với tọa độ kết cấu sẽ có hai giá trị.

glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER,coordBuf.limit()*4, coordBuf, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);

Tiếp theo, bạn có thể liên kết kết cấu và vẽ mảng, bạn sẽ muốn tạo trình đổ bóng Vert và Frag, biên dịch và đính kèm nó vào một chương trình (không có ở đây).

glActiveTexture(textureID); //bind our texture
glBindTexture(GL_TEXTURE_2D, textureID);
glDrawArrays(GL_TRIANGLES,0,6); //in this case 6 indices are used for two triangles forming a square

5

VAO là một đối tượng đại diện cho giai đoạn tìm nạp đỉnh của đường ống OpenGL và được sử dụng để cung cấp đầu vào cho bộ đổ bóng đỉnh.

Bạn có thể tạo đối tượng mảng đỉnh như thế này

GLuint vao;
glCreateVertexArrays(1, &vao);
glBindVertexArray(vao);

Đầu tiên, hãy làm một ví dụ đơn giản. Hãy xem xét một tham số đầu vào như vậy trong mã đổ bóng

layout (location = 0) in vec4 offset; // input vertex attribute

Để điền vào thuộc tính này, chúng ta có thể sử dụng

glVertexAttrib4fv(0, attrib); // updates the value of input attribute 0

Mặc dù đối tượng mảng đỉnh lưu trữ các giá trị thuộc tính tĩnh này cho bạn, nhưng nó có thể làm được nhiều việc hơn thế.

Sau khi tạo đối tượng mảng đỉnh, chúng ta có thể bắt đầu điền vào trạng thái của nó. Chúng tôi sẽ yêu cầu OpenGL tự động điền nó bằng cách sử dụng dữ liệu được lưu trữ trong một đối tượng đệm mà chúng tôi cung cấp. Mỗi thuộc tính đỉnh có thể lấy dữ liệu từ một bộ đệm liên kết với một trong một số liên kết bộ đệm đỉnh. Đối với kết thúc này, chúng tôi sử dụng glVertexArrayAttribBinding(GLuint vao, GLuint attribindex, GLuint bindingindex). Ngoài ra, chúng tôi sử dụng glVertexArrayVertexBuffer()hàm để liên kết một bộ đệm với một trong các liên kết bộ đệm đỉnh. Chúng tôi sử dụng glVertexArrayAttribFormat()hàm để mô tả bố cục và định dạng của dữ liệu, và cuối cùng chúng tôi cho phép tự động điền thuộc tính bằng cách gọi glEnableVertexAttribArray().

Khi một thuộc tính đỉnh được bật, OpenGL sẽ cung cấp dữ liệu vào bộ đổ bóng đỉnh dựa trên định dạng và thông tin vị trí mà bạn đã cung cấp glVertexArrayVertexBuffer()glVertexArrayAttribFormat(). Khi thuộc tính bị vô hiệu hóa, bộ đổ bóng đỉnh sẽ được cung cấp thông tin tĩnh mà bạn cung cấp khi gọi tới glVertexAttrib*().

// First, bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(vao, 0, buffer, 0, sizeof(vmath::vec4));

// Now, describe the data to OpenGL, tell it where it is, and turn on automatic
// vertex fetching for the specified attribute
glVertexArrayAttribFormat(vao, 0, 4, GL_FLOAT, GL_FALSE, 0);

glEnableVertexArrayAttrib(vao, 0);

Và viết mã trong trình đổ bóng

layout (location = 0) in vec4 position;

Sau khi tất cả, bạn cần phải gọi đến glDeleteVertexArrays(1, &vao).


Bạn có thể đọc OpenGL SuperBible để hiểu rõ hơn.


3
Thật tốt khi thấy mọi người quảng bá việc sử dụng OpenGL kiểu DSA.
Nicol Bolas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.