Tại sao các hướng dẫn sử dụng các cách tiếp cận khác nhau để kết xuất OpenGL?


43

http://www.sdltutorials.com/sdl-opengl-tutorial-basics

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-trigin/

Hai hướng dẫn này sử dụng các phương pháp hoàn toàn khác nhau để có được kết quả gần như nhau. Việc đầu tiên sử dụng những thứ như glBegin(GL_QUADS). Cái thứ hai sử dụng những thứ như vertexBufferObjects, shader dựa trên GLEW. Nhưng kết quả là như nhau: bạn có được hình dạng cơ bản.

Tại sao những khác biệt này tồn tại?

Cách tiếp cận đầu tiên có vẻ dễ hiểu hơn nhiều. Lợi thế của cách tiếp cận thứ hai phức tạp là gì?


4
Không bao giờ chỉ có một cách để lột da một con mèo.
Philipp

4
@Philipp Có, nhưng có những cách đúng và cách sai, cách cũ và cách mới (và như câu trả lời dưới đây chứng minh, cách cũ và cách mới có thể không tương thích trong mọi tình huống)
Andrew Hill

3
Không cách đúng và cách sai, chỉ có cách tồi tệ hơn và cách tốt hơn (trong một số chiều khác nhau).
dùng253751

glBeginglEndđã bị phản đối vì chúng cực kỳ kém hiệu quả đối với các kiến ​​trúc đồ họa hiện tại
Alex

Câu trả lời:


77

OpenGL có bốn phiên bản chính khác nhau, không tính các phiên bản cho thiết bị di động và hệ thống nhúng (OpenGL | ES) và Web thông qua JavaScript (WebGL). Giống như Direct3D 11 có cách làm việc khác với Direct3D 8, OpenGL 3 cũng có cách làm khác với OpenGL 1. Sự khác biệt lớn là các phiên bản OpenGL chủ yếu chỉ là các tiện ích bổ sung cho các phiên bản cũ (nhưng không hoàn toàn).

Ngoài các phiên bản và phiên bản khác nhau của OpenGL, OpenGL chính cũng đã thêm khái niệm về cấu hình. Cụ thể là Hồ sơ tương thích (cho phép hỗ trợ API từ các phiên bản cũ hơn) và Hồ sơ cốt lõi (vô hiệu hóa các API cũ đó). Những thứ như glBeginđơn giản là không hoạt động khi bạn sử dụng Hồ sơ cốt lõi nhưng sẽ khi bạn sử dụng Hồ sơ tương thích (là mặc định).

Là một vấn đề phức tạp nữa, một số triển khai OpenGL (như của Apple, trong số những người khác) sẽ chỉ kích hoạt các tính năng OpenGL mới hơn khi bạn đang sử dụng Cấu hình lõi. Điều này có nghĩa là bạn phải ngừng sử dụng các API cũ hơn để sử dụng các API mới hơn.

Sau đó, bạn kết thúc với một số tình huống rất khó hiểu cho hướng dẫn:

  1. Hướng dẫn này đã cũ và chỉ sử dụng các API không dùng nữa.
  2. Hướng dẫn này mới và được viết tốt và chỉ sử dụng API tương thích Core.
  3. Hướng dẫn này mới nhưng sai lầm khi cho rằng bạn đang làm việc với trình điều khiển cho phép tất cả các API ở chế độ Tương thích và tự do trộn lẫn cả API mới và API cũ.
  4. Hướng dẫn dành cho một phiên bản OpenGL khác như OpenGL | ES không hỗ trợ bất kỳ API cũ nào, ở bất kỳ phiên bản nào.

Những thứ như glBeginlà một phần của những gì đôi khi được gọi là API chế độ ngay lập tức. Điều này cũng rất khó hiểu vì không có chế độ được giữ lại trong OpenGL và "chế độ ngay lập tức" đã có một định nghĩa khác trong đồ họa. Sẽ tốt hơn nhiều nếu chỉ xem những API đó là API OpenGL 1.x vì chúng đã lỗi thời kể từ OpenGL 2.1.

