Có cách nào để sử dụng một số lượng ánh sáng tùy ý trong một shader mảnh không?


19

Có cách nào để vượt qua một số lượng vị trí ánh sáng (và màu sắc) tùy ý cho trình tạo bóng mảnh và lặp lại chúng trong trình tạo bóng không?

Nếu không thì làm thế nào nhiều đèn được mô phỏng? Ví dụ, đối với ánh sáng định hướng khuếch tán, bạn không thể vượt qua một tổng trọng lượng ánh sáng cho bóng đổ.


Tôi chưa làm việc với WebGL, nhưng trong OpenGL, bạn có tối đa 8 nguồn sáng. Theo tôi, nếu bạn muốn vượt qua nhiều hơn thế, bạn phải sử dụng ví dụ các biến thống nhất.
zacharmarz

Phương pháp cũ là luôn luôn vượt qua tất cả các đèn, đèn không sử dụng được đặt thành 0 độ chói và do đó sẽ không ảnh hưởng đến cảnh. Có lẽ không được sử dụng nhiều nữa ;-)
Patrick Hughes

7
Khi bạn Google những thứ như thế này, đừng sử dụng thuật ngữ 'WebGL' - công nghệ còn quá trẻ để mọi người có thể tiếp cận mặc dù về cách tiếp cận những vấn đề này. Lấy ví dụ về tìm kiếm này , 'Tôi cảm thấy may mắn' sẽ có hiệu quả. Hãy nhớ rằng một vấn đề WebGL sẽ dịch độc đáo cho cùng một vấn đề OpenGL chính xác.
Jonathan Dickinson

Đối với hơn 8 đèn trong kết xuất phía trước, tôi thường sử dụng một bóng đổ nhiều đường và cung cấp cho mỗi đường chuyền một nhóm 8 đèn khác nhau để xử lý, sử dụng pha trộn phụ gia.
ChrisC

Câu trả lời:


29

Nhìn chung có hai phương pháp để đối phó với điều này. Ngày nay, chúng được gọi là kết xuất chuyển tiếp và kết xuất hoãn lại. Có một biến thể trên hai cái này mà tôi sẽ thảo luận dưới đây.

Chuyển tiếp kết xuất

Kết xuất mỗi đối tượng một lần cho mỗi ánh sáng ảnh hưởng đến nó. Điều này bao gồm ánh sáng xung quanh. Bạn sử dụng chế độ hòa trộn phụ gia ( glBlendFunc(GL_ONE, GL_ONE)), để mỗi đóng góp của ánh sáng được thêm vào nhau. Vì sự đóng góp của các loại đèn khác nhau là phụ gia, bộ đệm khung cuối cùng có giá trị

Bạn có thể nhận được HDR bằng cách kết xuất với bộ đệm khung hình nổi. Sau đó, bạn vượt qua cảnh cuối cùng để giảm các giá trị ánh sáng HDR xuống phạm vi có thể nhìn thấy; đây cũng sẽ là nơi bạn thực hiện nở hoa và các hiệu ứng hậu kỳ khác.

Một cải tiến hiệu suất phổ biến cho kỹ thuật này (nếu cảnh có nhiều đối tượng) là sử dụng "pre-pass", trong đó bạn kết xuất tất cả các đối tượng mà không vẽ bất kỳ thứ gì cho bộ đệm khung màu (sử dụng glColorMaskđể tắt ghi màu). Điều này chỉ lấp đầy trong bộ đệm sâu. Bằng cách này, nếu bạn kết xuất một vật thể phía sau vật thể khác, GPU có thể nhanh chóng bỏ qua những mảnh vỡ đó. Nó vẫn phải chạy shader đỉnh, nhưng nó có thể bỏ qua các tính toán shader mảnh đắt tiền hơn.

Điều này là đơn giản để mã và dễ hình dung hơn. Và trên một số phần cứng (chủ yếu là GPU di động và GPU nhúng), nó có thể hiệu quả hơn so với giải pháp thay thế. Nhưng trên phần cứng cao cấp hơn, sự thay thế thường thắng trong các cảnh có nhiều ánh sáng.

Trì hoãn kết xuất

Kết xuất hoãn lại phức tạp hơn một chút.

Phương trình chiếu sáng bạn sử dụng để tính toán ánh sáng cho một điểm trên một bề mặt sử dụng các tham số bề mặt sau:

  • Vị trí bề mặt
  • Bề mặt chuẩn
  • Bề mặt khuếch tán màu
  • Màu sắc bề mặt
  • Bề mặt sáng bóng
  • Có thể các tham số bề mặt khác (tùy thuộc vào mức độ phức tạp của phương trình chiếu sáng của bạn)

