Có vẻ như câu trả lời đúng cho vấn đề này là bỏ qua ContentPipeline và sử dụng Texture2D.FromStream để tải kết cấu khi chạy. Phương pháp này hoạt động tốt trong PC và mặc dù sẽ có một hiệu suất nhỏ nhưng đây là điều tôi có thể tối ưu hóa một khi tôi đến gần ngày phát hành. Hiện tại, có khả năng tự động sửa đổi nội dung cho cả trình chỉnh sửa và trò chơi là chính xác những gì tôi cần. Khi nội dung bị đóng băng, tôi có thể tối ưu hóa điều này bằng cách quay lại ContentPipeline.
Vì bạn đã chọn tuyến đường này, tôi phải cảnh báo bạn rằng nó thực sự không đơn giản như chỉ sử dụng Texture2D.FromStream
vì hai lý do:
Vấn đề # 1 - Thiếu hỗ trợ alpha được nhân lên trước
XNA4 hiện xử lý họa tiết với màu sắc ở định dạng alpha được xử lý trước theo mặc định. Khi bạn tải một kết cấu thông qua đường ống nội dung, quá trình xử lý đó được thực hiện cho bạn một cách tự động. Thật không may Texture2D.FromStream
, không làm điều tương tự, vì vậy bất kỳ kết cấu nào yêu cầu một mức độ minh bạch nào đó sẽ được tải và hiển thị không chính xác. Dưới đây là một ảnh chụp màn hình để minh họa vấn đề:
Vì vậy, để có được kết quả chính xác, bạn cần phải tự xử lý. Phương pháp tôi sẽ trình bày sử dụng GPU để xử lý nên khá nhanh. Nó được dựa trên bài viết tuyệt vời này . Tất nhiên bạn cũng có thể hướng dẫn SpriteBatch
kết xuất ở chế độ NonPremultiplyAlpha cũ nhưng tôi không thực sự khuyên bạn nên làm điều đó.
Vấn đề # 2 - Các định dạng không được hỗ trợ
Các đường ống nội dung hỗ trợ nhiều định dạng hơn Texture2D.FromStream
. Đặc biệt, Texture2D.FromStream
chỉ hỗ trợ png, jpg và gif. Mặt khác, đường ống nội dung hỗ trợ bmp, dds, dib, hdr, jpg, pfm, png, ppm và tga. Nếu bạn cố tải một định dạng được sử dụng thông qua Texture2D.FromStream
bạn sẽ nhận được một InvalidOperationException
ít thông tin bổ sung.
Tôi thực sự cần hỗ trợ bmp trên công cụ của mình vì vậy trong trường hợp cụ thể đó, tôi đã tìm thấy một cách giải quyết có vẻ hoạt động tốt. Tôi không biết về bất kỳ định dạng nào khác. Điều hấp dẫn với phương pháp của tôi là bạn cần thêm một tham chiếu đến System.Drawing
tổ hợp vào dự án của mình, vì nó sử dụng GDI Image.FromStream
hỗ trợ nhiều định dạng hơn Texture2D.FromStream
.
Nếu bạn không quan tâm đến việc hỗ trợ bmp, bạn có thể dễ dàng bỏ phần giải pháp đó của tôi và chỉ cần thực hiện xử lý alpha được nhân lên trước.
Giải pháp - Phiên bản đơn giản (chậm hơn)
Trước hết, đây là giải pháp đơn giản nhất nếu bạn không quan tâm đến việc hỗ trợ bmps. Trong ví dụ này, giai đoạn xử lý được thực hiện hoàn toàn trên CPU. Nó chậm hơn một chút so với giải pháp thay thế mà tôi sẽ trình bày bên dưới (tôi đã làm chuẩn cả hai giải pháp) nhưng dễ hiểu hơn:
public static Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream)
{
Texture2D texture = Texture2D.FromStream(graphicsDevice, stream);
Color[] data = new Color[texture.Width * texture.Height];
texture.GetData(data);
for (int i = 0; i != data.Length; ++i)
data[i] = Color.FromNonPremultiplied(data[i].ToVector4());
texture.SetData(data);
return texture;
}
Nếu bạn quan tâm đến bmps thì việc bạn cần làm là tải hình ảnh bằng GDI trước rồi sau đó chuyển đổi thành PNG trước khi chuyển nó sang Texture2D.FromStream
. Đây là mã thực hiện điều đó:
// Load image using GDI because Texture2D.FromStream doesn't support BMP
using (Image image = Image.FromStream(stream))
{
// Now create a MemoryStream which will be passed to Texture2D after converting to PNG internally
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
texture = Texture2D.FromStream(_graphicsDevice, ms);
}
}
Giải pháp - Phiên bản phức tạp (Nhanh hơn)
Cuối cùng, cách tiếp cận tôi sử dụng cho các dự án của mình là sử dụng GPU để xử lý thay thế. Trong phương pháp này, bạn cần tạo mục tiêu kết xuất, thiết lập một số trạng thái pha trộn chính xác và vẽ hình ảnh hai lần bằng SpriteBatch. Cuối cùng, tôi đi qua toàn bộ RenderTarget2D và sao chép nội dung vào một đối tượng Texture2D riêng vì RenderTarget2D rất dễ bay hơi và sẽ không tồn tại những thứ như thay đổi kích thước backbuffer để sao chép an toàn hơn.
Điều buồn cười là ngay cả với tất cả những điều này, trong các thử nghiệm của tôi, phương pháp này đã thực hiện nhanh hơn khoảng 3 lần so với phương pháp CPU. Vì vậy, nó chắc chắn nhanh hơn so với việc đi qua từng pixel và tự tính toán màu sắc. Mã này hơi dài nên tôi đã đặt nó vào một pastebin:
http://pastie.org/3651642
Chỉ cần thêm lớp đó vào dự án của bạn và sử dụng nó đơn giản như:
TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");
Lưu ý: Bạn chỉ cần tạo một TextureLoader
phiên bản cho toàn bộ trò chơi. Ngoài ra, tôi đang sử dụng bản sửa lỗi BMP nhưng bạn có thể gỡ bỏ nếu bạn không cần và đạt được hiệu suất cao hoặc chỉ để lại needsBmp
tham số là sai.