Về cơ bản, bạn đang gặp phải tình huống khiến NVIDIA Cg trở thành một phần mềm hấp dẫn như vậy (ngoài thực tế là nó không hỗ trợ GL | ES, mà bạn nói bạn đang sử dụng).
Cũng lưu ý rằng bạn thực sự không nên sử dụng glGetAttribLocation. Chức năng đó là juju tồi từ những ngày đầu của GLSL trước khi những người phụ trách GL thực sự bắt đầu tìm hiểu làm thế nào một ngôn ngữ tạo bóng tốt nên hoạt động. Nó không bị phản đối vì nó thường được sử dụng, nhưng nói chung, thích glBindAttibLocation hoặc phần mở rộng vị trí thuộc tính rõ ràng (cốt lõi trong GL 3.3+).
Đối phó với sự khác biệt trong các ngôn ngữ đổ bóng là phần khó nhất trong phần mềm chuyển giữa GL và D3D. Các vấn đề API mà bạn gặp phải liên quan đến định nghĩa bố cục đỉnh cũng có thể được xem như là một vấn đề về ngôn ngữ đổ bóng, vì các phiên bản GLSL trước 3.30 không hỗ trợ vị trí thuộc tính rõ ràng (tương tự về ngữ nghĩa thuộc tính trong các phiên bản HLSL) và các phiên bản GLSL trước đây 4.10 iirc không hỗ trợ các ràng buộc thống nhất rõ ràng.
Cách tiếp cận "tốt nhất" là có một thư viện ngôn ngữ và định dạng dữ liệu tô bóng mức cao, đóng gói các gói đổ bóng của bạn. KHÔNG chỉ đơn giản cung cấp một bó GLSL / HLSL thô cho một lớp Shader mỏng và mong muốn có thể đưa ra bất kỳ loại API lành mạnh nào.
Thay vào đó, đặt shader của bạn ra thành một tập tin. Gói chúng trong một chút dữ liệu meta. Bạn có thể sử dụng XML và viết các gói shader như:
<shader name="bloom">
<profile type="glsl" version="1.30">
<source type="vertex"><![CDATA[
glsl vertex shader code goes here
]]></source>
<source type="fragment"><![CDATA[
glsl fragment shader code goes here
]]></source>
</profile>
<profile type="hlsl" version="sm3">
<source type="fx"><![CDATA[
hlsl effects code goes here
you could also split up the source elements for hlsl
]]></source>
</profile>
</shader>
Viết một trình phân tích cú pháp tối thiểu cho việc đó là chuyện nhỏ (chỉ cần sử dụng TinyXML chẳng hạn). Hãy để thư viện shader của bạn tải lên gói đó, chọn cấu hình phù hợp cho trình kết xuất mục tiêu hiện tại của bạn và biên dịch các shader.
Cũng lưu ý rằng nếu bạn thích, bạn có thể giữ nguồn bên ngoài theo định nghĩa shader, nhưng vẫn có tệp. Chỉ cần đặt tên tệp thay vì nguồn vào các thành phần nguồn. Điều này có thể có lợi nếu bạn có kế hoạch biên dịch trước các shader.
Phần khó bây giờ tất nhiên là xử lý GLSL và những thiếu sót của nó. Vấn đề là bạn cần liên kết các vị trí thuộc tính với một cái gì đó giống với ngữ nghĩa của HLSL. Điều này có thể được thực hiện bằng cách xác định các ngữ nghĩa đó trong API của bạn và sau đó sử dụng glBindAttribLocation trước khi liên kết cấu hình GLSL. Khung gói shader của bạn có thể xử lý việc này một cách rõ ràng, hoàn toàn không cần API đồ họa của bạn để lộ chi tiết.
Bạn có thể làm điều đó bằng cách mở rộng định dạng XML ở trên với một số thành phần mới trong cấu hình GLSL để chỉ định rõ ràng các vị trí thuộc tính, ví dụ:
<shader name="bloom">
<profile type="glsl" version="1.30">
<attrib name="inPosition" semantic="POSITION"/>
<attrib name="inColor" semantic="COLOR0"/>
<source type="vertex"><![CDATA[
#version 150
in vec4 inPosition;
in vec4 inColor;
out vec4 vColor;
void main() {
vColor = inColor;
gl_Position = position;
}
]]></source>
</profile>
</shader>
Mã gói shader của bạn sẽ đọc tất cả các phần tử attrib trong XML, lấy tên và ngữ nghĩa từ chúng, tra cứu chỉ mục thuộc tính được xác định trước cho từng ngữ nghĩa và sau đó tự động gọi glBindAttribLocation cho bạn khi liên kết trình đổ bóng.
Kết quả cuối cùng là API của bạn bây giờ có thể cảm thấy đẹp hơn nhiều so với mã GL cũ của bạn có thể đã từng nhìn và thậm chí sạch hơn một chút so với D3D11 sẽ cho phép:
// simple example, easily improved
VertexLayout layout = api->createLayout();
layout.bind(gfx::POSITION, buffer0, gfx::FLOATx4, sizeof(Vertex), offsetof(Vertex, position));
layout.bind(gfx::COLOR0, buffer0, gfx::UBYTEx4, sizeof(Vertex), offsetof(Vertex, color));
Cũng lưu ý rằng bạn không thực sự cần định dạng gói shader. Nếu bạn muốn đơn giản hóa mọi thứ, bạn chỉ cần có một loại hàm loadShader (const char * name) tự động lấy các tệp name.vs và name.fs GLSL trong chế độ GL và biên dịch và liên kết chúng. Tuy nhiên, bạn hoàn toàn sẽ muốn siêu dữ liệu thuộc tính đó. Trong trường hợp đơn giản, bạn có thể tăng mã GLSL của mình bằng các nhận xét đặc biệt dễ phân tích, như:
#version 150
/// ATTRIB(inPosition,POSITION)
in vec4 inPosition;
/// ATTRIB(inColor,COLOR0)
in vec4 inColor;
out vec4 vColor
void main() {
vColor = inColor;
gl_Position = inPosition;
}
Bạn có thể thấy lạ mắt như bạn thấy thoải mái khi phân tích nhận xét. Hơn một vài công cụ chuyên nghiệp sẽ đi xa đến mức tạo ra các phần mở rộng ngôn ngữ nhỏ mà chúng phân tích và sửa đổi, thậm chí, chẳng hạn như chỉ thêm hoàn toàn các khai báo ngữ nghĩa theo kiểu HLSL. Nếu kiến thức về phân tích cú pháp của bạn mạnh mẽ, bạn sẽ có thể tin cậy tìm thấy các khai báo mở rộng đó, trích xuất thông tin bổ sung và sau đó thay thế văn bản bằng mã tương thích GLSL.
Bất kể bạn làm điều đó như thế nào, phiên bản ngắn là để tăng GLSL của bạn với thông tin ngữ nghĩa thuộc tính bị thiếu và có thỏa thuận trừu tượng trình tải shader của bạn với việc gọi glBindAttribLocation để sửa chữa mọi thứ và làm cho chúng giống với các phiên bản GLSL và HLSL hiện đại dễ dàng và hiệu quả.