XNA 4.0在一个批次中使用多个效果



我正在为XNA 4.0开发图形引擎,遇到了一个找不到任何解决方案的问题。目前我正在尝试使用着色器来实现灯光效果。图形引擎还包括一个粒子引擎,因此对我来说,至少考虑性能是非常重要的。这两件事的结合造成了问题。

首先,我做了很多阅读和研究,据我所知,你打的平局越少,你的表现就越好。所谓绘制调用,我的意思是当spritebatch将实际的几何体和纹理发送到GPU时。因此,我试图在一个批次中绘制尽可能多的

现在问题来了。我的发动机绘图方法有2个过载:

public static void Draw(Texture texture)
{
    // Call spriteBatch.Draw(...);
}
public static void Draw(Light light)
{
    // Call spriteBatch.Draw(...);
}

第一个重载只是用默认着色器绘制一个普通纹理。第二个重载将绘制使用另一个着色器的灯光。我想做的是:

public static void Draw(Texture texture)
{
    // Use default shader
    // Call spriteBatch.Draw(...);
}
public static void Draw(Light light)
{
    // Use light shader
    // Call spriteBatch.Draw(...);
}

然而,SpriteBatch不支持同时使用多个着色器,因此我尝试使用效果传递来实现这一点:

public static void Draw(Texture texture)
{
    effect.CurrentTechnique.Passes[0].Apply();
    // Call spriteBatch.Draw(...);
}
public static void Draw(Light light)
{
    effect.CurrentTechnique.Passes[1].Apply();
    // Call spriteBatch.Draw(...);
}

这也不起作用,因为在调用spriteBatch.Draw()时不使用着色器,而是在调用sprite Batch.End()时使用着色器。因此,上面的代码使用我上次应用的pass呈现了所有内容。

我的第三次尝试是使用2个SpriteBatches:

public static void Begin()
{
    spriteBatchColor.Begin([...], null); // Default shader.
    spriteBatchLight.Begin([...], effect); // Light shader.
}
public static void Draw(Texture texture)
{
    // Call spriteBatchColor.Draw(...);
}
public static void Draw(Light light)
{
    // Call spriteBatchLight.Draw(...);
}
public static void End()
{
    spriteBatchColor.End();
    spriteBatchLight.End();
}

这确实有效,但我的层深度完全混乱了,这并不奇怪,因为spriteBatchLight.End()是最后一个调用的,所以它总是绘制在spriteBatchColor绘制的所有内容之上。

我知道我可以将SpriteSortMode设置为SpriteSortMode。立即,但我会受到很大的性能损失,因为该模式直接绘制所有内容。由于性能在这种情况下非常重要,因此我想通过对GPU进行尽可能少的绘制调用来优化,SpriteSortMode.立即不是我的选择。

所以,我的问题是:

有没有办法让我在一个批次内使用两种不同的效果/技术/传球?或者有没有什么方法可以让我使用两个不同的SpriteBatches,但在最后绘制时将它们组合在一起,这样层深度就不会变得一团糟?

哇,不要理会我之前的回答,因为它大错特错。

实际的方法是在主游戏类中创建一个List<RenderTarget2D>,并将其传递给正在绘图的函数/类。

发动机内绘图方法:

public static void Draw(Texture texture, List<RenderTarget2D> targetList)
{
    RenderTarget2D target = new RenderTarget2D(...);
    //Tell spriteBatch to draw to target instead of the screen
    spriteBatch.GraphicsDevice.SetRenderTarget(target);
    //Make the renderTarget's background clear
    spriteBatch.GraphicsDevice.Clear(new Color(0,0,0,0)) 
    spriteBatch.Begin();
    //Apply effect and draw
    spriteBatch.End();
    targetList.Add(target);
}

然后将每个纹理绘制到屏幕上。

游戏内抽奖方式:

void Draw()
{
    List<RenderTarget2D> targetList = new List<RenderTarget2D>();
    engine.Draw(texture, targetList);
    engine.Draw(light, targetList);
    //Tell spriteBatch to draw to the screen
    spriteBatch.GraphicsDevice.SetRenderTarget(null)
    //Make the screen background black
    spriteBatch.GraphicsDevice.Clear(Color.Black)
    spriteBatch.Begin();
    //Draw each target in target list to screen
    spriteBatch.End();
}

最新更新