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