Một số thuật ngữ là một chút off:
- Một
Vertex Array
chỉ là một mảng (thường là một float[]
) có chứa dữ liệu đỉnh. Nó không cần phải bị ràng buộc với bất cứ điều gì. Không được nhầm lẫn với Vertex Array Object
hoặc VAO, mà tôi sẽ đi qua sau
- A
Buffer Object
, thường được gọi là Vertex Buffer Object
khi lưu trữ đỉnh hoặc VBO cho ngắn, là những gì bạn đang gọi chỉ là Buffer
.
- Không có gì được lưu trở lại mảng đỉnh,
glVertexAttribPointer
hoạt động chính xác như glVertexPointer
hoặc glTexCoordPointer
hoạt động, thay vì các thuộc tính được đặt tên, bạn sẽ cung cấp một số chỉ định thuộc tính của riêng bạn. Bạn chuyển giá trị này là index
. Tất cả các cuộc gọi glVertexAttribPointer
của bạn được xếp hàng đợi trong lần tiếp theo bạn gọi glDrawArrays
hoặc glDrawElements
. Nếu bạn có một VAO ràng buộc, VAO sẽ lưu trữ các thiết lập cho tất cả các thuộc tính của bạn.
Vấn đề chính ở đây là bạn đang bối rối các thuộc tính đỉnh với VAO. Thuộc tính Vertex chỉ là cách mới để xác định đỉnh, texcoords, normals, vv để vẽ. Trạng thái cửa hàng VAOs. Đầu tiên tôi sẽ giải thích cách vẽ hoạt động với các thuộc tính đỉnh, sau đó giải thích cách bạn có thể cắt giảm số lượng cuộc gọi phương thức bằng VAOs:
- Bạn phải bật thuộc tính trước khi có thể sử dụng trong trình đổ bóng.Ví dụ: nếu bạn muốn gửi các đỉnh lên đến trình đổ bóng, bạn có nhiều khả năng sẽ gửi nó làm thuộc tính đầu tiên, 0. Vì vậy, trước khi bạn kết xuất, bạn cần bật nó với
glEnableVertexAttribArray(0);
.
- Bây giờ, một thuộc tính được bật, bạn cần phải xác định dữ liệu mà nó sẽ sử dụng. Để làm như vậy bạn cần phải liên kết VBO của bạn -
glBindBuffer(GL_ARRAY_BUFFER, myBuffer);
.
- Và bây giờ chúng tôi có thể xác định thuộc tính -
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
. Theo thứ tự của tham số: 0 là thuộc tính bạn xác định, 3 là kích thước của mỗi đỉnh, GL_FLOAT
là loại, GL_FALSE
có nghĩa là không chuẩn hóa từng đỉnh, 2 số 0 cuối cùng có nghĩa là không có sải chân hoặc lệch trên đỉnh.
- Vẽ cái gì đó với nó -
glDrawArrays(GL_TRIANGLES, 0, 6);
- Điều tiếp theo bạn vẽ có thể không sử dụng thuộc tính 0 (thực tế nó sẽ, nhưng đây là một ví dụ), vì vậy chúng tôi có thể vô hiệu hóa nó -
glDisableVertexAttribArray(0);
Bọc rằng trong glUseProgram()
cuộc gọi và bạn có hệ thống hiển thị hoạt động với trình tạo bóng phù hợp. Nhưng giả sử bạn có 5 thuộc tính, đỉnh, texcoords, normals, color và lightmap tọa độ khác nhau. Trước hết, bạn sẽ thực hiện một cuộc gọi glVertexAttribPointer
đơn cho mỗi thuộc tính này và bạn phải bật tất cả các thuộc tính trước. Giả sử bạn định nghĩa các thuộc tính 0-4 khi tôi liệt kê chúng. Bạn sẽ cho phép tất cả trong số họ như vậy:
for (int i = 0; i < 5; i++)
glEnableVertexAttribArray(i);
Và sau đó bạn sẽ phải ràng buộc VBOs khác nhau cho mỗi thuộc tính (trừ khi bạn lưu trữ chúng tất cả trong một VBO và sử dụng offsets/sải chân), sau đó bạn cần phải thực hiện 5 các cuộc gọi glVertexAttribPointer
khác nhau, từ glVertexAttribPointer(0,...);
đến glVertexAttribPointer(4,...);
cho các tọa độ theo tọa độ ánh sáng tương ứng.
Hy vọng rằng hệ thống một mình có ý nghĩa. Bây giờ tôi sẽ chuyển sang VAO để giải thích cách sử dụng chúng để cắt giảm số lượng cuộc gọi phương thức khi thực hiện loại kết xuất này. Lưu ý rằng việc sử dụng VAO là không cần thiết.
A Vertex Array Object
hoặc VAO được sử dụng để lưu trữ trạng thái của tất cả các cuộc gọi glVertexAttribPointer
và các VBO được nhắm mục tiêu khi mỗi cuộc gọi glVertexAttribPointer
được thực hiện.
Bạn tạo một cuộc gọi với một cuộc gọi đến glGenVertexArrays
. Để lưu trữ mọi thứ bạn cần trong VAO, hãy liên kết nó với glBindVertexArray
, sau đó thực hiện cuộc gọi vẽ đầy đủ
. Tất cả các cuộc gọi liên kết
bị chặn và lưu trữ bởi VAO. Bạn có thể unbind VAO với glBindVertexArray(0);
Bây giờ khi bạn muốn vẽ đối tượng, bạn không cần phải gọi tất cả các VBO liên kết hoặc glVertexAttribPointer
cuộc gọi, bạn chỉ cần để ràng buộc VAO với glBindVertexArray
sau đó gọi glDrawArrays
hoặc glDrawElements
và bạn sẽ vẽ chính xác điều tương tự như thể bạn đang thực hiện tất cả các cuộc gọi phương thức đó. Bạn có thể muốn unbind VAO sau đó quá.
Sau khi bạn hủy liên kết VAO, tất cả trạng thái trả về mức độ trước khi bạn ràng buộc VAO. Tôi không chắc chắn nếu bất kỳ thay đổi bạn thực hiện trong khi VAO là ràng buộc được giữ, nhưng có thể dễ dàng được tìm ra với một chương trình thử nghiệm. Tôi đoán bạn có thể nghĩ đến glBindVertexArray(0);
như liên kết với các VAO "mặc định" ...
Cập nhật: Có người mang đến sự chú ý của tôi sự cần thiết của các lệnh gọi vẽ thực tế.Khi nó quay ra, bạn không thực sự cần phải làm một cuộc gọi vẽ đầy đủ khi thiết lập VAO, chỉ là tất cả các công cụ ràng buộc. Không biết tại sao tôi nghĩ rằng nó là cần thiết trước đó, nhưng nó cố định ngay bây giờ.
"Đối tượng đệm Vertex hoặc VBO (đôi khi được gọi là đối tượng đệm)" Đôi khi "được gọi là bởi vì đó thực sự là những gì được gọi. Nó chỉ là một đối tượng đệm, không khác với bất kỳ đối tượng đệm nào khác mà bạn có thể sử dụng cho các khối đồng nhất, truyền pixel, chuyển đổi phản hồi hoặc bất kỳ việc sử dụng nào khác. Đặc tả OpenGL * không bao giờ * đề cập đến bất cứ điều gì như là một "đối tượng đệm đỉnh"; thậm chí [spec mở rộng ban đầu] (http://www.opengl.org/registry/specs/ARB/vertex_buffer_object.txt) không bao giờ gọi nó như thế. –
Đã chỉnh sửa để khớp với cách đặt tên phù hợp, cảm ơn. –
Câu trả lời hay. Cảm ơn bạn đã dành thời gian viết bài này! Một vài câu hỏi tiếp theo: (1) Bạn nói "trước khi bạn render, và trước khi bạn định nghĩa thuộc tính, bạn cần kích hoạt nó với glEnableVertexAttribArray (0)" - bạn có chắc nó cần được kích hoạt trước khi cuộc gọi đến ' glVertexAttribPointer'? Trong các thử nghiệm của tôi, thứ tự dường như không quan trọng. (2) Nếu tôi hiểu bạn một cách chính xác, Thuộc tính Vertex là toàn cục và chỉ trạng thái đã bật/tắt của chúng được lưu vào VAO hiện đang bị ràng buộc? – mpen