Vẽ biểu đồ cảnh của riêng tôi


23

Xin chào phát triển game SE!

Tôi đang tìm đường đến OpenGL với hy vọng tạo ra một công cụ trò chơi đơn giản và rất nhẹ. Tôi xem dự án là một kinh nghiệm học tập có thể kiếm được một ít tiền cuối cùng, nhưng dù sao cũng sẽ rất vui.

Cho đến nay tôi đã sử dụng GLFW để đạt được một số I / O cơ bản, một cửa sổ (với khóa toàn màn hình F11 rất lạ mắt) và dĩ nhiên là bối cảnh OpenGL. Tôi cũng đã sử dụng GLEW để hiển thị phần còn lại của các tiện ích mở rộng OpenGL vì tôi đang sử dụng Windows và tôi muốn sử dụng tất cả OpenGL 3.0+.

Điều này đưa tôi đến đồ thị cảnh. Nói tóm lại, tôi muốn tự lăn. Quyết định này được đưa ra sau khi xem xét OSG và đọc một vài bài viết về cách khái niệm biểu đồ cảnh đã bị xoắn, cong và vỡ. Một bài viết như vậy mô tả cách đồ thị cảnh đã phát triển như ...

Sau đó, chúng tôi đã thêm vào tất cả những thứ bổ sung này, như treo đồ trang trí trên cây Giáng sinh, ngoại trừ một số đồ trang trí là bít tết ngon ngọt và một số là những con bò sống.

Theo cách tương tự, tôi muốn bít tết, thịt của đồ thị cảnh nên là gì, mà không phải buộc vào đống mã bổ sung, hoặc bất kỳ con bò nào.

Vì vậy, trong tâm trí đó, tôi thấy mình tự hỏi chính xác biểu đồ cảnh nên là gì và nên thực hiện một biểu đồ cảnh đơn giản như thế nào? Đây là những gì tôi có cho đến nay ...

Một cha mẹ, cây n-con hoặc DAG mà ...

  • Nên theo dõi các biến đổi đối tượng trò chơi (vị trí, xoay, tỷ lệ)
  • Nên giữ trạng thái kết xuất để tối ưu hóa
  • Nên cung cấp một phương tiện loại bỏ các đối tượng không nằm trong tầm nhìn

Với các thuộc tính sau ...

  • Tất cả các nút phải được coi là có thể kết xuất (ngay cả khi chúng không kết xuất) Điều này có nghĩa là chúng ...

    • Tất cả nên có các phương thức cull (), state () và draw () (trả về 0 nếu không nhìn thấy)
    • cull () gọi đệ quy cull () trên tất cả các con, do đó tạo ra một lưới hoàn toàn cho toàn bộ nút và tất cả các con. Một phương thức khác, hasChanged () có thể cho phép cái gọi là lưới tĩnh không cần phải có hình học loại bỏ của chúng được tính toán cho từng khung. Điều này sẽ hoạt động sao cho nếu bất kỳ nút nào trong cây con đã thay đổi thì tất cả hình học xuống gốc được xây dựng lại.
  • Các trạng thái kết xuất sẽ được giữ trong một bảng liệt kê đơn giản, mỗi nút sẽ chọn từ bảng liệt kê này một bộ trạng thái OpenGL mà nó yêu cầu và trạng thái đó sẽ được thiết lập trước khi draw () được gọi trên nút đó. Điều này cho phép tạo khối, tất cả các nút của một tập trạng thái đã cho sẽ được hiển thị cùng nhau, sau đó tập trạng thái tiếp theo được thiết lập, v.v.

  • Không có nút nào nên trực tiếp giữ dữ liệu hình học / shader / texture, thay vào đó các nút nên trỏ đến các đối tượng được chia sẻ (có lẽ được quản lý bởi một số đối tượng singleton như trình quản lý tài nguyên).

  • Biểu đồ cảnh có thể tham chiếu các biểu đồ cảnh khác (có thể sử dụng nút proxy) để cho phép các tình huống như thế này , do đó cho phép các mô hình / đối tượng đa lưới phức tạp được sao chép xung quanh biểu đồ cảnh mà không cần thêm một tấn dữ liệu.

Tôi hy vọng sẽ nhận được một số phản hồi có giá trị về thiết kế hiện tại của tôi. Có thiếu chức năng không? Có một cách tốt hơn rất nhiều / mẫu thiết kế? Tôi có thiếu một số khái niệm lớn hơn sẽ cần thiết để đưa vào thiết kế này cho một trò chơi 3D đơn giản không? V.v.

