似乎正确的答案是跳过ContentPipeline,并使用Texture2D.FromStream在运行时加载纹理。这种方法在PC上效果很好,即使性能影响不大,但在接近发布日期后,我就可以对其进行优化。就目前而言,具有为编辑器和游戏动态修改内容的能力正是我所需要的。内容冻结后,我可以通过返回ContentPipeline对其进行优化。
既然您选择了这条路线,我必须警告您,实际上这并不像仅仅使用Texture2D.FromStream
两条路那样简单,原因有两个:
问题1-缺少预乘alpha支持
XNA4现在默认使用预乘alpha格式的颜色处理纹理。通过内容管道加载纹理时,该处理将自动为您完成。不幸的是,Texture2D.FromStream
它不会做同样的事情,因此任何需要一定透明度的纹理都将被错误地加载和渲染。下面是说明问题的屏幕截图:
因此,为了获得正确的结果,您需要自己进行处理。我将展示的方法使用GPU进行处理,因此速度非常快。它基于这篇伟大的文章。当然,您也可以指示SpriteBatch
以旧的NonPremultiplyAlpha模式进行渲染,但是我不建议这样做。
问题2-不支持的格式
内容管道支持的格式超过Texture2D.FromStream
。特别是,Texture2D.FromStream
仅支持png,jpg和gif。另一方面,内容管道支持bmp,dds,dib,hdr,jpg,pfm,png,ppm和tga。如果您尝试通过加载惯用的格式,Texture2D.FromStream
则会获得InvalidOperationException
很少的其他信息。
我确实需要引擎上的bmp支持,因此对于这种特殊情况,我发现了一种可行的解决方法。我不知道其他任何格式。我的方法的不足之处在于,您需要在工程中添加对该System.Drawing
程序集的引用,因为它利用了GDI Image.FromStream
,该GDI 支持的格式比的更多Texture2D.FromStream
。
如果您不关心支持bmp,则可以轻松删除该解决方案的这一部分,而只需进行预乘alpha处理即可。
解决方案-简单版本(较慢)
首先,如果您不关心支持bmp ,这是最简单的解决方案。在此示例中,处理阶段完全在CPU上完成。它比我将在下面显示的替代方案(我对两个解决方案都进行了基准测试)要慢一些,但更易于理解:
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;
}
如果您关心bmp,那么您需要做的就是先使用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是易失性的,无法忍受更改后缓冲区大小等事情,因此进行复制更安全。
有趣的是,即使有所有这些,在我的测试中,这种方法的执行速度也比CPU方法快约3倍。因此,它绝对比遍历每个像素并自己计算颜色快。该代码有点长,所以我将其放在了pastebin中:
http://pastie.org/3651642
只需将该类添加到您的项目中,即可使用它,方法很简单:
TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");
注意:您只需要TextureLoader
为整个游戏创建一个实例。另外,我正在使用BMP修复程序,但是如果不需要并获得大量性能,或者仅将needsBmp
参数保留为false ,则可以将其删除。