Trong kết xuất về phía trước, các tham số này có được chức năng chiếu sáng của mảnh vỡ bằng cách được truyền trực tiếp từ trình tạo bóng đỉnh, được kéo từ kết cấu (thường thông qua tọa độ kết cấu được truyền từ trình tạo bóng đỉnh) hoặc được tạo từ toàn bộ vải trong trình tạo bóng mảnh các thông số khác. Màu khuếch tán có thể được tính bằng cách kết hợp màu trên mỗi đỉnh với kết cấu, kết hợp nhiều họa tiết, bất cứ thứ gì.

Trong kết xuất hoãn lại, chúng tôi thực hiện tất cả điều này rõ ràng. Trong lần đầu tiên, chúng ta kết xuất tất cả các đối tượng. Nhưng chúng ta không thể hiện màu sắc . Thay vào đó, chúng tôi đưa ra các tham số bề mặt . Vì vậy, mỗi pixel trên màn hình có một bộ thông số bề mặt. Điều này được thực hiện thông qua kết xuất với kết cấu ngoài màn hình. Một kết cấu sẽ lưu trữ màu khuếch tán dưới dạng RGB của nó và có thể là độ sáng bóng đặc trưng như alpha. Một kết cấu khác sẽ lưu trữ màu sắc đặc biệt. Một phần ba sẽ lưu trữ bình thường. Và như vậy.

Các vị trí thường không được lưu trữ. Thay vào đó, nó được hoàn nguyên trong lần thứ hai bởi toán học quá phức tạp để vào đây. Có thể nói, chúng tôi sử dụng bộ đệm độ sâu và vị trí phân đoạn không gian màn hình làm đầu vào để tìm ra vị trí không gian camera của điểm trên một bề mặt.

Vì vậy, bây giờ khi các kết cấu này chủ yếu chứa tất cả thông tin bề mặt cho mỗi pixel hiển thị trong cảnh, chúng tôi bắt đầu hiển thị các hình tứ giác toàn màn hình. Mỗi ánh sáng được hiển thị toàn màn hình quad. Chúng tôi lấy mẫu từ kết cấu tham số bề mặt (và khôi phục vị trí), sau đó chỉ cần sử dụng chúng để tính toán sự đóng góp của ánh sáng đó. Điều này được thêm (một lần nữa glBlendFunc(GL_ONE, GL_ONE)) vào hình ảnh. Chúng tôi tiếp tục làm điều này cho đến khi chúng tôi hết đèn.

HDR một lần nữa là một bước hậu xử lý.

Nhược điểm lớn nhất của kết xuất hoãn lại là khử răng cưa. Nó đòi hỏi một chút công việc để antialias đúng cách.

Ưu điểm lớn nhất, nếu GPU của bạn có nhiều băng thông bộ nhớ, là hiệu năng. Chúng tôi chỉ hiển thị hình học thực tế một lần (hoặc 1 + 1 cho mỗi ánh sáng có bóng, nếu chúng tôi đang thực hiện ánh xạ bóng). Chúng tôi không bao giờ dành bất kỳ thời gian nào cho các pixel hoặc hình học ẩn không thể nhìn thấy sau này. Tất cả thời gian vượt qua ánh sáng được dành cho những thứ thực sự có thể nhìn thấy.

Nếu GPU của bạn không có nhiều băng thông bộ nhớ, thì vượt qua ánh sáng thực sự có thể bắt đầu bị tổn thương. Kéo từ 3-5 họa tiết trên mỗi pixel màn hình không thú vị.

Vượt qua nhẹ

Đây là một loại biến thể của kết xuất hoãn lại có sự đánh đổi thú vị.

Giống như trong kết xuất hoãn lại, bạn kết xuất các tham số bề mặt của mình thành một bộ đệm. Tuy nhiên, bạn đã viết tắt dữ liệu bề mặt; dữ liệu bề mặt duy nhất bạn quan tâm trong thời gian này là giá trị bộ đệm độ sâu (để tái tạo lại vị trí), bình thường và độ sáng bóng đặc trưng.

Sau đó, với mỗi ánh sáng, bạn chỉ tính kết quả chiếu sáng. Không nhân với màu bề mặt, không có gì. Chỉ là dấu chấm (N, L) và thuật ngữ cụ thể, hoàn toàn không có màu sắc bề mặt. Các thuật ngữ cụ thể và khuếch tán nên được giữ trong các bộ đệm riêng biệt. Các thuật ngữ cụ thể và khuếch tán cho mỗi ánh sáng được tóm tắt trong hai bộ đệm.

Sau đó, bạn kết xuất lại hình học, sử dụng tổng số tính toán ánh sáng cụ thể và khuếch tán để thực hiện kết hợp cuối cùng với màu bề mặt, do đó tạo ra độ phản xạ tổng thể.