Cảm ơn, -Cody

Câu trả lời:


15

Khái niệm

Về cơ bản, một biểu đồ cảnh không gì khác hơn là một biểu đồ chu kỳ hai hướng, dùng để biểu diễn một tập hợp các cấu trúc các mối quan hệ không gian.

Động cơ trong tự nhiên có xu hướng bao gồm các tính năng khác vào biểu đồ cảnh, như đã lưu ý. Cho dù bạn thấy rằng thịt hay bò có thể phụ thuộc vào kinh nghiệm của bạn với các công cụ và thư viện ngoài kia.

Giữ cho nó nhẹ

Tôi thích kiểu Unity3D có nút đồ thị cảnh của bạn (mà trái tim của nó là cấu trúc liên kết chứ không phải cấu trúc không gian / địa hình) vốn đã bao gồm các tham số và chức năng không gian. Trong công cụ của tôi, các nút của tôi thậm chí còn nhẹ hơn Unity3D, nơi chúng thừa hưởng rất nhiều thành viên rác không cần thiết từ các siêu giao diện / giao diện được triển khai: Đây là những gì tôi có - nhẹ như bạn có thể nhận được:

  • thành viên con trỏ cha / con.
  • các thành viên tham số không gian trước biến đổi: vị trí xyz, cao độ, ngáp và cuộn.
  • một ma trận biến đổi; các ma trận trong chuỗi phân cấp có thể nhân lên rất nhanh và dễ dàng bằng cách đi bộ lên / xuống cây theo cách đệ quy, cung cấp cho bạn các phép biến đổi không gian phân cấp là đặc điểm chính của biểu đồ cảnh;
  • một updateLocal()phương thức chỉ cập nhật ma trận biến đổi của nút này
  • một updateAll()phương thức cập nhật ma trận biến đổi này và tất cả các ma trận con cháu

... Tôi cũng bao gồm các phương trình logic chuyển động và do đó các thành viên vận tốc / gia tốc (tuyến tính & góc) trong lớp nút của tôi. Bạn có thể từ bỏ điều đó và xử lý nó trong bộ điều khiển chính của bạn thay vào đó nếu bạn muốn. Nhưng đó là nó - thực sự rất nhẹ. Hãy nhớ rằng, bạn có thể có những thứ này trên hàng ngàn thực thể. Vì vậy, như bạn đã đề nghị, giữ cho nó nhẹ.

Xây dựng hệ thống phân cấp

Bạn nói gì về một biểu đồ cảnh tham chiếu các biểu đồ cảnh khác ... Tôi đang chờ đến phần cuối? Tất nhiên họ làm. Đó là công dụng chính của họ. Bạn có thể thêm bất kỳ nút nào vào bất kỳ nút nào khác và các phép biến đổi sẽ tự động xảy ra trong không gian cục bộ của biến đổi mới. Tất cả những gì bạn đang làm là thay đổi một con trỏ, không giống như bạn đang sao chép dữ liệu xung quanh! Bằng cách thay đổi một con trỏ, sau đó bạn có một biểu đồ cảnh sâu hơn. Nếu sử dụng Proxy làm cho mọi thứ hiệu quả hơn thì bằng mọi cách, nhưng tôi chưa bao giờ thấy sự cần thiết.

Tránh Logic liên quan đến kết xuất