API 1.x của OpenGL sẽ ngay lập tức gửi các đỉnh cho đường ống đồ họa trở lại trong những ngày cũ. Điều này hoạt động tốt khi tốc độ của phần cứng tạo ra các đỉnh gần bằng với tốc độ của CPU tạo ra dữ liệu đỉnh. OpenGL trở lại sau đó chỉ giảm tải tam giác raster và không nhiều thứ khác.

Ngày nay, GPU có thể nhai một số lượng lớn các đỉnh với tốc độ rất cao trong khi thực hiện chuyển đổi đỉnh và pixel tiên tiến và CPU chỉ đơn giản là không thể theo kịp từ xa. Trên hết, giao diện giữa CPU và GPU đã được thiết kế xoay quanh sự khác biệt về tốc độ này có nghĩa là thậm chí không thể gửi các đỉnh cho GPU một lần nữa.

Tất cả các trình điều khiển GL phải mô phỏng glBeginbằng cách phân bổ nội bộ bộ đệm đỉnh, đặt các đỉnh được gửi glVertexvào bộ đệm này và sau đó gửi toàn bộ bộ đệm đó trong một lệnh gọi rút ra khi glEndđược gọi. Chi phí hoạt động của các hàm này lớn hơn nhiều so với việc bạn tự cập nhật bộ đệm đỉnh, đó là lý do tại sao một số tài liệu sẽ (rất nhầm!) Gọi bộ đệm đỉnh là "tối ưu hóa" (không phải là tối ưu hóa; đó là cách duy nhất để thực sự nói chuyện với GPU).

Có nhiều API khác đã bị phản đối hoặc bị lỗi thời trong OpenGL trong những năm qua. Cái gọi là đường ống chức năng cố định là một phần khác như vậy. Một số tài liệu vẫn có thể sử dụng đường ống này hoặc trộn với đường ống lập trình. Đường ống chức năng cố định xuất phát từ thời xa xưa khi các card đồ họa được mã hóa cứng tất cả các phép toán được sử dụng để hiển thị cảnh 3D và API OpenGL bị giới hạn trong việc đặt một số giá trị cấu hình cho toán học đó. Ngày nay, phần cứng có rất ít toán học được mã hóa cứng và (giống như CPU ​​của bạn) chạy các chương trình do người dùng cung cấp (thường được gọi là shader).

Một lần nữa, các trình điều khiển phải mô phỏng API cũ vì các tính năng chức năng cố định đơn giản là không còn hiện diện trên phần cứng nữa. Điều này có nghĩa là trình điều khiển có một loạt các shader tương thích được nhúng trong nó để thực thi toán học cũ từ những ngày có chức năng cố định được sử dụng khi bạn không cung cấp các shader của riêng mình. Các chức năng OpenGL cũ sửa đổi trạng thái chức năng cố định cũ đó (như API chiếu sáng OpenGL cũ) thực sự đang sử dụng các tính năng OpenGL hiện đại như bộ đệm thống nhất để cung cấp các giá trị này cho trình đổ bóng tương thích của trình điều khiển.

Các trình điều khiển hỗ trợ khả năng tương thích phải thực hiện nhiều công việc hậu trường chỉ để tìm ra khi bạn sử dụng các tính năng lỗi thời này và đảm bảo rằng bạn có thể kết hợp chúng với các tính năng hiện đại một cách trơn tru, điều này làm tăng thêm chi phí và làm phức tạp trình điều khiển. Đây là một trong những lý do mà một số trình điều khiển buộc bạn phải kích hoạt Cấu hình lõi để có được các tính năng mới hơn; nó đơn giản hóa rất nhiều phần bên trong trình điều khiển của họ bằng cách không phải hỗ trợ cả API cũ và API mới được sử dụng đồng thời.

