Вірна відповідь на це полягає в тому, щоб пропустити ContentPipeline і використовувати Texture2D.FromStream для завантаження текстур під час виконання. Цей метод відмінно працює на ПК, і, хоча буде невеликий показник продуктивності, це те, що я можу оптимізувати, коли наближуся до дати випуску. Наразі мені потрібна можливість динамічно змінювати контент як для редактора, так і для гри саме те, що мені потрібно. Як тільки вміст буде заморожено, я можу оптимізувати це, повернувшись до ContentPipeline.
Оскільки ви вибрали цей маршрут, я повинен попередити вас, що насправді це не так просто, як просто використовувати Texture2D.FromStream
з двох причин:
Проблема №1 - Відсутність попередньо розмноженої підтримки альфа
XNA4 тепер обробляє текстури кольорами у попередньо промноженому альфа-форматі за замовчуванням. Коли ви завантажуєте текстуру через контент конвеєра, ця обробка проводиться автоматично. На жаль, Texture2D.FromStream
це не робить те ж саме, тому будь-які текстури, які потребують певної ступеня прозорості, будуть завантажені та надані неправильно. Нижче наведено скріншот для ілюстрації проблеми:
Отже, щоб отримати правильні результати, обробку потрібно зробити самостійно. Метод, який я покажу, використовує GPU для обробки, тому це досить швидко. Він був заснований на цій чудовій статті . Звичайно, ви можете також доручити SpriteBatch
візуалізувати у старому режимі NonPremultiplyAlpha, але я не рекомендую цього робити.
Проблема №2 - Непідтримувані формати
Контент конвеєра підтримує більше форматів, ніж Texture2D.FromStream
. Зокрема, Texture2D.FromStream
підтримує лише png, jpg та gif. З іншого боку, контент-конвеєр підтримує bmp, dds, dib, hdr, jpg, pfm, png, ppm та tga. Якщо ви спробуєте завантажити usutported формат, Texture2D.FromStream
ви отримаєте InvalidOperationException
невелику додаткову інформацію.
Мені дуже потрібна підтримка bmp на моєму двигуні, тому для цього конкретного випадку я знайшов вирішення, яке, здається, працює нормально. Я не знаю про жоден з інших форматів. Мій метод полягає в тому, що вам потрібно додати посилання на System.Drawing
збірку до свого проекту, оскільки він використовує GDI, Image.FromStream
які підтримують більше форматів, ніж Texture2D.FromStream
.
Якщо ви не переймаєтесь підтримкою bmp, ви можете легко скинути цю частину мого рішення і просто виконати попередньо розмножену альфа-обробку.
Рішення - Проста версія (повільніше)
Перш за все, ось найпростіше рішення, якщо вам не байдуже підтримка бамперів. У цьому прикладі етап обробки повністю виконується на процесорі. Це трохи повільніше, ніж альтернатива, яку я покажу нижче (я зробив орієнтир обох рішень), але простіше зрозуміти:
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;
}
Якщо ви дбаєте про штампи, то вам потрібно спочатку завантажити зображення в GDI, а потім перетворити всередині PNG, перш ніж передавати його Texture2D.FromStream
. Ось код, який робить це:
// 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);
}
}
Рішення - Складна версія (швидше)
Нарешті, підхід, який я використовую у своїх проектах, полягає у використанні GPU для обробки. У цьому методі вам потрібно створити ціль візуалізації, правильно встановити деякі стану суміші та двічі намалювати зображення за допомогою SpriteBatch. Наприкінці я переглядаю весь RenderTarget2D і клоную вміст в окремий об’єкт Texture2D, тому що RenderTarget2D є мінливим і не переживе таких речей, як зміна розміру резервного буфера, тому безпечніше зробити копію.
Найсмішніше, що навіть при всьому цьому, на моїх тестах цей підхід працював приблизно в 3 рази швидше, ніж підхід CPU. Тож, безумовно, швидше, ніж перебирати кожен піксель і самостійно обчислювати колір. Код трохи довгий, тому я помістив його в пастину:
http://pastie.org/3651642
Просто додайте цей клас до свого проекту та використовуючи його так просто, як:
TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");
Примітка. Вам потрібно створити лише один TextureLoader
екземпляр для всієї гри. Також я використовую виправлення BMP, але ви можете вийняти його, якщо вам це не потрібно, і ви отримаєте купу продуктивності, або просто залиште needsBmp
параметр помилковим.