Hãy quên việc kết xuất khi bạn viết lớp nút biểu đồ cảnh, hoặc bạn sẽ tự làm bối rối mọi thứ. Tất cả vấn đề là bạn có một mô hình dữ liệu - cho dù đó là biểu đồ cảnh hay không không quan trọng - và một số trình kết xuất sẽ kiểm tra mô hình dữ liệu đó và kết xuất các đối tượng trên thế giới, cho dù đó là trong 1, 2 , 3 hoặc 7 chiều. Điểm tôi đang làm là: Không làm ô nhiễm biểu đồ cảnh của bạn bằng logic kết xuất. Một biểu đồ cảnh là về cấu trúc liên kết và địa hình - tức là các đặc điểm kết nối và không gian. Đây là trạng thái thực sự của mô phỏng và tồn tại ngay cả khi không có kết xuất (có thể ở bất kỳ dạng nào dưới ánh mặt trời từ góc nhìn người thứ nhất đến biểu đồ thống kê đến mô tả văn bản). Các nút không trỏ đến các đối tượng liên quan đến kết xuất - tuy nhiên điều ngược lại cũng có thể đúng. Cũng xem xét điều này: Không phải mọi nút đồ thị cảnh trong toàn bộ cây của bạn sẽ được hiển thị. Nhiều người sẽ chỉ là container. Vậy tại sao thậm chí phân bổ bộ nhớ cho một đối tượng con trỏ để kết xuất? Ngay cả một thành viên con trỏ không bao giờ được sử dụng, vẫn chiếm bộ nhớ. Vì vậy, đảo ngược hướng con trỏ: Ví dụ liên quan đến kết xuất tham chiếu mô hình dữ liệu (có thể, hoặc bao gồm, nút biểu đồ cảnh của bạn), KHÔNG ngược lại. Và nếu bạn muốn một cách dễ dàng để chạy qua danh sách bộ điều khiển của bạn mà vẫn có quyền truy cập vào chế độ xem liên quan, thì hãy sử dụng từ điển / hashtable, tiếp cận thời gian truy cập đọc O (1). Bằng cách đó, không có ô nhiễm và logic mô phỏng của bạn không quan tâm đến trình kết xuất nào đang diễn ra, điều này làm cho ngày và đêm của bạn được mã hóa Vậy tại sao thậm chí phân bổ bộ nhớ cho một đối tượng con trỏ để kết xuất? Ngay cả một thành viên con trỏ không bao giờ được sử dụng, vẫn chiếm bộ nhớ. Vì vậy, đảo ngược hướng con trỏ: Ví dụ liên quan đến kết xuất tham chiếu mô hình dữ liệu (có thể, hoặc bao gồm, nút biểu đồ cảnh của bạn), KHÔNG ngược lại. Và nếu bạn muốn một cách dễ dàng để chạy qua danh sách bộ điều khiển của bạn mà vẫn có quyền truy cập vào chế độ xem liên quan, thì hãy sử dụng từ điển / hashtable, tiếp cận thời gian truy cập đọc O (1). Bằng cách đó, không có ô nhiễm và logic mô phỏng của bạn không quan tâm đến trình kết xuất nào đang diễn ra, điều này làm cho ngày và đêm của bạn được mã hóa Vậy tại sao thậm chí phân bổ bộ nhớ cho một đối tượng con trỏ để kết xuất? Ngay cả một thành viên con trỏ không bao giờ được sử dụng, vẫn chiếm bộ nhớ. Vì vậy, đảo ngược hướng con trỏ: Ví dụ liên quan đến kết xuất tham chiếu mô hình dữ liệu (có thể, hoặc bao gồm, nút biểu đồ cảnh của bạn), KHÔNG ngược lại. Và nếu bạn muốn một cách dễ dàng để chạy qua danh sách bộ điều khiển của bạn mà vẫn có quyền truy cập vào chế độ xem liên quan, thì hãy sử dụng từ điển / hashtable, tiếp cận thời gian truy cập đọc O (1). Bằng cách đó, không có ô nhiễm và logic mô phỏng của bạn không quan tâm đến trình kết xuất nào đang diễn ra, điều này làm cho ngày và đêm của bạn được mã hóa Và nếu bạn muốn một cách dễ dàng để chạy qua danh sách bộ điều khiển của bạn mà vẫn có quyền truy cập vào chế độ xem liên quan, thì hãy sử dụng từ điển / hashtable, tiếp cận thời gian truy cập đọc O (1). Bằng cách đó, không có ô nhiễm và logic mô phỏng của bạn không quan tâm đến trình kết xuất nào đang diễn ra, điều này làm cho ngày và đêm của bạn được mã hóa Và nếu bạn muốn một cách dễ dàng để chạy qua danh sách bộ điều khiển của bạn mà vẫn có quyền truy cập vào chế độ xem liên quan, thì hãy sử dụng từ điển / hashtable, tiếp cận thời gian truy cập đọc O (1). Bằng cách đó, không có ô nhiễm và logic mô phỏng của bạn không quan tâm đến trình kết xuất nào đang diễn ra, điều này làm cho ngày và đêm của bạn được mã hóathế giới dễ dàng hơn.

Đối với việc loại bỏ, tham khảo lại ở trên. Loại bỏ khu vực quan tâm là một khái niệm logic mô phỏng. Đó là, bạn không xử lý thế giới bên ngoài khu vực này (thường là hình hộp, hình tròn hoặc hình cầu). Điều này diễn ra trong vòng điều khiển chính / vòng lặp trò chơi, trước khi kết xuất. Mặt khác, việc loại bỏ sự thất vọng hoàn toàn liên quan đến kết xuất. Vì vậy, quên đi việc loại bỏ ngay bây giờ. Nó không liên quan gì đến đồ thị cảnh, và bằng cách tập trung vào nó, bạn sẽ che khuất mục đích thực sự của những gì bạn đang cố gắng đạt được.