Rất nhiều tài liệu có thể khuyên bạn nên bắt đầu với các API cũ đơn giản vì tài khoản của chúng dễ bắt đầu hơn. Direct3D đã giải quyết vấn đề này cho người mới bắt đầu bằng cách cung cấp thư viện đồng hành ( Bộ công cụ DirectX ) cung cấp API vẽ đơn giản hơn và các trình tạo bóng được viết sẵn có thể được trộn tự do với việc sử dụng Direct3D 11 thô khi chuyên môn của bạn phát triển. Không may, cộng đồng OpenGL rộng hơn hầu như bị mắc kẹt với Hồ sơ tương thích cho người mới bắt đầu, thật không may, một vấn đề nữa là có những hệ thống không cho phép bạn trộn API OpenGL cũ với các API mới hơn. Có các thư viện và công cụ không chính thức để hiển thị đơn giản hơn trên OpenGL mới với các mức độ khác nhau của các tính năng và trường hợp sử dụng mục tiêu và ngôn ngữ ( MonoGame đối với người dùng .NET chẳng hạn), nhưng không có gì chính thức được chứng thực hoặc đồng ý rộng rãi.

Tài liệu bạn tìm thấy thậm chí có thể không dành cho OpenGL nhưng có thể dành cho một trong các API tương tự khác. OpenGL | ES 1.x có kết xuất chức năng cố định nhưng không có API OpenGL 1.x để gửi đỉnh. OpenGL | ES 2.x + và WebGL 1+ hoàn toàn không có bất kỳ tính năng chức năng cố định nào và không có chế độ tương thích ngược cho các API đó.

Các API này trông rất giống với OpenGL chính; chúng không hoàn toàn tương thích, nhưng có những phần mở rộng chính thức cho OpenGL mà một số trình điều khiển (không phải tất cả) hỗ trợ để tương thích với OpenGL | ES (mà WebGL dựa trên). Bởi vì mọi thứ không đủ khó hiểu trước đây.


4
+1 câu trả lời tuyệt vời! Nếu bạn có thể đề cập đến một vài thư viện và công cụ không chính thức để hiển thị đơn giản trên OpenGL mới thì sẽ rất tuyệt :)
Mehrdad

2
Câu trả lời rực rỡ. Tôi đã gặp rắc rối tương tự với DirectX trở lại trong ngày - đơn giản hơn nhiều so với OpenGL, nhưng bước nhảy vọt từ chế độ được giữ lại / ngay lập tức sang trình đổ bóng là rất lớn. May mắn thay, tài liệu này đã giúp ích rất nhiều (không giống như OpenGL, ít nhất là đối với tôi), nhưng khởi đầu của "làm thế nào để tôi sáng" thật điên rồ: D
Luaan

Tôi là tác giả của opengl-tutorial.org và tôi đồng ý với Sean. API phát triển theo cách này chủ yếu vì lý do hiệu suất.
Calvin1602

Thông tin rất tốt về chủ đề ..
reynmar 27/1/2015

1
@Mehrdad: Tôi không nhớ bất kỳ cái gì trên đỉnh đầu; có những thư viện như SDL2 hoặc SFML có thêm kết xuất 2D đơn giản, thư viện đồ thị cảnh khác nhau, MonoGame cho C #, v.v., nhưng tôi thực sự không biết bất cứ điều gì tương đương trực tiếp với Direct TK mà tôi nghĩ về nó. Sẽ chỉnh sửa bài kể từ khi nói "nhiều" có thể là một lời nói dối béo lớn. :)
Sean Middleditch

9

Sự khác biệt chính là cách cập nhật các chiến lược. Chế độ ngay lập tức được sử dụng trong hướng dẫn đầu tiên:

glBegin(GL_QUADS);
    glColor3f(1, 0, 0); glVertex3f(0, 0, 0);
    glColor3f(1, 1, 0); glVertex3f(100, 0, 0);
    glColor3f(1, 0, 1); glVertex3f(100, 100, 0);
    glColor3f(1, 1, 1); glVertex3f(0, 100, 0);
