Mức độ trừu tượng chính xác cho một thành phần kết xuất 3d?


8

Tôi đã thấy rất nhiều câu hỏi xung quanh khu vực này nhưng không phải câu hỏi chính xác này nên xin lỗi nếu đây là một bản sao.

Tôi đang làm một trò chơi 3d nhỏ. Thành thật mà nói, đó chỉ là một dự án sở thích nhỏ và có khả năng sẽ không trở thành một trò chơi thực tế, tôi sẽ rất vui khi tạo một bản demo đồ họa đẹp và tìm hiểu về kết xuất 3d và thiết kế c ++.

Mục đích của tôi là sử dụng direct3d9 để kết xuất vì tôi có một chút kinh nghiệm về nó và dường như nó đáp ứng yêu cầu của tôi. Tuy nhiên nếu tôi đã học được một điều với tư cách là một lập trình viên thì hãy hỏi " có lý do nào có thể hiểu được rằng thành phần này có thể được thay thế bằng một hàm ý khác " và nếu câu trả lời là có thì tôi cần thiết kế một sự trừu tượng và giao diện phù hợp với thành phần đó . Vì vậy, mặc dù tôi có ý định thực hiện d3d9, tôi cần thiết kế giao diện 3d có thể được triển khai cho d3d11, opengl ...

Câu hỏi của tôi là mức độ nào là tốt nhất để làm điều này? Tôi nghĩ rằng một giao diện có khả năng tạo và vẽ sau này

  • Bộ đệm Vertex và bộ đệm chỉ mục
  • Hoạ tiết
  • "Shader" của Vertex và Pixel
  • Một số đại diện của trạng thái vẽ (chế độ hòa trộn, v.v ...)

Nói cách khác, một giao diện cấp độ khá thấp, nơi mã của tôi để vẽ, ví dụ như một mô hình hoạt hình sẽ sử dụng giao diện để có được bộ đệm đỉnh trừu tượng, v.v. Tôi lo lắng mặc dù nó quá thấp để trừu tượng hóa tất cả các chức năng tôi cần một cách hiệu quả.

Cách khác là làm điều này ở mức cao hơn, nơi giao diện có thể vẽ các đối tượng, hình động, cảnh quan, v.v. và thực hiện chúng cho từng hệ thống. Điều này có vẻ như nhiều công việc hơn, nhưng tôi đoán linh hoạt hơn.

Vì vậy, đó thực sự là câu hỏi của tôi, khi trừu tượng hóa hệ thống vẽ, mức độ giao diện nào hoạt động tốt nhất?


Nó có thể không trả lời hoàn toàn câu hỏi của bạn, nhưng tôi khuyên bạn nên xem các nguồn của các công cụ DirectX + OpenGL như OGRE và Irrlicht để xem họ đã trừu tượng hóa nó như thế nào.
Vịt Cộng sản

Câu trả lời:


8

Xin lỗi về độ dài của phản ứng này!

"Có bất kỳ lý do có thể hiểu được rằng thành phần này có thể được thay thế bằng một hàm ý khác" và nếu câu trả lời là có thì tôi cần phải thiết kế một sự trừu tượng và giao diện thích hợp cho thành phần đó.

"Bất kỳ lý do có thể hiểu được" là một lập trường quá cực đoan. Hầu như mọi chức năng đều có thể được tham số hóa theo một cách nào đó, mọi mô-đun được đưa ra một số chiến lược hoặc chính sách khác nhau và mọi điều chỉnh không đổi. Nếu bạn cung cấp một mức độ trừu tượng cao hơn cho mỗi thứ này thì cuối cùng bạn sẽ viết gấp 2 lần mã mà không thu được ngay lập tức, chỉ là mức tăng tiềm năng sau này nếu bạn yêu cầu sự linh hoạt đó.

