Làm thế nào tôi có thể đạt được hiệu ứng ánh sáng 2D mượt mà?


18

Tôi đang tạo một trò chơi xếp gạch 2D trong XNA.

Hiện tại sét của tôi trông như thế này .

Làm thế nào tôi có thể làm cho nó trông như thế này ?

Thay vì mỗi khối có tông màu riêng, nó có lớp phủ mịn.

Tôi đang giả sử một số loại shader và để chuyển các giá trị ánh sáng cho các ô xung quanh sang shader, nhưng tôi là người mới bắt đầu sử dụng shader nên tôi không chắc chắn.

Ánh sáng hiện tại của tôi tính toán ánh sáng, và sau đó chuyển nó sang a SpriteBatchvà vẽ với tham số màu. Mỗi ô có một số Colorđược tính toán trước khi vẽ trong thuật toán chiếu sáng của tôi, được sử dụng cho màu.

Đây là một ví dụ về cách tôi hiện đang tính toán ánh sáng (tôi cũng làm điều này từ trái, phải và dưới cùng, nhưng tôi thực sự mệt mỏi khi thực hiện từng khung hình này ...)

nhập mô tả hình ảnh ở đây

Vì vậy, thực sự nhận và vẽ ánh sáng cho đến nay là không có vấn đề !

Tôi đã thấy vô số hướng dẫn về sương mù chiến tranh và sử dụng lớp phủ tròn hình tròn để tạo ra tia sét mượt mà, nhưng tôi đã có một phương pháp hay để gán cho mỗi ô một giá trị sét, nó chỉ cần được làm mịn giữa chúng.