glEnd();

Đã lỗi thời và không được hỗ trợ trên các phiên bản mới hơn.

Sử dụng bộ đệm và bộ tạo bóng đỉnh là phương pháp kết xuất hiện tại với OpenGL. Nó có vẻ phức tạp hơn, nhưng nó thực hiện tốt hơn đáng kể. Ngoài ra, khi bạn có mã hỗ trợ của mình gói các công cụ OpenGL, phần lớn sự khác biệt sẽ được trừu tượng hóa.


2

Chỉ cần thêm một số bối cảnh cho các câu trả lời tuyệt vời khác.

Chế độ ngay lập tức như được mô tả trong liên kết đầu tiên, như những người khác đã nói, mã kế thừa từ các phiên bản sớm nhất của OpenGL (1.1). Nó đã được sử dụng trở lại khi GPU ít hơn các raster tam giác và ý tưởng về các đường ống lập trình không tồn tại. Nếu bạn xem mã nguồn của một số trò chơi được tăng tốc phần cứng sớm như GLQuake và Quake 2 chẳng hạn, bạn sẽ thấy chế độ ngay lập tức được sử dụng. Nói một cách đơn giản, CPU sẽ gửi hướng dẫn cho các đỉnh một lần tới GPU để bắt đầu vẽ các hình tam giác trên màn hình. Đối với bản ghi, GL_QUADS có kết quả tương tự GL_TRIANGLES, ngoại trừ GPU phải biến các hình tứ giác đó thành hình tam giác khi đang di chuyển.

OpenGL hiện đại (3.2+) có cách tiếp cận khác. Nó đệm dữ liệu đỉnh vào bộ nhớ GPU để truy cập nhanh và sau đó bạn có thể gửi hướng dẫn vẽ bằng cách sử dụng glDrawArrays hoặc glDrawElements. Bạn cũng có đường ống lập trình (glUseProgram) cho phép bạn tùy chỉnh cách vị trí GPU và tô màu các đỉnh.

Có một vài lý do tại sao chế độ ngay lập tức bị phản đối, lý do chính là hiệu suất. Như Sean đã nói trong câu trả lời của mình, ngày nay GPU có thể xử lý dữ liệu nhanh hơn CPU có thể tải lên, do đó bạn sẽ làm tắc nghẽn hiệu suất GPU. Có một chi phí nhỏ liên quan đến mỗi cuộc gọi tới OpenGL mà bạn thực hiện, đó là rất nhỏ nhưng khi bạn thực hiện hàng chục nghìn cuộc gọi cho mỗi khung thì nó bắt đầu chồng lên. Nói một cách đơn giản, để vẽ một mô hình kết cấu bằng chế độ ngay lập tức, bạn cần ít nhất 2 cuộc gọi cho mỗi đỉnh (glTexCoord2f và glVertex3f) trên mỗi khung. Với OpenGL hiện đại, bạn sử dụng một vài cuộc gọi khi bắt đầu để đệm dữ liệu, sau đó bạn có thể vẽ toàn bộ mô hình, bất kể nó chứa bao nhiêu đỉnh, chỉ sử dụng một vài cuộc gọi để liên kết đối tượng mảng đỉnh, bật một số con trỏ thuộc tính và sau đó một cuộc gọi đến glDrawElements hoặc glDrawArrays.

Kỹ thuật nào đúng? Vâng, điều đó phụ thuộc vào những gì bạn đang cố gắng làm. Một trò chơi 2D đơn giản không yêu cầu bất kỳ kỹ thuật xử lý hậu kỳ hay shader nào sẽ hoạt động tốt khi sử dụng chế độ ngay lập tức và có thể sẽ dễ dàng hơn để viết mã. Tuy nhiên, một trò chơi 3D hiện đại hơn sẽ thực sự gặp khó khăn và nếu bạn dự định học GLSL (ngôn ngữ đổ bóng), thì chắc chắn sẽ học được kỹ thuật hiện đại này.

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.