Các phương pháp hay nhất về OpenGL VAO


79

Tôi đang đối mặt với một vấn đề mà tôi tin là phụ thuộc vào VAO, nhưng tôi không chắc ..

Tôi không chắc về cách sử dụng VAO chính xác, những gì tôi thường làm trong quá trình khởi tạo GL rất đơn giản

glGenVertexArrays(1,&vao)

Theo sau là một

glBindVertexArray(vao)

và sau đó, trong đường dẫn bản vẽ của mình, tôi chỉ gọi glBindBuffer (), glVertexAttribPointer (), glEnableVertexAttribArray () , v.v. mà không cần quan tâm đến VAO bị ràng buộc

đây có phải là một thực hành đúng?


31
Tôi không chắc tại sao câu hỏi này có -1 .. nó không cho thấy bất kỳ nỗ lực nào và. Hoặc không rõ ràng? Tôi đã dành 3 ngày đấu tranh với các thông số kỹ thuật, wiki và các diễn đàn trước khi gửi bài này và didnt nhận được bất kỳ lý do thực sự tôi nên sử dụng VAO .. meh ..
user815129

18
Bởi vì đối với những người anh em sành điệu, tất cả đều hiển nhiên ngay sau khi họ có sẵn đồ cắt và dán, tại sao phải bận tâm đến việc tìm hiểu mọi thứ ??
mlvljr

Câu trả lời:


93

VAO hoạt động tương tự như VBO và kết cấu liên quan đến cách chúng được liên kết. Có một VAO duy nhất bị ràng buộc trong toàn bộ thời lượng chương trình của bạn sẽ không mang lại lợi ích về hiệu suất vì bạn cũng có thể chỉ hiển thị mà không có VAO nào cả. Trên thực tế, nó có thể chậm hơn tùy thuộc vào cách triển khai chặn cài đặt thuộc tính đỉnh khi chúng được vẽ.

Mục đích của VAO là chạy tất cả các phương thức cần thiết để vẽ một đối tượng một lần trong quá trình khởi tạo và loại bỏ tất cả chi phí gọi phương thức phụ trong vòng lặp chính. Vấn đề là phải có nhiều VAO và chuyển đổi giữa chúng khi vẽ.

Về phương pháp hay nhất, đây là cách bạn nên tổ chức mã của mình:

initialization:
    for each batch
        generate, store, and bind a VAO
        bind all the buffers needed for a draw call
        unbind the VAO

main loop/whenever you render:
    for each batch
        bind VAO
        glDrawArrays(...); or glDrawElements(...); etc.
    unbind VAO

Điều này tránh sự lộn xộn của các bộ đệm ràng buộc / không liên kết và chuyển tất cả các cài đặt cho mỗi thuộc tính đỉnh và thay thế nó chỉ bằng một lệnh gọi phương thức duy nhất, ràng buộc một VAO.


1
có thực sự cần thiết để hủy liên kết VAO không? Ive tìm thấy tập quán khác nhau xung quanh
user815129

3
Nó không cần thiết, nhưng tôi thường làm điều đó trong trường hợp một số đối tượng khác quyết định hiển thị mà không có VAO (gỡ lỗi bản vẽ / thư viện kết xuất phông chữ / v.v.), vì điều đó sẽ (theo như tôi biết) thay thế cài đặt thuộc tính cho VAO hiện bị ràng buộc .
Robert Rouhani

2
Tại sao việc hủy liên kết chỉ xảy ra một lần và bên ngoài vòng lặp? Việc hủy liên kết có nên xảy ra trên cơ sở từng đợt không?
batbrat

12
Ràng buộc một VAO mới thay thế cho VAO cũ. Bỏ ràng buộc bên trong vòng lặp sẽ chỉ là công việc phụ.
Robert Rouhani

1
@RobertRouhani Có lợi ích gì cho VAO cuối cùng chưa được liên kết đó không? (Không unbinding ảnh hưởng đến bất cứ điều gì?)
Mateen Ulhaq

27

Không, đó không phải là cách bạn sử dụng VAO. Bạn nên sử dụng VAO giống như cách bạn đang sử dụng VBO hoặc kết cấu, hoặc bộ tạo bóng. Đầu tiên hãy thiết lập nó. Và trong quá trình hiển thị, chỉ Bind chúng, mà không sửa đổi nó.

Vì vậy, với VAO bạn làm như sau:

void Setup() {
    glGenVertexArrays(..);
    glBindVertexArray(..);
    // now setup all your VertexAttribPointers that will be bound to this VAO
   glBindBuffer(..);
   glVertexAttribPointer(..);
   glEnableVertexAttribArray(..);
}