Nếu bạn đi theo con đường cung cấp giao diện để mỗi thứ này có thể là mặt tiền cho một trong một số tùy chọn khác nhau, thì bạn phải quyết định cách bạn cung cấp các tùy chọn khác nhau này, bao gồm cả mặc định và khi bạn thử và làm cho nó đơn giản vì đối tượng ban đầu là bạn thường kết thúc với toàn bộ lớp bổ sung ở trên cùng. Điều này có thể (và không) trở nên vô lý trong rất nhiều phần mềm 'doanh nghiệp' trong thế giới thực - tôi có thể khiêm tốn đề nghị đọc tác phẩm châm biếm này, điều đáng buồn xảy ra quá gần nhà trong nhiều trường hợp: Tại sao tôi ghét Khung .

Lập luận lớn nhất chống lại điều này là sự phức tạp. Hãy giả sử (có thể không chính xác) rằng một giao diện tốt tồn tại có thể áp dụng tốt như nhau cho hai API kết xuất 3D và ai đó có thể mô tả điều này cho bạn trong câu trả lời ở đây. Sau đó, bạn sẽ làm việc với giao diện này, do tính chất đa mục đích của nó, chắc chắn bao gồm các yếu tố mà DX9 không sử dụng - ví dụ: rất nhiều tiện ích mở rộng API có sẵn trong OpenGL nhưng không có trong DirectX 9. Đây là một sự phức tạp thêm vào sẽ làm cho mã của bạn khó hiểu và duy trì hơn, và làm chậm quá trình học tập của bạn. Nó đủ tệ khi sử dụng một thư viện đa nền tảng hiện có như SDL, bởi vì mọi người mãi mãi gặp phải các chức năng và hạn chế kỳ lạ tồn tại hoàn toàn để sửa một cái gì đó trên một nền tảng, nhưng bạn d không chỉ phải tìm hiểu lý do tại sao một phần của giao diện ở đó mà còn làm thế nào nó có thể hoặc không thể tương tác với các phần mà bạn hiện đang viết. ví dụ. Cuối cùng, bạn sẽ viết các đối tượng trừu tượng gói gọn các khía cạnh của các tiện ích mở rộng OpenGL cần thiết trong mã kết xuất cốt lõi của bạn, mặc dù bạn chưa có cách nào sử dụng chúng.

Tuy nhiên, trong tương lai, một khi bạn thành thạo với cách hệ thống DX9 hoạt động, bạn có thể xem xét cách hệ thống OpenGL hoạt động. Sau đó, bạn sẽ thấy cách bạn cần điều chỉnh hệ thống hiện tại của mình để làm cho cả hai phần hoạt động và sẽ làm như vậy, với sự tồn tại tiện dụng của đường dẫn mã DX9 hiện tại của bạn như một thử nghiệm đơn vị hiệu quả để đảm bảo tái cấu trúc của bạn là chính xác.

Bạn thật may mắn khi bạn đang làm việc với một trong những vật liệu dễ uốn và dễ uốn nhất được biết đến với kỹ thuật - mã nguồn được lưu trữ dưới dạng dữ liệu máy tính. Nắm bắt phước lành đó bằng cách thay đổi nó khi bạn biết bạn cần chúng, thay vì thực hiện chúng trước nhiều tháng và gánh nặng bản thân trong khi chờ đợi bằng cách viết lên một bản tóm tắt mà bạn chưa đạt được.


Đây là cảm giác của tôi, nhưng đồng thời, lời khuyên phổ biến mà tôi thấy là việc mã hóa nền tảng cụ thể cho một trò chơi rất dễ thực hiện và nên được thực hiện ... Tôi không biết phải nghĩ gì.
Rei Miyasaka

5

Tuy nhiên nếu tôi đã học được một điều với tư cách là một lập trình viên thì hãy hỏi "có lý do nào có thể hiểu được rằng thành phần này có thể được thay thế bằng một hàm ý khác" và nếu câu trả lời là có thì tôi cần thiết kế một sự trừu tượng và giao diện phù hợp với thành phần đó .

Điều này không thực sự trả lời câu hỏi của bạn, nhưng theo kinh nghiệm của tôi với tư cách là một lập trình viên, việc khái quát hóa sớm cũng tệ như tối ưu hóa sớm. Bạn hiếm khi đạt được bất cứ điều gì và phải duy trì nó trong suốt quá trình của dự án.

