2012-01-02 27 views
86

Chỉ muốn chắc chắn rằng tôi hiểu được điều này một cách chính xác (Tôi muốn hỏi về SO Trò chuyện, nhưng nó đã chết trong đó!):glVertexAttribPointer rõ

Chúng tôi đã có một mảng Vertex, mà chúng tôi thực hiện "hiện tại" bởi ràng buộc nó
sau đó chúng tôi đã có một Buffer, mà chúng tôi liên kết với một mục tiêu
sau đó chúng tôi điền vào đó target qua glBufferData trong đó chủ yếu populates bất cứ điều gì đã được liên kết với mục tiêu đó, tức là đệm của chúng tôi
và sau đó chúng ta gọi là glVertexAttribPointer trong đó mô tả cách dữ liệu được trình bày - dữ liệu ở bất kỳ vị trí nào được ràng buộc với GL_ARRAY_BUFFER và t người mô tả của anh ấy được lưu vào Vertex Array ban đầu của chúng tôi

(1) Sự hiểu biết của tôi có chính xác không?
documentation hơi thưa thớt về cách mọi thứ tương quan.

(2) Có loại Vertex Array mặc định nào không? Bởi vì tôi đã quên/bỏ qua glGenVertexArraysglBindVertexArray và chương trình của tôi hoạt động tốt mà không có nó.


Edit: tôi đã bỏ lỡ một bước ... glEnableVertexAttribArray.

(3) Vertex Attrib được gắn với Vertex Array tại thời điểm glVertexAttribPointer được gọi, và sau đó chúng tôi có thể bật/vô hiệu hóa attrib đó qua glEnableVertexAttribArray bất kỳ lúc nào, bất kể Vertex Array nào bị ràng buộc?

Hoặc (3b) Vertex Attrib gắn với Vertex Array tại thời điểm glEnableVertexAttribArray được gọi và do đó chúng tôi có thể thêm cùng Vertex Attrib vào nhiều Vertex Arrays bằng cách gọi glEnableVertexAttribArray vào các thời điểm khác nhau. ?

Trả lời

195

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:

  1. 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);.
  2. 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);.
  3. 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.
  4. Vẽ cái gì đó với nó - glDrawArrays(GL_TRIANGLES, 0, 6);
  5. Đ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ờ.

+9

"Đố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ế. –

+0

Đã chỉnh sửa để khớp với cách đặt tên phù hợp, cảm ơn. –

+3

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

2

Thuật ngữ và chuỗi các API được gọi là khá khó hiểu. Thậm chí còn khó hiểu hơn là làm thế nào các khía cạnh khác nhau - bộ đệm, thuộc tính đỉnh chung và biến thuộc tính shader được kết hợp. Xem OpenGL-Terminology để có giải thích khá tốt.

Hơn nữa, liên kết OpenGL-VBO,shader,VAO hiển thị ví dụ đơn giản với các cuộc gọi API cần thiết. Nó đặc biệt tốt cho những người chuyển từ chế độ ngay lập tức sang đường ống lập trình.

Hy vọng điều đó sẽ hữu ích.

Chỉnh sửa: Như bạn có thể thấy từ các nhận xét bên dưới, mọi người có thể đưa ra các giả định và chuyển đến kết luận. Thực tế là nó khá khó hiểu cho người mới bắt đầu.

+0

"* Xem OpenGL-Terminology để có giải thích khá tốt. *" Trong chưa đầy 1 phút tìm kiếm, tôi đã tìm thấy một thông tin sai lệch: "Chúng được thay thế bằng các thuộc tính đỉnh chung với một số nhận dạng (được gọi là chỉ số) liên kết với một biến shader (cho cooordinates, màu sắc vv) mà quá trình các thuộc tính. " Chúng không được gọi là "chỉ số"; chúng là "vị trí". Đó là một sự phân biệt rất quan trọng vì các thuộc tính đỉnh [* cũng * có "chỉ số"] (https://www.opengl.org/wiki/Program_Introspection#Attributes), * rất khác * từ các vị trí. Đó là một trang web rất khủng khiếp. –

+1

Đó là một nhận xét công bằng nhưng không hoàn toàn chính xác. Nếu bạn nhìn vào API OpenGL để xác định một thuộc tính chung [glVertexAttribPointer] (https://www.opengl.org/sdk/docs/man/html/glVertexAttribPointer.xhtml), 'void glVertexAttribPointer (chỉ số GLuint, kích thước GLint, GLenum loại, GLboolean chuẩn hóa, GLsizei stride, const GLvoid * con trỏ) ', định danh được gọi là' index'. Cùng một định danh trong ngữ cảnh của chương trình được gọi là 'vị trí' trong API [glGetAttribLocation] (https://www.opengl.org/sdk/docs/man/html/glGetAttribLocation.xhtml). –