Một lưu ý cuối cùng ...

Tôi có được cảm giác mạnh mẽ mà bạn đang đến từ nền Flash (cụ thể là AS3), với tất cả các chi tiết về kết xuất được bao gồm ở đây. Có, mô hình Flash Stage / DisplayObject bao gồm tất cả logic kết xuất như là một phần của khung cảnh. Nhưng Flash đưa ra rất nhiều giả định mà bạn không nhất thiết muốn đưa ra. Đối với một công cụ trò chơi hoàn chỉnh, tốt hơn là không kết hợp cả hai, vì lý do hiệu suất, sự thuận tiện và kiểm soát độ phức tạp của mã thông qua SoC thích hợp .


1
Cảm ơn Nick. Tôi thực sự là một nhà làm phim hoạt hình 3D (3D thực sự không flash) đã trở thành lập trình viên, do đó tôi có xu hướng suy nghĩ về mặt đồ họa. Nếu điều đó không đủ tồi tệ, tôi đã bắt đầu bằng Java và tự hào về bản thân "mọi thứ phải là một đối tượng" thấm nhuần trong ngôn ngữ đó. Bạn đã thuyết phục tôi rằng đồ thị cảnh nên được tách ra khỏi mã kết xuất và loại bỏ, bây giờ các bánh răng của tôi đang quay tròn chính xác cách thực hiện điều đó. Tôi đang nghĩ về việc đối xử với trình kết xuất giống như hệ thống riêng biệt của nó tham chiếu biểu đồ cảnh cho dữ liệu biến đổi, v.v.
Cody Smith

1
@CodySmith, Vui mừng vì nó đã giúp. Trình cắm không biết xấu hổ, nhưng tôi duy trì một khuôn khổ là tất cả về SoC / MVC. Khi làm như vậy, tôi đã đến với trại truyền thống hơn trong ngành, những người khăng khăng mọi thứ phải ở trong một vật thể trung tâm, nguyên khối. Nhưng thậm chí họ sẽ nói chung với bạn - giữ cho kết xuất của bạn tách biệt khỏi biểu đồ cảnh của bạn. SoC / SRP là thứ tôi không thể nhấn mạnh đủ - không bao giờ trộn nhiều logic vào một lớp hơn bạn cần. Tôi thậm chí còn ủng hộ các chuỗi thừa kế OO phức tạp trên logic hỗn hợp trong cùng một lớp, nếu bạn đặt một khẩu súng vào đầu tôi!
Kỹ sư

Không tôi thích khái niệm này. Và quyền của bạn, đây là lần đầu tiên đề cập đến SoC tôi đã thấy ở bất cứ đâu trong nhiều năm đọc về Thiết kế trò chơi. Cảm ơn một lần nữa.
Cody Smith

@CodySmith Suy nghĩ nhanh trong khi duyệt qua cái này một lần nữa. Nói chung là tốt để giữ cho mọi thứ tách rời. Đối với các loại khác nhau của các đối tượng mô hình điều khiển trong codebase của bạn mà trải qua render, tuy nhiên, nó tốt cho bạn để giữ cho các bộ sưu tập của Renderables (đó là một giao diện hoặc lớp trừu tượng) trong nội bộ để những lõi đối tượng mô hình điều khiển. Ví dụ tốt về điều này là các thực thể hoặc các yếu tố UI. Do đó, bạn có thể nhanh chóng truy cập chỉ các trình kết xuất phù hợp với đối tượng cốt lõi cụ thể đó - mà không cần các chi tiết cụ thể thực hiện sẽ làm nhiễm bẩn lớp thực thể, do đó sử dụng các giao diện.
Kỹ sư

@CodySmith Lợi ích rất rõ ràng với các thực thể, có thể là ví dụ. có các đại diện cả trong chế độ xem thế giới và trên bản đồ nhỏ. Do đó, bộ sưu tập. Ngoài ra, bạn có thể chỉ cho phép một khe kết xuất duy nhất cho từng đối tượng điều khiển mô hình, bên trong đối tượng đó. Nhưng giữ giao diện chung! Không có chi tiết cụ thể - chỉ Renderer.
Kỹ sư
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.