void Render() {
    glBindVertexArray(vao);
    // that's it, now call one of glDraw... functions
    // no need to set up vertex attrib pointers and buffers!
    glDrawXYZ(..)
}

Xem thêm các liên kết này:


1
Câu trả lời này nên cao hơn. Nó đặc biệt sửa một lỗi khó theo dõi của tôi.
abarax

3
glEnableVertexAttribArray(...)nên được gọi trước glVertexAttribPointer(...). Một số trình điều khiển (bao gồm cả tôi) thực sự không thích nó theo cách khác.
RecursiveExceptionException,

2
Được ủng hộ vì hầu hết các ví dụ không rõ ràng rằng bạn PHẢI gọi glEnableVertexAttribArray trong quá trình liên kết VAO.
Rupert Rawnsley

10

đây có phải là một thực hành đúng?

Vâng, điều này là hoàn toàn hợp pháp và hợp lệ. Liệu nó có tốt không? Tốt...

Đã có một số thử nghiệm hiệu suất không chính thức về loại điều này. Và có vẻ như, ít nhất là trên phần cứng NVIDIA nơi điều này đã được thử nghiệm, việc sử dụng "đúng cách" VAO (tức là: điều mà mọi người ủng hộ) thực sự chậm hơn trong nhiều trường hợp. Điều này đặc biệt đúng nếu việc thay đổi VAO không thay đổi bộ đệm nào bị ràng buộc.

Theo hiểu biết của tôi, không có thử nghiệm hiệu suất tương tự nào diễn ra trên phần cứng AMD. Nói chung, trừ khi có điều gì đó thay đổi với chúng, đây là cách sử dụng VAO được chấp nhận.


2
Chắc chắn một khi mỗi VAO có đủ trạng thái khác nhau mà nó theo dõi, việc chuyển đổi bộ đệm và phân bổ ptrs theo cách thủ công trong vòng kết xuất sẽ yêu cầu nhiều lệnh gọi, chúng ta sẽ bắt đầu thấy hiệu suất được cải thiện từ việc sử dụng VAO?
Steven Lu

Thật không may, liên kết đã chết.
Nick Caplinger

3

Câu trả lời của Robert ở trên đã làm việc cho tôi khi tôi thử nó. Đối với những gì nó đáng giá ở đây là mã, trong Go, sử dụng nhiều Đối tượng thuộc tính Vertex:

// VAO 1

vao1 := gl.GenVertexArray()
vao1.Bind()

vbo1 := gl.GenBuffer()
vbo1.Bind(gl.ARRAY_BUFFER)

verticies1 := []float32{0, 0, 0, 0, 1, 0, 1, 1, 0}
gl.BufferData(gl.ARRAY_BUFFER, len(verticies1)*4, verticies1, gl.STATIC_DRAW)

pa1 := program.GetAttribLocation("position")
pa1.AttribPointer(3, gl.FLOAT, false, 0, nil)
pa1.EnableArray()
defer pa1.DisableArray()

vao1.Unbind()

// VAO 2

vao2 := gl.GenVertexArray()
vao2.Bind()

vbo2 := gl.GenBuffer()
vbo2.Bind(gl.ARRAY_BUFFER)

verticies2 := []float32{-1, -1, 0, -1, 0, 0, 0, 0, 0}
gl.BufferData(gl.ARRAY_BUFFER, len(verticies2)*4, verticies2, gl.STATIC_DRAW)

pa2 := program.GetAttribLocation("position")
pa2.AttribPointer(3, gl.FLOAT, false, 0, nil)
pa2.EnableArray()
defer pa2.DisableArray()

vao2.Unbind()

Sau đó, trong vòng lặp chính của bạn, bạn có thể sử dụng chúng như sau:

for !window.ShouldClose() {
    gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

    vao1.Bind()
    gl.DrawArrays(gl.TRIANGLES, 0, 3)
    vao1.Unbind()

    vao2.Bind()
    gl.DrawArrays(gl.TRIANGLES, 0, 3)
    vao2.Unbind()

    window.SwapBuffers()
    glfw.PollEvents()

    if window.GetKey(glfw.KeyEscape) == glfw.Press {
        window.SetShouldClose(true)
    }
}

Nếu bạn muốn xem nguồn đầy đủ, nó có sẵn dưới dạng Gist và được lấy từ các ví dụ trong go-gl:

https://gist.github.com/mdmarek/0f73890ae2547cdba3a7

Cảm ơn mọi người vì những câu trả lời ban đầu, tôi có cùng câu hỏi với ECrownofFire.


3
Bạn không cần phải hủy liên kết vao1 trước khi ràng buộc vào2, chỉ cần ràng buộc vào2 là đủ.
havokentity
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.