Vì vậy, để xem xét

  • Tính toán ánh sáng (Xong)
  • Vẽ gạch (Xong, tôi biết tôi sẽ cần sửa đổi nó cho trình đổ bóng)
  • Gạch bóng râm (Cách vượt qua các giá trị và áp dụng "gradient)

Câu trả lời:


6

Còn những thứ như thế này thì sao?

Đừng vẽ ánh sáng của bạn bằng cách pha màu gạch của bạn. Vẽ các ô không hợp lệ của bạn vào mục tiêu kết xuất, sau đó vẽ đèn chiếu sang mục tiêu kết xuất thứ hai, đại diện cho mỗi ô như một hình chữ nhật màu xám bao phủ khu vực của ô. Để kết xuất cảnh cuối cùng, sử dụng một shader để kết hợp hai mục tiêu kết xuất, làm tối từng pixel của cái đầu tiên theo giá trị của cái thứ hai.

Điều này sẽ sản xuất chính xác những gì bạn có bây giờ. Điều đó không giúp ích gì cho bạn, vì vậy hãy thay đổi nó một chút.

Thay đổi kích thước của mục tiêu kết xuất ánh sáng của bạn để mỗi ô được thể hiện bằng một pixel , thay vì một khu vực hình chữ nhật. Khi kết hợp cảnh cuối cùng, sử dụng trạng thái lấy mẫu với lọc tuyến tính. Nếu không thì để mọi thứ khác như cũ.

Giả sử bạn đã viết đúng shader của mình, lightmap sẽ được "thu nhỏ" một cách hiệu quả trong quá trình tổng hợp. Điều này sẽ giúp bạn có được hiệu ứng chuyển màu đẹp miễn phí thông qua bộ lấy mẫu kết cấu của thiết bị đồ họa.

Bạn cũng có thể cắt bỏ shader và làm điều này đơn giản hơn với BlendState 'làm tối', nhưng tôi phải thử nghiệm nó trước khi tôi có thể cung cấp cho bạn thông tin cụ thể.

CẬP NHẬT

Tôi đã có một thời gian ngày hôm nay để thực sự chế nhạo điều này. Câu trả lời ở trên phản ánh thói quen sử dụng shader của tôi như là câu trả lời đầu tiên của tôi cho mọi thứ, nhưng trong trường hợp này chúng không thực sự cần thiết và việc sử dụng chúng không cần thiết làm phức tạp mọi thứ.

Như tôi đã đề xuất, bạn có thể thực hiện chính xác hiệu ứng tương tự bằng cách sử dụng BlendState tùy chỉnh. Cụ thể, BlendState tùy chỉnh này:

BlendState Multiply = new BlendState()
{
    AlphaSourceBlend = Blend.DestinationAlpha,
    AlphaDestinationBlend = Blend.Zero,
    AlphaBlendFunction = BlendFunction.Add,
    ColorSourceBlend = Blend.DestinationColor,
    ColorDestinationBlend = Blend.Zero,
    ColorBlendFunction = BlendFunction.Add
}; 

Phương trình pha trộn là

result = (source * sourceBlendFactor) blendFunction (dest * destBlendFactor)

Vì vậy, với BlendState tùy chỉnh của chúng tôi, điều đó trở thành

result = (lightmapColor * destinationColor) + (0)

Điều đó có nghĩa là màu nguồn của màu trắng tinh khiết (1, 1, 1, 1) sẽ giữ màu đích, màu nguồn của màu đen thuần khiết (0, 0, 0, 1) sẽ làm tối màu đích thành màu đen thuần khiết và bất kỳ màu nào màu xám ở giữa sẽ làm tối màu đích đến một lượng trung bình.

Để áp dụng điều này vào thực tế, trước tiên hãy làm bất cứ điều gì bạn cần làm để tạo ánh sáng của bạn:

var lightmap = GetLightmapRenderTarget();

Sau đó, chỉ cần vẽ cảnh không thích hợp của bạn trực tiếp vào backbuffer như bình thường:

spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
/* draw the world here */
spriteBatch.End();

Sau đó vẽ sơ đồ ánh sáng bằng BlendState tùy chỉnh:

var offsetX = 0; // you'll need to set these values to whatever offset is necessary
var offsetY = 0; // to align the lightmap with the map tiles currently being drawn
var width = lightmapWidthInTiles * tileWidth;
var height = lightmapHeightInTiles * tileHeight;

spriteBatch.Begin(SpriteSortMode.Immediate, Multiply);
spriteBatch.Draw(lightmap, new Rectangle(offsetX, offsetY, width, height), Color.White);
spriteBatch.End();

Điều này sẽ nhân màu đích (gạch không sáng) với màu nguồn (ánh sáng), làm tối màu gạch không thích hợp và tạo hiệu ứng chuyển màu do kết cấu ánh sáng được thu nhỏ theo kích thước cần thiết.


Điều này có vẻ khá đơn giản, Ill xem những gì tôi có thể làm sau này. Tôi đã thử một cái gì đó mô phỏng trước đây (tôi đã đưa ra ánh sáng trên mọi thứ khác và sử dụng một trình tạo bóng mờ, nhưng tôi không thể làm cho renderTarget "trong suốt" nó có lớp phủ màu tím, nhưng tôi muốn nó nhìn xuyên qua lớp gạch
Actuall

Sau khi đặt mục tiêu kết xuất của bạn trên thiết bị, hãy gọi device.Clear (Color.Transparent);
Cole Campbell

Mặc dù, trong trường hợp này, đáng để chỉ ra rằng ánh sáng của bạn có lẽ nên được xóa thành màu trắng hoặc đen, những thứ đó là ánh sáng đầy đủ và bóng tối hoàn toàn, tương ứng.
Cole Campbell

Tôi nghĩ đó là những gì tôi đã thử trước đây nhưng nó vẫn còn màu tím, tôi sẽ nhìn vào ngày mai khi tôi có thời gian để làm việc này.
Cyral

Hầu như mọi thứ đều ổn, nhưng nó mờ dần từ đen sang trắng, thay vì đen sang trong suốt. Phần shader là những gì tôi bối rối, làm thế nào để viết một cái để làm mờ cảnh không sáng ban đầu với cảnh mới.
Cyral

10

Được rồi đây là một phương pháp rất đơn giản để tạo ra một số tia sét 2D đơn giản và mượt mà, kết xuất trong ba lần:

  • Pass 1: thế giới trò chơi không có sét.
  • Pass 2: Lightning mà không có thế giới
  • Kết hợp thẻ 1. và 2. được hiển thị trên màn hình.

Trong pass 1 bạn vẽ tất cả các họa tiết và địa hình.

Trong pass 2, bạn vẽ một nhóm các sprite thứ hai là các nguồn sáng. Chúng sẽ trông giống như thế này:
nhập mô tả hình ảnh ở đây
Khởi tạo mục tiêu kết xuất với màu đen và vẽ những họa tiết đó lên nó bằng cách trộn tối đa hoặc Phụ gia.

Trong pass 3 bạn kết hợp hai pass trước. Có nhiều cách khác nhau để có thể kết hợp chúng. Nhưng phương pháp đơn giản và ít nghệ thuật nhất là trộn chúng lại với nhau thông qua Multiply. Điều này sẽ trông như thế này:
nhập mô tả hình ảnh ở đây


Vấn đề là, tôi đã có một hệ thống chiếu sáng, và đèn điểm không hoạt động ở đây vì một số đối tượng cần ánh sáng để đi qua và một số thì không.
Cyral

2
Vâng, đó là khó khăn hơn. Bạn sẽ cần phải chiếu tia để có được bóng. Teraria sử dụng các khối lớn cho địa hình của họ để không có vấn đề gì ở đó. Bạn có thể sử dụng phương pháp đúc tia không chính xác nhưng điều đó sẽ không tốt hơn terraria và có thể phù hợp thậm chí ít hơn nếu bạn không chặn đồ họa. Đối với một kỹ thuật tiên tiến và ưa nhìn, có bài viết này: gamedev.net/page/resource/_/technical/ Kẻ
API-Beast

2

Liên kết thứ hai mà bạn đăng lên trông giống như một màn sương chiến tranh , có một vài câu hỏi khác về điều này

Điều đó đối với tôi giống như một kết cấu , trong đó bạn "xóa" các bit của lớp phủ màu đen (bằng cách đặt alpha của các pixel đó thành 0) khi người chơi tiến về phía trước.

Tôi muốn nói rằng người đó phải sử dụng loại bàn chải "xóa" nơi người chơi đã khám phá.


1
Đó không phải là sương mù của chiến tranh, Nó tính toán từng tia sét. Trò chơi tôi chỉ ra tương tự như Terraria.
Cyral

Ý tôi là nó có thể được thực hiện tương tự như sương mù chiến tranh
bobobobo

2

Ánh sáng đỉnh (góc giữa gạch) thay vì gạch. Pha trộn ánh sáng trên mỗi gạch dựa trên bốn đỉnh của nó.

Thực hiện các thử nghiệm tầm nhìn đến từng đỉnh để xác định độ sáng của nó (bạn có thể có một khối dừng tất cả ánh sáng hoặc giảm ánh sáng, ví dụ: đếm có bao nhiêu giao điểm với mỗi đỉnh kết hợp với khoảng cách để tính giá trị ánh sáng thay vì sử dụng một kiểm tra nhị phân thuần túy nhìn thấy / không nhìn thấy).

Khi kết xuất một lát, gửi giá trị ánh sáng cho mỗi đỉnh tới GPU. Bạn có thể dễ dàng sử dụng một shader mảnh để lấy giá trị ánh sáng được nội suy tại mỗi mảnh trong sprite của ô để chiếu sáng từng pixel một cách mượt mà. Đã đủ lâu kể từ khi tôi chạm vào GLSL rằng tôi sẽ không cảm thấy thoải mái khi đưa ra một mẫu mã thực sự, nhưng trong mã giả, nó sẽ đơn giản như:

texture t_Sprite
vec2 in_UV
vec4 in_Light // coudl be one float; assuming you might want colored lights though

fragment shader() {
  output = sample2d(t_Sprite, in_UV) * in_Light;
}

Trình tạo bóng đỉnh chỉ đơn giản là phải chuyển các giá trị đầu vào cho trình đổ bóng mảnh, không có gì phức tạp thậm chí từ xa.

Bạn sẽ có được ánh sáng mượt mà hơn với nhiều đỉnh hơn (giả sử, nếu mỗi ô được hiển thị dưới dạng bốn ô phụ), nhưng điều đó có thể không đáng để nỗ lực tùy thuộc vào chất lượng bạn sẽ làm. Hãy thử nó theo cách đơn giản hơn trước.


1
line-of sight tests to each vertex? Điều đó sẽ rất tốn kém.
tro999

Bạn có thể tối ưu hóa với một số thông minh. Đối với trò chơi 2D, bạn có thể thực hiện một số lượng đáng kinh ngạc các bài kiểm tra tia đối với lưới nghiêm ngặt khá nhanh. Thay vì thử nghiệm LOS thẳng, bạn có thể "chảy" (tôi không thể nghĩ đến thuật ngữ thích hợp ngay bây giờ; đêm bia) các giá trị ánh sáng trên các đỉnh thay vì thực hiện các thử nghiệm tia.
Sean Middleditch

Sử dụng các bài kiểm tra thị lực sẽ làm phức tạp thêm điều này, tôi đã tìm ra ánh sáng. Bây giờ khi tôi gửi 4 đỉnh cho shader, làm thế nào tôi có thể làm điều đó? Tôi biết bạn không cảm thấy thoải mái khi đưa ra một mẫu mã thực sự, nhưng nếu tôi có thể có thêm một chút thông tin sẽ tốt hơn. Tôi cũng chỉnh sửa câu hỏi với nhiều thông tin hơn.
Cyral
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.