我从目录加载纹理有一些问题.也许代码优先:
private Texture2D LoadTextureStream(string filePath)
{
Texture2D file = null;
RenderTarget2D result = null;
try
{
using (System.IO.Stream titleStream = TitleContainer.OpenStream(filePath))
{
file = Texture2D.FromStream(GraphicsDevice, titleStream);
}
}
catch
{
throw new System.IO.FileLoadException("Cannot load '" + filePath + "' file!");
}
PresentationParameters pp = GraphicsDevice.PresentationParameters;
//Setup a render target to hold our final texture which will have premulitplied alpha values
result = new RenderTarget2D(GraphicsDevice, file.Width, file.Height, true, pp.BackBufferFormat, pp.DepthStencilFormat);
GraphicsDevice.SetRenderTarget(result);
GraphicsDevice.Clear(Color.Black);
//Multiply each color by the source alpha, and write in just the color values into the final texture
BlendState blendColor = new BlendState();
blendColor.ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue;
blendColor.AlphaDestinationBlend = Blend.Zero;
blendColor.ColorDestinationBlend = Blend.Zero;
blendColor.AlphaSourceBlend = Blend.SourceAlpha;
blendColor.ColorSourceBlend = Blend.SourceAlpha;
SpriteBatch spriteBatch = new SpriteBatch(GraphicsDevice);
spriteBatch.Begin(SpriteSortMode.Immediate, blendColor);
spriteBatch.Draw(file, file.Bounds, Color.White);
spriteBatch.End();
//Now copy over the alpha values from the PNG source texture to the final one, without multiplying them
BlendState blendAlpha = new BlendState();
blendAlpha.ColorWriteChannels = ColorWriteChannels.Alpha;
blendAlpha.AlphaDestinationBlend = Blend.Zero;
blendAlpha.ColorDestinationBlend = Blend.Zero;
blendAlpha.AlphaSourceBlend = Blend.One;
blendAlpha.ColorSourceBlend = Blend.One;
spriteBatch.Begin(SpriteSortMode.Immediate, blendAlpha);
spriteBatch.Draw(file, file.Bounds, Color.White);
spriteBatch.End();
//Release the GPU back to drawing to the screen
GraphicsDevice.SetRenderTarget(null);
return result as Texture2D;
}
Run Code Online (Sandbox Code Playgroud)
首先,纹理从流加载.然后,我更改了一些混合选项以达到行为,例如ContentPipeline加载的纹理.不幸的是,以这种方式获得的纹理,在游戏窗口最小化后消失.我读了一些关于这个问题的东西,很多东西都表明RenderTarget2D是错误的,因为渲染目标毕竟设置为null.我应该怎么做以永久保持我的纹理?
好的,我使用了第四个选项,它的工作非常完美.这是固定的代码:
private Texture2D LoadTextureStream(string filePath)
{
Texture2D file = null;
Texture2D resultTexture;
RenderTarget2D result = null;
try
{
using (System.IO.Stream titleStream = TitleContainer.OpenStream(filePath))
{
file = Texture2D.FromStream(GraphicsDevice, titleStream);
}
}
catch
{
throw new System.IO.FileLoadException("Cannot load '" + filePath + "' file!");
}
PresentationParameters pp = GraphicsDevice.PresentationParameters;
//Setup a render target to hold our final texture which will have premulitplied alpha values
result = new RenderTarget2D(GraphicsDevice, file.Width, file.Height, true, pp.BackBufferFormat, pp.DepthStencilFormat);
GraphicsDevice.SetRenderTarget(result);
GraphicsDevice.Clear(Color.Black);
//Multiply each color by the source alpha, and write in just the color values into the final texture
BlendState blendColor = new BlendState();
blendColor.ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue;
blendColor.AlphaDestinationBlend = Blend.Zero;
blendColor.ColorDestinationBlend = Blend.Zero;
blendColor.AlphaSourceBlend = Blend.SourceAlpha;
blendColor.ColorSourceBlend = Blend.SourceAlpha;
SpriteBatch spriteBatch = new SpriteBatch(GraphicsDevice);
spriteBatch.Begin(SpriteSortMode.Immediate, blendColor);
spriteBatch.Draw(file, file.Bounds, Color.White);
spriteBatch.End();
//Now copy over the alpha values from the PNG source texture to the final one, without multiplying them
BlendState blendAlpha = new BlendState();
blendAlpha.ColorWriteChannels = ColorWriteChannels.Alpha;
blendAlpha.AlphaDestinationBlend = Blend.Zero;
blendAlpha.ColorDestinationBlend = Blend.Zero;
blendAlpha.AlphaSourceBlend = Blend.One;
blendAlpha.ColorSourceBlend = Blend.One;
spriteBatch.Begin(SpriteSortMode.Immediate, blendAlpha);
spriteBatch.Draw(file, file.Bounds, Color.White);
spriteBatch.End();
//Release the GPU back to drawing to the screen
GraphicsDevice.SetRenderTarget(null);
resultTexture = new Texture2D(GraphicsDevice, result.Width, result.Height);
Color[] data = new Color[result.Height * result.Width];
Color[] textureColor = new Color[result.Height * result.Width];
result.GetData<Color>(textureColor);
for (int i = 0; i < result.Height; i++)
{
for (int j = 0; j < result.Width; j++)
{
data[j + i * result.Width] = textureColor[j + i * result.Width];
}
}
resultTexture.SetData(data);
return resultTexture;
}
Run Code Online (Sandbox Code Playgroud)
非常感谢您的帮助!
这个开始让我疯了.我已经回答了这个问题,所以很多次.所以我会努力使这一个明确.
这个问题不断回归,因为 - 首先XNA记录很少 - 但也因为人们不断在教程和论坛中发布这样的代码,并坚持认为它"好"因为它似乎有效...直到你最小化窗口和所有你的纹理丢失了!另外还有一种误解 - 通过在GPU上完成工作 - 它必须更快(可能不是).
在C#/ CPU层,a RenderTarget2D 是 a Texture2D.在as Texture2D你的方法到底有没有做什么都不准确.你做的演员可能是隐含的.强制转换不会对引用的对象实例进行任何更改.您可以将其强制转换为a RenderTarget2D,同样,它不会更改对象本身.
RenderTarget2D继承的原因Texture2D是您可以将渲染目标传递给任何需要纹理并使其正常工作的方法.但它们的底层功能有一些重要的区别:
在Direct3D/GPU层,正在发生的是您收到"设备丢失"错误,因为您使用的设备上下文消失了(由于窗口被最小化 - 但这不是唯一的事情这可能导致它).这意味着您将丢失正在使用的所有GPU内存 - 包括纹理和渲染目标.
常规Texture2D(使用ContentManager.Load或加载Texture2D.FromStream或设置SetData)维护数据的CPU端副本.因此,当设备丢失时,XNA将自动从CPU端副本重新创建该纹理的内容.
但是RenderTarget2D完全保留在GPU上.如果丢失,XNA无法重新创建它.获取其内容的CPU端副本需要在更改时从GPU返回极其昂贵的副本.
选项1是总是在每个帧的开始重新呈现渲染目标的内容.这是使用渲染目标的标准方法,因为您通常会让内容更改每个帧.不适用于您的情况.
选项2是RenderTarget2D.ContentLost通过重新创建渲染目标的内容来响应事件.(或者:IsContentLost每帧检查标志.)
选项3是创建纹理的CPU端副本.基本上从渲染目标中获取数据GetData.然后创建一个新的Texture2D并将数据设置到其上SetData.然后,XNA将为您处理任何设备损失(如上所述).
选项4是根本不使用渲染目标!使用GetData让您的纹理数据,在软件执行转换,然后将其放回SetData.看到纹理数据无论如何都会被复制 - 为什么不自己复制并同时预先复制呢?
选项5是替换FromStream为加载时预乘的东西.这与选项4类似,但可以节省一些副本.可能是矫枉过正.
选项6是首先以预乘格式存储纹理.虽然此时你也可以使用内容管道.
就个人而言,我可能会根据您的情况选择选项4.
最后:不要忘记调用Dispose的任何资源(纹理,渲染目标等),您自己创建的(有new或FromStream,但不是从ContentManager)你已经使用完.
我注意到,在你的代码中,你正在泄漏Texture2D file.
为了节省一些精神错乱,我将在我的答案中添加一个简单的,未经测试的方法,它完全在CPU上预处理纹理:
public static void PremultiplyTexture(Texture2D texture)
{
Color[] buffer = new Color[texture.Width * texture.Height];
texture.GetData(buffer);
for(int i = 0; i < buffer.Length; i++)
{
buffer[i] = Color.FromNonPremultiplied(
buffer[i].R, buffer[i].G, buffer[i].B, buffer[i].A);
}
texture.SetData(buffer);
}
Run Code Online (Sandbox Code Playgroud)