Ưu điểm ở đây là bạn có được nhiều mẫu trở lại (ít nhất, dễ dàng hơn so với trả chậm). Bạn thực hiện kết xuất theo từng đối tượng ít hơn kết xuất phía trước. Nhưng điều chính yếu hơn là việc cung cấp này là thời gian dễ dàng hơn để có các phương trình chiếu sáng khác nhau cho các bề mặt khác nhau.

Với kết xuất hoãn lại, bạn thường vẽ toàn bộ cảnh với cùng một bóng đổ trên mỗi ánh sáng. Vì vậy, mọi đối tượng phải sử dụng các tham số vật liệu giống nhau. Với ánh sáng trước, bạn có thể cung cấp cho mỗi đối tượng một bóng đổ khác nhau, để nó có thể tự thực hiện bước chiếu sáng cuối cùng.

Điều này không cung cấp nhiều tự do như trường hợp kết xuất phía trước. Nhưng nó vẫn nhanh hơn nếu bạn có băng thông kết cấu dự phòng.


-1: không đề cập đến LPP / PPL. -1 hoãn lại: kết xuất là một chiến thắng tức thì trên bất kỳ phần cứng DX9.0 nào (có ngay cả trên máy tính xách tay 'doanh nghiệp' của tôi) - đó là yêu cầu cơ bản trong khoảng năm 2009. Trừ khi bạn đang nhắm mục tiêu DX8.0 (không thể thực hiện Trì hoãn / LPP) Trì hoãn / LPP là mặc định . Cuối cùng, "rất nhiều băng thông bộ nhớ" là điên rồ - chúng ta thường thậm chí không bão hòa PCI-X x4, hơn nữa, LPP giảm đáng kể băng thông bộ nhớ. Cuối cùng, -1 cho nhận xét của bạn; Vòng lặp như thế này OK? Bạn biết những vòng lặp đó đang diễn ra 2073600 lần trên mỗi khung, phải không? Ngay cả với sự tương đồng của card đồ họa, nó vẫn tệ.
Jonathan Dickinson

1
@JonathanDickinson Tôi nghĩ rằng quan điểm của anh ấy là băng thông bộ nhớ cho việc vượt qua trước / ánh sáng bị trì hoãn thường lớn hơn nhiều lần so với kết xuất trước. Điều này không làm mất hiệu lực cách tiếp cận hoãn lại; nó chỉ là một cái gì đó để xem xét khi lựa chọn nó. BTW: bộ đệm hoãn lại của bạn phải ở trong bộ nhớ video, do đó băng thông PCI-X không liên quan; đó là băng thông nội bộ của GPU có vấn đề. Các shader pixel dài, ví dụ với một vòng lặp không được kiểm soát, sẽ không có gì đáng ngại nếu chúng đang làm công việc hữu ích. Và không có gì sai với thủ thuật chuẩn bị bộ đệm z; nó hoạt động tốt.
Nathan Reed

3
@JonathanDickinson: Đây là nói về WebGL, vì vậy mọi cuộc thảo luận về "mô hình đổ bóng" đều không liên quan. Và loại kết xuất nào được sử dụng không phải là một "chủ đề tôn giáo": nó đơn giản chỉ là vấn đề bạn đang chạy trên phần cứng nào. GPU nhúng, trong đó "bộ nhớ video" chỉ là RAM CPU thông thường, sẽ hoạt động rất tệ với kết xuất bị hoãn. Trên một trình kết xuất dựa trên gạch di động, nó thậm chí còn tồi tệ hơn . Kết xuất hoãn lại không phải là "chiến thắng tức thì" bất kể phần cứng; nó có sự đánh đổi của nó, giống như bất kỳ phần cứng nào.
Nicol Bolas

2
@JonathanDickinson: "Ngoài ra, với thủ thuật vượt qua bộ đệm z, bạn sẽ phải vật lộn để loại bỏ chiến đấu z với các đối tượng nên được rút ra." Đó là tổng số vô nghĩa. Bạn đang hiển thị cùng một đối tượng với cùng ma trận biến đổi và cùng một shader đỉnh. Kết xuất nhiều trang đã được thực hiện trong Voodoo 1 ngày; đây là một vấn đề được giải quyết Tích lũy ánh sáng không có gì thay đổi điều đó.
Nicol Bolas

8
@JonathanDickinson: Nhưng chúng ta không nói về việc dựng khung dây, phải không? Chúng ta đang nói về việc vẽ các hình tam giác giống như trước đây. OpenGL đảm bảo tính bất biến cho cùng một đối tượng được hiển thị (miễn là bạn đang sử dụng cùng một shader đỉnh, và thậm chí sau đó, có invarianttừ khóa để đảm bảo nó cho các trường hợp khác).
Nicol Bolas

