Trong bài viết trước của tôi mô tả thiết kế của Perl 5 và tính phù hợp của nó như một "ngôn ngữ keo", tôi đã đề cập rằng trước đây tôi đã viết các ràng buộc OpenGL cho Bash. Đây có lẽ là một tuyên bố quá khó tin để đưa ra mà không có bằng chứng nào đó, vì vậy tôi đã quay trở lại các góc đầy bụi của ổ cứng, đào nó ra, làm mới nó một chút, cải thiện hỗ trợ phông chữ, viết tài liệu và xuất bản nó trên trang web của tôi và trên GitHub. (Bạn sẽ cần một hệ thống có hỗ trợ cả Bash và OpenGL để tận mắt trải nghiệm nó, nhưng đây là một video.)
Vì vậy, bây giờ lời thú nhận của tôi:Đồ họa Perl trong bảng điều khiển DeLorean mà tôi đã mô tả trong DeLorean của tôi chạy Perl chia sẻ lịch sử với dự án OpenGL for Bash của tôi. Trong một bước ngoặt thú vị và đầy mỉa mai, tôi đã bắt đầu dự án cách đây 13 năm sau khi chứng kiến Frozen Bubble và cảm thấy khó hiểu về kỹ thuật của tôi rằng ai đó đã viết một trò chơi điện tử thời gian thực ở Perl. Hồi đó, ngôn ngữ chính của tôi là C ++ và tôi đang học OpenGL cho mục đích trò chơi điện tử. Tôi đã tuyên bố với bạn bè của mình rằng điều tồi tệ duy nhất sẽ là nếu nó là 3D và được viết bằng Bash. Sau khi nói to ý tưởng, nó tiếp tục thôi thúc tôi, và cuối cùng tôi quyết định thử một lần nữa về sự "kinh khủng" của việc sử dụng Perl cho đồ họa thời gian thực.
Mở rộng Bash
Cách trực tiếp nhất để thêm hỗ trợ OpenGL vào Bash là thay đổi mã nguồn và thêm từng hàm OpenGL dưới dạng "nội trang" của trình bao; tuy nhiên, những người duy nhất có thể trải nghiệm nó sẽ là những người sẵn sàng cài đặt phiên bản Bash tùy chỉnh và có lẽ không ai muốn làm điều đó. Nó cũng có vẻ không đúng với tinh thần làm những việc "theo cách Bash".
Sau đó, tôi nảy ra ý tưởng về một máy khách-máy chủ, trong đó mỗi hàm OpenGL sẽ được cài đặt trong đường dẫn và gọi một máy khách sẽ kết nối với máy chủ OpenGL để gửi một thông báo thực thi chức năng đó. Đó hẳn là một giải pháp thú vị "khủng khiếp", nhưng cho dù tôi đã tối ưu hóa mọi thứ chăm chỉ đến mức nào, thì vẫn còn quá chậm để tôi nói rằng tôi đã thành công với mục tiêu tổng thể.
Cuối cùng, tôi đã quyết định một thiết kế trong đó tôi sẽ có một quy trình "OpenGL Interpreter" sẽ đọc các lệnh OpenGL trên tiêu chuẩn và ghi bất kỳ sự kiện đầu vào của người dùng nào thành tiêu chuẩn. Sau đó, một tập lệnh Bash có thể khởi động trình thông dịch này được kết nối với đường ống và mỗi lệnh OpenGL có thể là một hàm Bash ghi vào đường ống. Trong khi làm việc đó, tôi quyết định ném vào túi toàn bộ thủ thuật hiệu quả và viết trình thông dịch bằng C đơn giản với một số bảng băm được biên dịch tĩnh và cây đỏ-đen.
Chế độ tức thì OpenGL
Chi tiết OpenGL là nơi dự án này bắt đầu giao với dự án bảng điều khiển DeLorean. Trước tiên, tôi sẽ lùi lại một bước và tóm tắt lại API OpenGL.
OpenGL là tất cả về việc viết một chương trình lập kế hoạch đồ họa của nó dưới dạng tọa độ và kết cấu 3D, sau đó chuyển dữ liệu đó đến cạc đồ họa để hiển thị trên màn hình 2D. Để thực sự đơn giản, có một tập hợp toán học bạn sử dụng để mô tả vùng màn hình được sơn và một tập hợp toán học để mô tả màu sắc của từng pixel trong vùng đó.
Thao tác phổ biến nhất là mô tả ba góc của hình tam giác trong không gian 3D ảo, hãy để OpenGL tìm ra vị trí đặt trên màn hình 2D, sau đó yêu cầu nó kéo dài hình ảnh 2D trên khu vực đó, cũng có thể được kết hợp với một hình ảnh khác hoặc thay đổi bằng một số tính toán độ sáng. Vòng lặp chính của chương trình tạo ra một khung video bằng cách xóa bộ đệm, vẽ mọi đa giác có thể nhìn thấy và gửi nó ra màn hình. Nếu bạn thực hiện toàn bộ tình huống đóng thế này trong 16 mili giây hoặc ít hơn, bạn có thể duy trì 60 khung hình / giây và có đồ họa mượt mà đẹp mắt.
Tôi không thể nói với cơ quan có thẩm quyền về nguồn gốc của API OpenGL, nhưng có vẻ khá rõ ràng rằng nó được xây dựng dựa trên ý tưởng phát trực tuyến, có thể là để tương thích với giao thức hiển thị X11, nhưng cũng chỉ vì đó là một ý tưởng hay. Vì vậy, hầu hết các hàm OpenGL không có giá trị trả về, trái ngược với các API khác trong đó mỗi lệnh gọi hàm trả về một trạng thái cho bạn biết kết quả của hoạt động. Nếu bạn hình dung một hàm OpenGL chẳng hạn như glVertex3f(1,2,3)
như một câu lệnh in viết ba số trên một đường ống, bạn đã có một ý tưởng khá hay về cách nó hoạt động đằng sau hậu trường. Trên thực tế, đối với các ràng buộc Bash, tôi viết theo nghĩa đen là glVertex 1 2 3
qua đường ống khi glVertex 1 2 3
được thực thi. Tập lệnh Bash chạy mù mịt, không có bất kỳ ý tưởng nào về việc liệu các lệnh đồ họa của nó có đang làm bất cứ điều gì như dự định hay không.
Đã có một số bản sửa đổi lớn đối với API OpenGL trong những năm qua và mọi người viết toàn bộ các chương trong sách của họ về chế độ "giữ lại" so với "ngay lập tức", nhưng tất cả chỉ tóm gọn lại thành hai khái niệm:
- Việc gửi lại tất cả dữ liệu đó qua đường ống mỗi khung rất chậm, vì vậy hãy lưu một số dữ liệu vào bộ nhớ cache ở đầu bên kia.
- Chúng tôi không bao giờ có thể đáp ứng nhu cầu của mọi người với phép toán tích hợp sẵn, vì vậy, hãy cung cấp cho mọi người một ngôn ngữ để mô tả phép toán tùy chỉnh của riêng họ.
Điều đó nói rằng, trong khi bộ nhớ đệm và toán học tùy chỉnh được cung cấp bởi các API OpenGL mới hiệu quả hơn nhiều và chắc chắn cần thiết cho các trò chơi điện tử hàng đầu, chúng cũng nỗ lực nhiều hơn để thiết lập và làm việc với (và học hỏi) và có thể gây hại nhiều hơn tốt cho những người có sở thích. Chúng cũng không cần thiết trừ khi bạn đang thực hiện các hiệu ứng đồ họa nâng cao hoặc các mô hình chi tiết cao.
Vì vậy, mặc dù liên kết Bash OpenGL chỉ xử lý API "không được dùng nữa", tôi vẫn khuyến khích mọi người xem xét nó cho mục đích giáo dục và mày mò.
Danh sách hiển thị
May mắn thay, API OpenGL ban đầu có một số cơ chế lưu vào bộ nhớ đệm và chúng dễ sử dụng. Chúng hoạt động gần như theo trình tự:
"Này OpenGL, tôi muốn bạn tạo đối tượng 37 trên đầu từ xa"
"Đây là một số dữ liệu mô tả đối tượng 37"
"Sử dụng đối tượng 37 trong các bước kết xuất tiếp theo"
Để dễ dàng hơn, các liên kết Bash cho phép bạn sử dụng tên thay vì số.
Loại đối tượng chính đầu tiên là kết cấu, nơi bạn tải một hình ảnh 2D vào cạc đồ họa và sau đó "vẽ" các đa giác với nó. Thứ hai là "danh sách hiển thị", nơi bạn ghi lại một chuỗi các lệnh OpenGL, sau đó phát lại chúng như thể chúng là một lệnh OpenGL duy nhất.
Danh sách hiển thị hoạt động tốt cho việc tạo mẫu đặc biệt. Bạn có thể vẽ ra một số điểm mô tả mô hình 3D (hoặc 2D) của mình bằng các lệnh đỉnh đơn giản, sau đó ghi lại chuỗi các điểm đó dưới dạng danh sách hiển thị và bây giờ bạn có thể hiển thị mô hình đó bằng một lệnh duy nhất.
Để biết ví dụ về sức mạnh này, hãy xem Robot.sh trong thư mục Ví dụ. Nó tạo một danh sách hiển thị cho mỗi đoạn cơ thể của robot khi khởi động và trong khi chạy, nó chỉ phát ra 58 dòng văn bản trên mỗi khung hình để kết xuất robot. Bash có thể dễ dàng tạo ra 58 dòng văn bản trong 16 mili giây (thậm chí có thể là 12 năm trước), vì vậy bản demo có thể chạy ở tốc độ tối đa trên phần cứng thông thường.
Danh sách hiển thị là thủ thuật chính mà tôi đã chuyển sang đồ họa Perl mà tôi đang sử dụng trong phần mềm bảng điều khiển DeLorean. Các lệnh gọi hàm Perl hơi tốn kém so với C, đặc biệt là đối với một API nặng về hàm như OpenGL 1.4, nhưng bằng cách kết hợp mọi thứ vào danh sách hiển thị, Perl không có nhiều việc phải làm trên mỗi khung hình video. Nếu tôi không học được mẹo này cho dự án Bash, thì dự án Perl sẽ không thể thành công như mong đợi.
Tại sao tôi không xuất bản điều này 12 năm trước?
Mặc dù tôi có thể tạo một số bản demo hoạt hình lạ mắt trong Bash, nhưng mục tiêu thực sự của tôi là tạo ra một trò chơi hoàn chỉnh với nó. Tôi đang nhắm đến một bản sao của trò chơi mô phỏng chuyến bay cũ Terminal Velocity và tôi đã tiến xa tới mức một con tàu vũ trụ có thể bay qua một cánh đồng hình khối (xem Flight.sh trong Ví dụ), nhưng vấn đề tiếp theo là phát hiện va chạm. Các nguyên thủy có sẵn trong Bash là "mảng kết hợp" toàn cục của các tên biến và biến mảng (mặc dù phiên bản 4 hiện có các biến mảng liên kết) và tất cả phép toán là số nguyên. Trong khi tôi có thể thực hiện các phép quay ma trận 3D trong phép toán số nguyên điểm cố định, việc triển khai phát hiện va chạm trông giống như một chướng ngại vật quá lớn. Tôi cũng không hài lòng với API phông chữ của mình. Trong khi cân nhắc các giải pháp cho những vấn đề này, dự án dần dần bị loại khỏi danh sách của tôi.
Đây là nơi tôi kết luận (như tôi đã viết), "Bash là một ngôn ngữ keo kinh khủng." Mặc dù dự án này bắt đầu và kết thúc như một trò đùa (hoặc công cụ giáo dục, có thể), phong cách chương trình tương tự trong Perl hóa ra lại khá hữu ích cho các ứng dụng thực tế. Nếu tôi xem xét Frozen Bubble một cách nghiêm túc hơn vào 13 năm trước, tôi có thể đã có thể bắt đầu tốt hơn với Perl, ngôn ngữ yêu thích của tôi bây giờ.
Tôi không có bất kỳ kế hoạch nào để nâng cao dự án này ngoài việc sửa lỗi, nhưng tôi nghĩ rằng nó ít nhất có thể hữu ích cho những người muốn tìm hiểu các khái niệm về OpenGL hoặc những người có nhiều thời gian rảnh và mong muốn viết một Doom sao chép trong M4.
Vui thích! Và hãy vui vẻ chuyển tiếp liên kết này, nếu không, sẽ không ai tin bạn.