Có một loạt các cách tiếp cận, nhưng không có cách nào là hoàn hảo.
Có thể chia sẻ mã bằng cách sử dụng glAttachShader
để kết hợp các trình tạo bóng, nhưng điều này không thể chia sẻ những thứ như khai báo cấu trúc hoặc #define
hằng số -d. Nó không hoạt động để chia sẻ chức năng.
Một số người thích sử dụng mảng các chuỗi được truyền vào glShaderSource
như một cách để bổ sung các định nghĩa phổ biến trước mã của bạn, nhưng điều này có một số nhược điểm:
- Việc kiểm soát những gì cần được đưa vào trong shader sẽ khó hơn (bạn cần một hệ thống riêng cho việc này.)
- Điều đó có nghĩa là tác giả shader không thể chỉ định GLSL
#version
, do tuyên bố sau trong thông số GLSL:
Lệnh #version phải xảy ra trong một shader trước bất cứ thứ gì khác, ngoại trừ các bình luận và khoảng trắng.
Do tuyên bố này, glShaderSource
không thể được sử dụng để thêm vào văn bản trước khi #version
khai báo. Điều này có nghĩa là #version
dòng cần phải được bao gồm trong các glShaderSource
đối số của bạn , điều đó có nghĩa là giao diện trình biên dịch GLSL của bạn cần được thông báo bằng cách nào đó phiên bản GLSL dự kiến sẽ được sử dụng. Ngoài ra, không chỉ định a #version
sẽ làm cho trình biên dịch GLSL mặc định sử dụng GLSL phiên bản 1.10. Nếu bạn muốn để các tác giả shader chỉ định #version
bên trong tập lệnh theo cách tiêu chuẩn, thì bạn cần phải chèn bằng cách nào đó #include
sau #version
câu lệnh. Điều này có thể được thực hiện bằng cách phân tích rõ ràng trình tạo bóng GLSL để tìm #version
chuỗi (nếu có) và tạo các vùi của bạn sau chuỗi đó, nhưng có quyền truy cập vào một#include
Chỉ thị có thể được ưu tiên để kiểm soát dễ dàng hơn khi những vùi đó cần được thực hiện. Mặt khác, vì GLSL bỏ qua các nhận xét trước #version
dòng, bạn có thể thêm siêu dữ liệu để bao gồm trong các nhận xét ở đầu tệp của mình (yuck.)
Câu hỏi bây giờ là: Có một giải pháp tiêu chuẩn cho #include
, hoặc bạn cần cuộn phần mở rộng tiền xử lý của riêng mình?
Có GL_ARB_shading_language_include
phần mở rộng, nhưng nó có một số nhược điểm:
- Nó chỉ được hỗ trợ bởi NVIDIA ( http://delphigl.de/glcapsviewer/listreports2.php?listreportsbyextension=GL_ARB_shading_lingu_include )
- Nó hoạt động bằng cách chỉ định các chuỗi bao gồm trước thời hạn. Do đó, trước khi biên dịch, bạn cần xác định rằng chuỗi
"/buffers.glsl"
(như được sử dụng trong #include "/buffers.glsl"
) tương ứng với nội dung của tệp buffer.glsl
(mà bạn đã tải trước đó).
- Như bạn có thể nhận thấy ở điểm (2), các đường dẫn của bạn cần bắt đầu bằng
"/"
, như các đường dẫn tuyệt đối theo kiểu Linux. Ký hiệu này thường không quen thuộc với các lập trình viên C và có nghĩa là bạn không thể chỉ định các đường dẫn tương đối.
Một thiết kế phổ biến là thực hiện #include
cơ chế của riêng bạn , nhưng điều này có thể khó khăn vì bạn cũng cần phân tích (và đánh giá) các hướng dẫn tiền xử lý khác như #if
để xử lý việc biên dịch có điều kiện (như bộ bảo vệ tiêu đề).
Nếu bạn tự thực hiện #include
, bạn cũng có một số quyền tự do trong cách bạn muốn thực hiện nó:
- Bạn có thể vượt qua các chuỗi trước thời hạn (như
GL_ARB_shading_language_include
).
- Bạn có thể chỉ định một cuộc gọi lại bao gồm (điều này được thực hiện bởi thư viện D3DCompiler của DirectX.)
- Bạn có thể thực hiện một hệ thống luôn đọc trực tiếp từ hệ thống tệp, như được thực hiện trong các ứng dụng C điển hình.
Để đơn giản hóa, bạn có thể tự động chèn các bộ bảo vệ tiêu đề cho mỗi bao gồm trong lớp tiền xử lý của mình, vì vậy lớp bộ xử lý của bạn trông như sau:
if (#include and not_included_yet) include_file();
(Tín dụng cho Trent Reed đã cho tôi thấy kỹ thuật trên.)
Tóm lại , không tồn tại giải pháp tự động, tiêu chuẩn và đơn giản. Trong một giải pháp trong tương lai, bạn có thể sử dụng một số giao diện OpenGL SPIR-V, trong trường hợp đó, trình biên dịch GLSL sang SPIR-V có thể nằm ngoài API GL. Có trình biên dịch bên ngoài thời gian chạy OpenGL đơn giản hóa rất nhiều việc thực hiện những thứ như #include
vì đây là nơi thích hợp hơn để giao tiếp với hệ thống tệp. Tôi tin rằng phương pháp phổ biến hiện nay là chỉ thực hiện một bộ tiền xử lý tùy chỉnh hoạt động theo cách mà bất kỳ lập trình viên C nào cũng nên làm quen.