Lời khuyên của tôi là hãy sử dụng mã máy khách D3D9 trong dự án của bạn và khi bạn xác định rằng bạn cần chuyển đổi giao diện, hãy tạo giao diện với tất cả những thứ bạn đang sử dụng. Các lập trình viên rất tệ trong việc dự đoán những gì họ có thể cần, đôi khi trong trường hợp bạn không quen thuộc lắm với tài liệu, vì vậy, cách này bạn đang làm ít công việc nhất.

Xem thêm: http://c2.com/xp/YouArentGonnaNeedIt.html


Rắc rối với điều này là nếu sau này anh ta phải đi trừu tượng, thì anh ta phải viết lại tất cả mã giao diện của mình. Và nếu D3D9 được đưa vào toàn cầu trong toàn bộ dự án của mình, thì sẽ rất khó để cấu trúc lại tất cả mã đó.
DeadMG

1
Dù sao đi nữa, anh ta sẽ phải viết lại mọi thứ khi anh ta có một trường hợp sử dụng không được bao phủ bởi bất kỳ giả định nào mà anh ta đưa ra dù thế nào đi nữa. Tái cấu trúc nên được chấp nhận, đặc biệt là cho một dự án học tập.
Tetrad

Tôi đang giải quyết vụ việc một chút. Tôi quá ghét sự trừu tượng không cần thiết. Nhưng trong trường hợp này tôi có thể nói thực sự có một cơ hội tốt tôi sẽ muốn làm điều này với một triển khai khác ...
JohnB

4

Hừm, có vẻ như tôi không đủ uy tín để đăng bình luận trả lời.

Cả Kylotan và Tetrad đều đưa ra câu trả lời xuất sắc. Những gì tôi muốn thêm là đề nghị bạn tách mã kết xuất khỏi logic trò chơi - điều này sẽ phục vụ một số mục đích:

  • mã của bạn sẽ sạch hơn
  • Bạn sẽ dễ dàng thay đổi trình kết xuất của mình hơn mà không cần viết lại một nửa logic trò chơi mỗi lần
  • cuối cùng, việc cung cấp các mô-đun kết xuất hoàn toàn khác nhau theo cách này dễ dàng hơn

Là một phần thưởng bổ sung, theo cách này, bạn sẽ xây dựng sự trừu tượng của mình dần dần khi bạn làm việc trong dự án của mình. Như bạn nói, đây là một dự án học tập, hãy làm cho nó dễ dàng và đơn giản hóa, thêm sự phức tạp và trừu tượng khi bạn đi thay vì lập kế hoạch lớn như bạn có thể thiếu chuyên môn để có được nó ngay từ đầu (như Tetrad cũng đề xuất).


Ôi thật đấy! Nhưng câu hỏi là tôi tách nó ở cấp độ nào? Ở cấp độ vẽ cả một thị trấn, hay cấp độ vẽ một hình tam giác? Hoặc nơi ở giữa
JohnB

Một cách hợp lý sẽ là giữ cho các cuộc gọi API hiển thị riêng biệt. Đó có thể là về "vẽ một tòa nhà hoặc một chiếc ô tô" theo cách tương tự của bạn (coi các tòa nhà và xe hơi của bạn là những vật thể đơn lẻ :)). Một ví dụ: bạn muốn làm động vật thể của mình (để đơn giản, giả sử mỗi lần chỉ có thể phát một cái). Một đối tượng trò chơi có thể có một danh sách các chuỗi có sẵn và độ dài của chúng và đặt hoạt hình hoạt động cũng như tốc độ của nó trong khi trình kết xuất sẽ quản lý, tốt, phần kết xuất. Bạn cũng có thể cần lấy lại dữ liệu từ trình kết xuất, ví dụ hình dạng va chạm của đối tượng có thể thay đổi trong khi hoạt ảnh.
Gilead

1

Bạn không thể viết một sự trừu tượng hóa ở mức độ thấp đối với loại điều này, bởi vì các chi tiết của một shader hoàn toàn khác nhau giữa D3D9, 11, OGL, v.v ... Điều đó đơn giản là không thể.

