C# 写入RenderTarget后,如何高效地克隆输出?
XNA noob在这里,每天学习。我刚刚研究了如何使用RenderTarget2D将多个纹理合成为一个纹理。但是,尽管我可以在大多数情况下将RenderTarget2D用作纹理2D,但有一个关键区别:当调整backbuffer的大小时(毫无疑问,在其他情况下,如内存不足的图形设备),这些渲染纹理将丢失 目前,我只是将完成的RenderTarget2D复制到一个新的非易失性Texture2D对象中。不过,我的代码非常复杂。有没有比这更优雅的方法?也许我只是累了,但我在谷歌上找不到答案 稍微简化:C# 写入RenderTarget后,如何高效地克隆输出?,c#,xna,texture2d,render-to-texture,C#,Xna,Texture2d,Render To Texture,XNA noob在这里,每天学习。我刚刚研究了如何使用RenderTarget2D将多个纹理合成为一个纹理。但是,尽管我可以在大多数情况下将RenderTarget2D用作纹理2D,但有一个关键区别:当调整backbuffer的大小时(毫无疑问,在其他情况下,如内存不足的图形设备),这些渲染纹理将丢失 目前,我只是将完成的RenderTarget2D复制到一个新的非易失性Texture2D对象中。不过,我的代码非常复杂。有没有比这更优雅的方法?也许我只是累了,但我在谷歌上找不到答案 稍微简化:
public static Texture2D MergeTextures(int width, int height, IEnumerable<Tuple<Texture2D, Color>> textures)
{
RenderTarget2D buffer = new RenderTarget2D(_device, width, height);
_device.SetRenderTarget(buffer);
_device.Clear(Color.Transparent);
SpriteBatch spriteBatch = new SpriteBatch(_device);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied);
// Paint each texture over the one before, in the appropriate color
Rectangle rectangle = new Rectangle(0, 0, width, height);
foreach (Tuple<Texture2D, Color> texture in textures)
spriteBatch.Draw(texture.Item1, rectangle, texture.Item2);
spriteBatch.End();
_device.SetRenderTarget((RenderTarget2D)null);
// Write the merged texture to a Texture2D, so we don't lose it when resizing the back buffer
// This is POWERFUL ugly code, and probably terribly, terribly slow
Texture2D mergedTexture = new Texture2D(_device, width, height);
Color[] content = new Color[width * height];
buffer.GetData<Color>(content);
mergedTexture.SetData<Color>(content);
return mergedTexture;
}
公共静态纹理2D合并纹理(int-width、int-height、IEnumerable纹理)
{
RenderTarget2D缓冲区=新的RenderTarget2D(_设备、宽度、高度);
_device.SetRenderTarget(缓冲区);
_设备。清晰(彩色。透明);
SpriteBatch SpriteBatch=新SpriteBatch(_设备);
spriteBatch.Begin(SpriteSortMode.Immediate、BlendState.NonPremultiplied);
//在前一个纹理上以适当的颜色绘制每个纹理
矩形=新矩形(0,0,宽度,高度);
foreach(纹理中的元组纹理)
绘制(texture.Item1,矩形,texture.Item2);
spriteBatch.End();
_device.SetRenderTarget((RenderTarget2D)null);
//将合并的纹理写入Texture2D,以便在调整后缓冲区大小时不会丢失它
//这是一个强大而丑陋的代码,而且可能非常非常缓慢
Texture2D mergedTexture=新的Texture2D(_设备、宽度、高度);
颜色[]内容=新颜色[宽度*高度];
获取数据(内容);
mergedTexture.SetData(内容);
返回合并文本;
}
我想我应该检查IsContentLost并根据需要重新渲染,但是这发生在我的主要绘图循环中间,当然你不能嵌套分批。我可以维护一个“渲染待办事项”列表,在主SpriteBatch结束后处理它们,然后它们就可以用于下一帧。这是首选策略吗
这段代码只调用了几次,所以性能不是一个问题,但我想学习如何正确处理。事实上,如果您在正常加载内容(游戏开始、级别更改、房间更改等)时,在一次性过程中生成纹理,那么您的代码并没有那么糟糕。你们在CPU和GPU之间传输纹理,和加载普通的ol纹理一样。它很简单而且很有效 如果您更频繁地生成纹理,并且它开始成为每帧成本,而不是加载时间成本,那么您将需要担心它的性能,并可能将它们保留为渲染目标
你不应该在绘图中间获得<代码>内容丢失<代码>,这样你就可以安全地响应这个事件,然后重新创建渲染目标。或者,您可以在每一帧上检查
IsContentLost
,最好是在渲染其他内容之前在帧的开头。无论哪种方式,都应该在SpriteBatch
开始之前检查所有内容
(通常在使用渲染目标时,您会在每一帧中重新生成它们,因此在这种情况下不需要检查它们。)如果在其他地方绘制渲染目标时遇到动态调整渲染目标大小的问题,您可以使用设置大小的屏幕外rendertarget,将完成的RT复制为如下所示:
Rendertarget2D offscreenRT = new RenderTarget2D(_device, width, height);
_device.SetRenderTarget(offscreenRT);
_device.Clear(Color.Transparent);
SpriteBatch spriteBatch = new SpriteBatch(_device);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied);
spriteBatch.Draw(buffer, Vector2.Zero, Color.White);
spriteBatch.End();
_device.SetRenderTarget(null);
替换
Texture2D mergedTexture = new Texture2D(_device, width, height);
Color[] content = new Color[width * height];
buffer.GetData<Color>(content);
mergedTexture.SetData<Color>(content);
return mergedTexture;
因为RenderTarget2D扩展了Texture2D,所以只会返回Texture2D类数据。另外,如果您感兴趣,这里有一个我为用多种纹理构建GUI库的小部件而设计的类。以防你需要经常做这种事情
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;
namespace Voodo.Utils {
/// <summary>
///
/// </summary>
public class TextureBaker {
private readonly SpriteBatch _batch;
private readonly RenderTarget2D _renderTarget;
private readonly GraphicsDevice _graphicsDevice;
/// <summary>
///
/// </summary>
public Rectangle Bounds {
get { return _renderTarget.Bounds; }
}
/// <summary>
///
/// </summary>
/// <param name="graphicsDevice"></param>
/// <param name="size"></param>
public TextureBaker(GraphicsDevice graphicsDevice, Vector2 size) {
_graphicsDevice = graphicsDevice;
_batch = new SpriteBatch(_graphicsDevice);
_renderTarget = new RenderTarget2D(
_graphicsDevice,
(int)size.X,
(int)size.Y);
_graphicsDevice.SetRenderTarget(_renderTarget);
_graphicsDevice.Clear(Color.Transparent);
_batch.Begin(
SpriteSortMode.Immediate,
BlendState.AlphaBlend,
SamplerState.LinearClamp,
DepthStencilState.Default,
RasterizerState.CullNone);
}
#region Texture2D baking
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
public void BakeTexture(Texture2D texture) {
_batch.Draw(
texture,
new Rectangle(0, 0, Bounds.Width, Bounds.Height),
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="destination"></param>
public void BakeTexture(Texture2D texture, Rectangle destination) {
_batch.Draw(
texture,
destination,
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="destination"></param>
/// <param name="source"></param>
public void BakeTexture(Texture2D texture, Rectangle destination, Rectangle source) {
_batch.Draw(
texture,
destination,
source,
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="sourceModification"></param>
/// <param name="destination"></param>
public void BakeTexture(Texture2D texture, System.Drawing.RotateFlipType sourceModification, Rectangle destination) {
Stream sourceBuffer = new MemoryStream();
texture.SaveAsPng(sourceBuffer, texture.Width, texture.Height);
System.Drawing.Image sourceImage = System.Drawing.Image.FromStream(sourceBuffer);
sourceBuffer = new MemoryStream();
sourceImage.RotateFlip(sourceModification);
sourceImage.Save(sourceBuffer, System.Drawing.Imaging.ImageFormat.Png);
_batch.Draw(
Texture2D.FromStream(_graphicsDevice, sourceBuffer),
destination,
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="sourceModification"></param>
/// <param name="destination"></param>
/// <param name="source"></param>
public void BakeTexture(Texture2D texture, System.Drawing.RotateFlipType sourceModification, Rectangle destination, Rectangle source) {
Stream sourceBuffer = new MemoryStream();
texture.SaveAsPng(sourceBuffer, texture.Width, texture.Height);
System.Drawing.Image sourceImage = System.Drawing.Image.FromStream(sourceBuffer);
sourceBuffer = new MemoryStream();
sourceImage.RotateFlip(sourceModification);
sourceImage.Save(sourceBuffer, System.Drawing.Imaging.ImageFormat.Png);
_batch.Draw(
Texture2D.FromStream(_graphicsDevice, sourceBuffer),
destination,
source,
Color.White);
}
#endregion
#region SpriteFont baking
/// <summary>
///
/// </summary>
/// <param name="font"></param>
/// <param name="text"></param>
/// <param name="location"></param>
/// <param name="textColor"></param>
public void BakeText(SpriteFont font, string text, Vector2 location, Color textColor) {
_batch.DrawString(font, text, location, textColor);
}
/// <summary>
///
/// </summary>
/// <param name="font"></param>
/// <param name="text"></param>
/// <param name="location"></param>
public void BakeTextCentered(SpriteFont font, string text, Vector2 location, Color textColor) {
var shifted = new Vector2 {
X = location.X - font.MeasureString(text).X / 2,
Y = location.Y - font.MeasureString(text).Y / 2
};
_batch.DrawString(font, text, shifted, textColor);
}
#endregion
/// <summary>
///
/// </summary>
/// <returns></returns>
public Texture2D GetTexture() {
_batch.End();
_graphicsDevice.SetRenderTarget(null);
return _renderTarget;
}
}
}
使用Microsoft.Xna.Framework;
使用Microsoft.Xna.Framework.Graphics;
使用System.IO;
命名空间Voodo.Utils{
///
///
///
公共类织纹机{
专用只读SpriteBatch\u批处理;
私有只读RenderTarget2D_renderTarget;
专用只读图形设备_GraphicsDevice;
///
///
///
公共矩形边界{
获取{return\u renderTarget.Bounds;}
}
///
///
///
///
///
公共纹理生成器(图形设备图形设备,矢量2大小){
_graphicsDevice=graphicsDevice;
_批次=新的SpriteBatch(_graphicsDevice);
_renderTarget=新建RenderTarget2D(
_图形设备,
(int)size.X,
(int)尺寸(Y);
_graphicsDevice.SetRenderTarget(_renderTarget);
_图形设备。清晰(颜色。透明);
_批处理,开始(
SpriteSortMode。立即,
BlendState.AlphaBlend,
samplestate.LinearClamp,
DepthStencilState.Default,
光栅化状态(CullNone);
}
#区域纹理2D烘焙
///
///
///
///
公共空间烘焙纹理(纹理2D纹理){
_分批抽签(
纹理
新矩形(0,0,Bounds.Width,Bounds.Height),
颜色(白色);
}
///
///
///
///
///
公共空心烘焙纹理(纹理2D纹理,矩形目标){
_分批抽签(
纹理
目的地,
颜色(白色);
}
///
///
///
///
///
///
公共空心烘焙纹理(纹理2D纹理、矩形目标、矩形源){
_分批抽签(
纹理
目的地,
来源:,
颜色(白色);
}
///
///
///
///
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;
namespace Voodo.Utils {
/// <summary>
///
/// </summary>
public class TextureBaker {
private readonly SpriteBatch _batch;
private readonly RenderTarget2D _renderTarget;
private readonly GraphicsDevice _graphicsDevice;
/// <summary>
///
/// </summary>
public Rectangle Bounds {
get { return _renderTarget.Bounds; }
}
/// <summary>
///
/// </summary>
/// <param name="graphicsDevice"></param>
/// <param name="size"></param>
public TextureBaker(GraphicsDevice graphicsDevice, Vector2 size) {
_graphicsDevice = graphicsDevice;
_batch = new SpriteBatch(_graphicsDevice);
_renderTarget = new RenderTarget2D(
_graphicsDevice,
(int)size.X,
(int)size.Y);
_graphicsDevice.SetRenderTarget(_renderTarget);
_graphicsDevice.Clear(Color.Transparent);
_batch.Begin(
SpriteSortMode.Immediate,
BlendState.AlphaBlend,
SamplerState.LinearClamp,
DepthStencilState.Default,
RasterizerState.CullNone);
}
#region Texture2D baking
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
public void BakeTexture(Texture2D texture) {
_batch.Draw(
texture,
new Rectangle(0, 0, Bounds.Width, Bounds.Height),
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="destination"></param>
public void BakeTexture(Texture2D texture, Rectangle destination) {
_batch.Draw(
texture,
destination,
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="destination"></param>
/// <param name="source"></param>
public void BakeTexture(Texture2D texture, Rectangle destination, Rectangle source) {
_batch.Draw(
texture,
destination,
source,
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="sourceModification"></param>
/// <param name="destination"></param>
public void BakeTexture(Texture2D texture, System.Drawing.RotateFlipType sourceModification, Rectangle destination) {
Stream sourceBuffer = new MemoryStream();
texture.SaveAsPng(sourceBuffer, texture.Width, texture.Height);
System.Drawing.Image sourceImage = System.Drawing.Image.FromStream(sourceBuffer);
sourceBuffer = new MemoryStream();
sourceImage.RotateFlip(sourceModification);
sourceImage.Save(sourceBuffer, System.Drawing.Imaging.ImageFormat.Png);
_batch.Draw(
Texture2D.FromStream(_graphicsDevice, sourceBuffer),
destination,
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="sourceModification"></param>
/// <param name="destination"></param>
/// <param name="source"></param>
public void BakeTexture(Texture2D texture, System.Drawing.RotateFlipType sourceModification, Rectangle destination, Rectangle source) {
Stream sourceBuffer = new MemoryStream();
texture.SaveAsPng(sourceBuffer, texture.Width, texture.Height);
System.Drawing.Image sourceImage = System.Drawing.Image.FromStream(sourceBuffer);
sourceBuffer = new MemoryStream();
sourceImage.RotateFlip(sourceModification);
sourceImage.Save(sourceBuffer, System.Drawing.Imaging.ImageFormat.Png);
_batch.Draw(
Texture2D.FromStream(_graphicsDevice, sourceBuffer),
destination,
source,
Color.White);
}
#endregion
#region SpriteFont baking
/// <summary>
///
/// </summary>
/// <param name="font"></param>
/// <param name="text"></param>
/// <param name="location"></param>
/// <param name="textColor"></param>
public void BakeText(SpriteFont font, string text, Vector2 location, Color textColor) {
_batch.DrawString(font, text, location, textColor);
}
/// <summary>
///
/// </summary>
/// <param name="font"></param>
/// <param name="text"></param>
/// <param name="location"></param>
public void BakeTextCentered(SpriteFont font, string text, Vector2 location, Color textColor) {
var shifted = new Vector2 {
X = location.X - font.MeasureString(text).X / 2,
Y = location.Y - font.MeasureString(text).Y / 2
};
_batch.DrawString(font, text, shifted, textColor);
}
#endregion
/// <summary>
///
/// </summary>
/// <returns></returns>
public Texture2D GetTexture() {
_batch.End();
_graphicsDevice.SetRenderTarget(null);
return _renderTarget;
}
}
}