3d - XNA/Monogame,绘制多个剪切/倾斜 Sprite 的最快方法

标签 3d xna drawing monogame spritebatch

我通常使用 SpriteBatch在 XNA/Monogame 中用于 2d 游戏,最近刚刚深入研究了 3D 绘图方法,例如 DrawUserIndexedPrimatives之类的。我正在做一个项目,我们的动画师希望能够剪切 Sprite 和纹理。

SpriteBatch您可以在 SpriteBatch 上传递一个矩阵开始剪切一个物体。就像是:

//translate object to origin
Matrix translate1 = Matrix.CreateTranslation(-rectangle.X, -rectangle.Y, 0);

//skew the sprite 33 degrees on the X and Y axis
Matrix skew = Matrix.Identity;
skew.M12 = (float)Math.Tan(33 * 0.0174532925f);
skew.M21 = (float)Math.Tan(33 * 0.0174532925f);

//translate object back
Matrix translate2 = Matrix.CreateTranslation(rectangle.X, rectangle.Y, 0);
Matrix transform = translate1 * skew * translate2;

_spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied,
                    SamplerState.PointWrap, DepthStencilState.Default,
                    RasterizerState.CullCounterClockwise, null, transform);
_spriteBatch.Draw(_texture, rectangle, Color.White);
_spriteBatch.End();

这个明显的缺点是它需要你创建一个新的 SpriteBatch每个剪切 Sprite 的开始和结束调用。我们目前只需调用 SpriteBatch 2 次电话即可。从我们的游戏开始。一个用于 UI,一个用于 World 的东西。我们的美工想要使用剪切来处理摇摆的树木或为生物的腿和四肢设置动画,所以如果我们给他们选择的话,我可以看到这个数字会跃升至 10 多个单独的批处理。

平均水平有大约 250 个元素,每个元素包含 10-20 个 Sprite 。

我已经为 Android 编写了一个测试,该测试调用了 1000 个 Sprite 。在没有任何倾斜的情况下,它可以在大约 11 秒或大约 53fps 内绘制全部 1000、600 次。但是,如果我每十个 Sprite 倾斜一次(添加 100 个新的 SpriteBatch 调用),则需要 47 秒,或大约 12fps。

这真的很糟糕。即使只有 200 个 Sprite (每十分之一倾斜),测试也会下降到 28fps。

因此,我还使用 DrawUserIndexedPrimitives 绘制的 Quads 创建了相同的测试。 .每个 Quad 使用共享的 BasicEffect在 Game 类中创建并通过 Sprite 传入类构造函数。我在每个 pass.Apply() 之前设置了 World Matrix 和 Texture像这样:
if (_basicEffect != null)
{
     foreach (EffectPass pass in _basicEffect.CurrentTechnique.Passes)
     {
        _basicEffect.World = Transform;
        _basicEffect.Texture = _texture;
        pass.Apply();

        GraphicsDevice.DrawUserIndexedPrimitives
            <VertexPositionNormalTexture>(
            PrimitiveType.TriangleList,
            _quad.Vertices, 0, 4,
            _quad.Indices, 0, 2);
}

对于 1000 个 Sprite ,没有倾斜,这给了我 12fps(我想这就像进行 1000 spriteBatch 调用)。这真的很糟糕。但是对于每 10 个 Sprite 倾斜的只有 200 个 Sprite ,我得到 46fps,这明显优于 SpriteBatch (即使我打了DrawUserIndexedPrimitives 200 次)。

- -我的问题 - -

我怎样才能将我的调用批量发送到 DrawUserIndexedPrimitives (或类似的东西)同时保持我的 Sprite 每个都包含在他们自己的继承DrawableGameComponent的类中?由于我们游戏引擎的性质以及它处理动画、碰撞和其他东西的方式,最后一部分非常重要。

我已经阅读了有关 Vertex Buffers 和 DrawIndexedPrimitives 的内容。 ,但我的头并没有完全围绕它,也不知道如何为以这种方式绘制的 Sprite 分配新的纹理和世界变换。

我是否应该期待与 SpriteBatch 类似/更好的性能?如果我批处理这些电话?

最佳答案

在我看来,你有几个选择,在这里。请注意,我主要熟悉 PC 上的 XNA 4.0,因此在您的情况下,并非所有这些都是可能的/高性能的。

简单的黑客方式

绘制 Sprite 时,您似乎没有使用颜色 channel ;此技术假定您的示例代表您的真实代码。

如果您不需要 Sprite 颜色来为 Sprite 着色,您可以劫持它作为将每个 Sprite 数据传递到自定义顶点/像素着色器的一种方式。例如,您可以这样做:

var shearX = MathHelper.ToRadians(33) / MathHelper.TwoPi;
var shearY = MathHelper.ToRadians(33) / MathHelper.TwoPi;
var color = new Color(shearX, shearY, 0f, 0f);
_spriteBatch.Draw(_texture, rectangle, color);

这将 x 和 y 剪切值表示为 2 * pi 的因子分别存储在红色和绿色 channel 中。

然后,您可以创建一个自定义顶点着色器来检索这些值并即时执行剪切计算。参见 Shawn Hargreaves 的文章 here有关如何执行此操作的信息。

混合方法

另一种相对简单的可能性是将传统的 Sprite 批处理与您的DrawUserIndexedPrimitives结合起来。代码。

良好性能的关键是最大限度地减少状态变化,因此仔细排列你的 sprite 可以有很长的路要走。组织你的 Sprite ,这样你就可以使用SpriteBatch一次绘制所有非倾斜的 Sprite 。 , 那么只使用较慢的 DrawUserIndexedPrimitives绘制真正需要它的 Sprite 的技术。假设给定帧中的大多数 Sprite 没有倾斜,这应该会显着减少发送到 GPU 的批处理数量。

批处理 + 自定义顶点格式

这可能是最好的技术,但它也涉及编写最多的代码。并不是说其中任何一个都特别复杂。

一路SpriteBatch内部工作是它维护一个动态顶点缓冲区,该缓冲区填充在 CPU 上,然后在一次调用中全部绘制。肖恩·哈格里夫斯 (Shawn Hargreaves) 提供了有关如何完成此类事情的高级概述 here .

扩展 DrawUserIndexedPrimitives 的问题使用这种技术是讨厌的世界矩阵;着色器并没有一个很好的方法将特定的世界矩阵附加到特定的 Sprite (除非您使用的是硬件实例化,我认为您的平台不支持)。所以,你可以做什么?

如果您创建自定义顶点格式,您可以将剪切值附加到每个顶点,并使用这些值在顶点着色器中执行剪切,就像第一种技术一样。这将允许您在一次调用中绘制所有游戏的 Sprite ,这应该非常快。

您可以找到有关自定义顶点声明的信息 here .

关于3d - XNA/Monogame,绘制多个剪切/倾斜 Sprite 的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14526403/

相关文章:

c# - 我如何优化这个像素不透明度计算?

XNA 不显示我自己的 fbx 模型的纹理

windows-phone-7 - 使用 Rx 从 TouchLocation 流中解释 "double tap"

ios - Objective-C 通过绘图裁剪图像

Java 代码 - 纸牌

python - 如何使用 python 中绘制的 3D 参数函数创建动画

德尔福立方体旋转

javascript - 基于对象的 Canvas javascript 事件

java - 在 LibGDX 中组合多种 Material \纹理

javascript - 使用 THREE.js 通过坐标自定义 3D 对象