Bạn có thể kiểm tra các tóm tắt được triển khai trong, giả sử, SFML, SDL, OGRE nếu bạn muốn xem những gì người khác đã chọn để thực hiện trừu tượng hóa.


Trừ khi các shader không phải là một phần của sự trừu tượng.
dùng253751

1

Tôi đã cố gắng viết một API bao quanh cả D3D và OGL một cách gọn gàng. Đó là một công việc lớn, tôi đã học được. Nhiều sự khác biệt của họ có thể được hòa giải. Ví dụ, tải shader. Tôi đóng gói hiệu quả mã shader D3D và OGL theo định dạng của riêng tôi. IE: Trong D3D, bạn cần chỉ định hồ sơ shader, vì vậy bản thân tệp có hồ sơ shader được viết trong đó. Khi nguồn shader đóng gói được chuyển đến API của tôi, nó sẽ gọi phương thức tạo shader D3D thích hợp với cấu hình shader thích hợp, v.v.

Tuy nhiên, vẫn còn nhiều sự khác biệt mà tôi chưa giải quyết được, bởi vì tôi đã quyết định rằng một sự trừu tượng ở cấp độ cao hơn sẽ dễ dàng hơn để thiết kế và đáp ứng các yêu cầu của tôi. Tôi hiện đang làm việc trên một cái gì đó tương tự như khung D3DXEffect. Các đối tượng có một kỹ thuật kết xuất, chứa các đường truyền kết xuất, có một tập hợp các trạng thái kết xuất, một tập hợp các shader và yêu cầu một số đầu vào nhất định. Điều này cho phép tôi trừu tượng hơn về API.


1

Tôi không biết tại sao mọi người tự động chuyển sang tổng quát hóa / tối ưu hóa sớm khi các câu hỏi như thế này xuất hiện. Có một điều gì đó để nói về việc phòng ngừa và nhận ra khi một thành phần trong hệ thống có khả năng bị tráo đổi. Tất cả các câu trả lời về "đừng lo lắng và mã hóa cho đến khi bạn cần thay đổi nó" thường khiến mọi người gặp rắc rối trong thế giới thực vì họ mang đến những thay đổi lớn hơn trên đường hoặc để lại mã được thiết kế khác bắt đầu với, nhưng bị bỏ lại vì nó "hoạt động". Làm một số suy nghĩ trước thời hạn và biết nơi dừng lại trước khi bạn suy nghĩ quá mức là một kỹ năng trong chính nó.

Sẽ dễ dàng hơn nhiều khi tái cấu trúc các giao diện để điều chỉnh sự khác biệt giữa DX / OGL, hơn là đưa một triển khai hoàn toàn mới (ví dụ DX) vào một hệ thống được mã hóa nghiêm ngặt để hoạt động với OGL. Việc trừu tượng hóa mã sẽ xảy ra ở một số mức độ vì bạn sẽ không muốn trộn mã đồ họa với mã logic trò chơi ở mọi nơi hoặc lặp lại mã. Nếu tôi đang làm việc với một trò chơi và bắt đầu với OpenGL (vì tôi có kinh nghiệm với nó), nhưng cũng biết rằng cuối cùng tôi muốn có được trò chơi trên Xbox / Windows (DX), điều đó thật ngớ ngẩn đối với tôi không xem xét điều đó khi thiết kế API đồ họa của tôi. Ý tưởng tái cấu trúc một thứ gì đó phức tạp như một trò chơi và giới thiệu các API đồ họa mới rất tẻ nhạt và dễ bị lỗi.

Bạn nên xem xét cách bạn sẽ sử dụng API của mình và xây dựng nó khi nhu cầu / kiến ​​thức của bạn thay đổi trong quá trình phát triển trò chơi. Bạn không thực hiện trừu tượng hóa OGL / DX cấp thấp, mà là tự tạo API để hỗ trợ tải hình học, tải / biên dịch shader, tải họa tiết, v.v.

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.