C#XNA二维轨迹效应优化

C#XNA二维轨迹效应优化,c#,optimization,xna,2d,sprite,C#,Optimization,Xna,2d,Sprite,目前,作为我游戏中的轨迹效果,我每5帧就有一个精灵的半透明纹理副本添加到轨迹列表中 这些轨迹的alpha值每帧递减一次,绘制函数在列表中迭代并绘制每个纹理。一旦它们达到0 alpha,它们将从列表中删除 结果是在移动的实体后面有一个很好的小轨迹效果。问题是对于大约100多个实体,帧速率开始急剧下降 所有的痕迹纹理都来自同一张雪碧表,所以我不认为这是批处理问题。我分析了代码,在FPS下降峰值期间CPU强度较低,而在正常FPS时CPU强度较低,所以我假设这意味着GPU受到限制 有没有办法更有效地实现

目前,作为我游戏中的轨迹效果,我每5帧就有一个精灵的半透明纹理副本添加到轨迹列表中

这些轨迹的alpha值每帧递减一次,绘制函数在列表中迭代并绘制每个纹理。一旦它们达到0 alpha,它们将从列表中删除

结果是在移动的实体后面有一个很好的小轨迹效果。问题是对于大约100多个实体,帧速率开始急剧下降

所有的痕迹纹理都来自同一张雪碧表,所以我不认为这是批处理问题。我分析了代码,在FPS下降峰值期间CPU强度较低,而在正常FPS时CPU强度较低,所以我假设这意味着GPU受到限制

有没有办法更有效地实现这一效果

以下是我使用的通用代码:

// fade alpha
m_alpha -= (int)(gameTime.ElapsedGameTime.TotalMilliseconds / 10.0f);

// draw
if (m_alpha > 0) {
    // p is used to alter RGB of the trails color (m_tint) depending on alpha value
    float p = (float)m_alpha/255.0f;
    Color blend = new Color((int)(m_tint.R*p), (int)(m_tint.G*p), (int)(m_tint.B*p), m_alpha);               
    // draw texture to sprite batch
    Globals.spriteBatch.Draw(m_texture, getOrigin(), m_rectangle, blend, getAngle(), new Vector2(m_rectangle.Width/2, m_rectangle.Height/2), m_scale, SpriteEffects.None, 0.0f);                
} else {
        // flag to remove from List<>
        m_isDone = true;               
}
//淡入淡出阿尔法
m_alpha-=(int)(gameTime.ElapsedGameTime.totalmicrosides/10.0f);
//画
如果(m_alpha>0){
//p用于根据alpha值改变轨迹颜色(m_色调)的RGB
浮点p=(浮点)m_α/255.0f;
颜色混合=新颜色((int)(m_-tint.R*p),(int)(m_-tint.G*p),(int)(m_-tint.B*p),m_-alpha);
//绘制纹理到sprite批处理
Globals.spriteBatch.Draw(m_纹理,getOrigin(),m_矩形,blend,getAngle(),新矢量2(m_矩形.宽度/2,m_矩形.高度/2),m_比例,SpriteEffects.None,0.0f);
}否则{
//要从列表中删除的标志
m_isDone=真;
}
我想我应该注意,给定给trail类的m_纹理是对所有trail共享的全局纹理的引用。我注意到为每个线索创建硬拷贝


编辑:如果我只是注释掉SpriteBatch.Draw调用,即使我为数百个对象的每一帧分配一条新轨迹,也不会出现帧内插入。。。必须有更好的方法来实现这一点。

通常对于轨迹,在绘制当前帧之前,您只需绘制一个透明的屏幕大小的矩形,而不是在每个帧上清除屏幕。因此,前一帧“变暗”或“颜色模糊”,而新帧完全“清晰”和“明亮”。当重复此操作时,将从以前的所有帧生成一条轨迹,这些帧从不清除,而是“变暗”

这项技术非常有效,它被用于著名的Flurry屏幕保护程序(www.youtube.com/watch?v=pKPEivA8x4g)

为了使轨迹更长,只需增加用于清除屏幕的矩形的透明度。否则,将使其更加不透明以缩短轨迹。但是,请注意,如果通过使矩形太透明而使轨迹太长,则可能会留下一些由于alpha混合而可能无法完全擦除的光轨迹。Flurry屏幕保护程序受到这种人为因素的影响,但是有一些方法可以弥补它

根据您的情况,您可能需要调整该技术。例如,您可能希望有几个图形图层,允许某些对象留下轨迹,而其他对象不生成轨迹

这项技术对于长距离的路径来说比你目前的方法更有效

另一方面,我认为代码中的瓶颈是以下几行:

Globals.spriteBatch.Draw(m_texture, getOrigin(), m_rectangle, blend, getAngle(), new Vector2(m_rectangle.Width/2, m_rectangle.Height/2), m_scale, SpriteEffects.None, 0.0f);   

像Draw()这样的数千个GPU调用效率很低。如果在缓冲区中有一个多边形列表,其中每个多边形都位于正确的位置,并且存储有透明度信息,则效率会更高。然后,只需调用Draw(),就可以使用正确的纹理和透明度渲染所有多边形。很抱歉,我无法为您提供这方面的代码,但如果您想继续您的方法,这可能是您的方向。简而言之,您的GPU一次当然可以绘制数百万个多边形,但它不能多次调用draw()

必须尝试禁用/删除跟踪效果以确保这是原因?是的,删除跟踪效果完全修复了帧速率下降。我认为这些轨迹只是添加了太多的精灵,GPU无法处理……你应该发布一些代码。显然,这样的事情是可能的,粒子系统很容易完成100多个粒子全部消失。你真的每5帧复制一次纹理吗?如果这是真的,停止这样做,只是在不同的alpha值绘制相同的纹理。不要从列表中删除它们,只需更改alpha值,然后根据需要将它们设置回默认值。你目前所做的是缓慢的,因为你正在不断地释放然后重新分配。GPU可以处理多个轨迹,如果它不能…谢谢谢谢,这正是我所希望的答案!阿尔法混合留下的那些伪影,我们如何防止/避免它们?@Pondwater:2个解决方案我知道:(1)找出这些伪影的最终色调,并使原始层具有该颜色。例如,如果工件的颜色为(5,5,5),则不要从黑屏(0,0,0)开始,而是从(5,5,5)开始。虽然不完美,但它很有效&很简单。(2) 您应该能够选择渲染期间使用的alpha混合类型。默认值为“乘法”。应该有一个是“加法”或“减法”的。你所要做的就是每隔一段时间向整个屏幕添加(-1,-1,-1),以确保颜色最终变为(0,0,0)。@Pondwater:希望“减法”混合不会导致颜色的负值。我可以想象XNA将小于0的数字截断为0。产生这些伪影的原因是“乘法”混合。当将两个加权整数相乘并对结果进行四舍五入时,可能永远不会收敛到所需的值。如果伪影的音调与期望的最终值相差1或2个值