4

Bạn cần sử dụng kết xuất hoãn lại hoặc chiếu sáng trước . Một số đường ống chức năng cố định cũ hơn (đọc: không có bóng đổ) hỗ trợ tối đa 16 hoặc 24 đèn - nhưng đó là nó . Kết xuất hoãn lại giúp loại bỏ giới hạn ánh sáng; nhưng với chi phí của một hệ thống kết xuất phức tạp hơn nhiều.

Rõ ràng WebGL hỗ trợ MRT hoàn toàn cần thiết cho bất kỳ hình thức kết xuất hoãn lại nào - vì vậy nó có thể thực hiện được; Tôi chỉ không chắc nó hợp lý như thế nào.

Ngoài ra, bạn có thể điều tra Unity 5 - đã hoãn việc kết xuất ngay lập tức.

Một cách đơn giản khác để giải quyết vấn đề này là chỉ cần ưu tiên đèn (có thể, dựa trên khoảng cách từ người chơi và liệu họ có ở trong máy ảnh không) và chỉ bật top 8. Rất nhiều tựa game AAA được quản lý để làm điều này mà không ảnh hưởng nhiều về chất lượng đầu ra (ví dụ, Far Cry 1).

Bạn cũng có thể nhìn vào các ánh sáng được tính toán trước . Các trò chơi như Quake 1 có rất nhiều dặm từ những trò chơi này - và chúng có thể khá nhỏ (bộ lọc song tuyến làm mềm các bóng đèn kéo dài khá độc đáo). Thật không may, tính toán trước loại trừ khái niệm đèn động 100%, nhưng nó thực sự trông rất tuyệt . Bạn có thể kết hợp điều này với giới hạn 8 đèn của mình, vì vậy, ví dụ, chỉ có tên lửa hoặc những thứ đó mới có ánh sáng thực sự - nhưng đèn trên tường hoặc như vậy sẽ là ánh sáng.

Lưu ý bên lề: Bạn sẽ không lặp lại chúng trong một shader? Nói lời tạm biệt với hiệu suất của bạn. GPU không phải là CPU và không được thiết kế để hoạt động giống như cách mà JavaScript làm. Hãy nhớ rằng mỗi pixel bạn kết xuất (nếu thậm chí nó bị ghi đè) phải thực hiện vòng lặp - vì vậy nếu bạn chạy ở 1920x1080 và một vòng lặp đơn giản chạy 16 lần thì bạn đang chạy mọi thứ bên trong vòng lặp đó 33177600 lần. Card đồ họa của bạn sẽ chạy song song rất nhiều những mảnh vỡ đó, nhưng những vòng lặp đó vẫn sẽ ăn phần cứng cũ hơn.


-1: "Bạn cần sử dụng kết xuất hoãn lại" Điều này hoàn toàn không đúng. Kết xuất hoãn lại chắc chắn là một cách để làm điều đó, nhưng nó không phải là cách duy nhất. Ngoài ra các vòng lặp không tệ về mặt hiệu suất, đặc biệt nếu chúng dựa trên các giá trị đồng nhất (nghĩa là: mỗi đoạn không có độ dài vòng lặp khác nhau).
Nicol Bolas

1
Xin vui lòng đọc đoạn 4.
Jonathan Dickinson

2

Bạn có thể sử dụng trình đổ bóng pixel hỗ trợ n đèn (trong đó n là một số nhỏ như 4 hoặc 8) và vẽ lại cảnh nhiều lần, truyền một loạt đèn mới mỗi lần và sử dụng pha trộn phụ gia để kết hợp tất cả chúng lại với nhau.

Đó là ý tưởng cơ bản. Tất nhiên có rất nhiều tối ưu hóa cần thiết để thực hiện điều này đủ nhanh cho một cảnh có kích thước hợp lý. Đừng vẽ tất cả các đèn, chỉ những cái có thể nhìn thấy (bực bội và loại bỏ tắc); không thực sự vẽ lại toàn bộ khung cảnh mỗi lần vượt qua, chỉ các đối tượng trong phạm vi ánh sáng trong đường chuyền đó; có nhiều phiên bản của trình đổ bóng hỗ trợ số lượng đèn khác nhau (1, 2, 3, ...) để bạn không lãng phí thời gian để đánh giá nhiều đèn hơn mức cần thiết.

Kết xuất hoãn lại như đã đề cập trong câu trả lời khác là một lựa chọn tốt khi bạn có nhiều đèn nhỏ, nhưng đó không phải là cách duy nhất.

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.