首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >写入RenderTarget后,如何有效地克隆输出?

写入RenderTarget后,如何有效地克隆输出?
EN

Stack Overflow用户
提问于 2011-04-01 04:49:25
回答 3查看 4.8K关注 0票数 9

每天都在这里学习。我刚刚想出了如何使用RenderTarget2D将多个纹理组合成一个。然而,虽然我可以在大多数情况下使用RenderTarget2D作为Texture2D,但有一个关键的区别:当调整缓冲区的大小时,这些呈现的纹理会丢失(毫无疑问,在其他情况下,比如内存不足的图形设备)。

目前,我只是将完成的RenderTarget2D复制到一个新的非易失性Texture2D对象中。不过,我这么做的代码是相当糟糕的。有什么更优雅的方法吗?也许我只是累了,但是我在Google上找不到答案。

略为简化:

代码语言:javascript
复制
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;
    }

我想我应该根据需要检查IsContentLost和重新呈现,但是这发生在我的主绘图循环的中间,当然您不能嵌套SpriteBatches。我可以维护一个“呈现TODO”列表,在主SpriteBatch结束之后处理这些列表,然后它们就可以用于下一个帧了。这是首选的策略吗?

这段代码只被调用了几次,所以性能不是问题,但是我想学习如何正确地处理事情。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-04-01 05:50:03

实际上,如果您在一次性过程中生成纹理,而通常加载内容(游戏开始、级别更改、空间更改等),代码就不会那么糟糕。你在CPU和GPU之间传输纹理,这和加载普通的纹理是一样的。这很简单,而且很有效!

如果您正在更频繁地生成纹理,并且它开始成为每帧成本,而不是负载时间成本,那么您将需要担心它的性能,并可能将它们保留为渲染目标。

您不应该在绘图过程中获得ContentLost,因此您可以安全地响应该事件并重新创建呈现目标。或者,您可以在它们上检查IsContentLost,最好是在呈现任何其他内容之前先在框架的开始处进行检查。无论哪种方式,都应该在SpriteBatch开始之前检查所有内容。

(通常在使用呈现目标时,您会重新生成每个帧,因此在这种情况下不需要检查它们。)

票数 4
EN

Stack Overflow用户

发布于 2011-05-01 07:36:49

替换

代码语言:javascript
复制
Texture2D  mergedTexture = new Texture2D(_device, width, height);
Color[]    content       = new Color[width * height];
buffer.GetData<Color>(content);
mergedTexture.SetData<Color>(content);
return mergedTexture;

使用

代码语言:javascript
复制
return buffer;

因为RenderTarget2D扩展了Texture2D,所以您将只返回Texture2D类数据。另外,如果您感兴趣,这里有一个类,用于用多个纹理构建GUI库的小部件。以防你经常做这种事。

代码语言:javascript
复制
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;
        }
    }
}
票数 2
EN

Stack Overflow用户

发布于 2011-04-01 11:02:53

如果在将呈现目标绘制到其他地方时出现了动态调整其大小的问题,则只需设置一个屏幕外呈现对象,您就可以复制完成的RT如下所示:

代码语言:javascript
复制
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);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5509265

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档