2012-06-02 82 views
3

背景資料C#/新華社/ HLSL - 應用二維精靈像素着色器會影響同其他精靈渲染目標

我剛開始學習HLSL和決定測試什麼,我已經從網上了解到通過編寫一個簡單的2D XNA 4.0子彈地獄遊戲。

我寫了一個像素着色器來改變子彈的顏色。

這裏的想法是:子彈的原始紋理主要是黑色,白色和紅色。在像素着色器的幫助下,子彈可以更加豐富多彩。

Idea

但是,我不知道如何以及何時在XNA 4.0 spriteBatch應用的着色器,以及何時結束。這可能是問題的原因。 XNA 3.x中有pass.begin()和pass.end(),但XNA 4.0中的pass.apply()讓我感到困惑。

另外,這是我第一次使用renderTarget。它可能會導致問題。

症狀

它的工作原理,但前提是在項目列表相同顏色的子彈。 如果呈現不同顏色的子彈,它會產生錯誤的顏色。

像素着色器似乎並未應用於子彈紋理,而是應用於包含所有渲染項目符號的renderTarget。

舉例: Screenshot 在這裏,我有一些紅色的子彈和藍色的子彈。最後創建的子彈是藍色的。看起來像素着色器在紅色上增加了藍色,使它們變成了藍紫色。

如果我不斷創建子彈,紅色子彈將顯示爲在紅色和藍紫色之間切換。 (我相信藍色的也切換,但不是很明顯。)

代碼

由於我是新來的HLSL,我真的不知道我有什麼提供。 以下是我相信或不知道它們是否與問題相關的所有事情。

C# - 敵人的子彈(或只是子彈):

protected SpriteBatch spriteBatch; 
protected Texture2D texture; 
protected Effect colorEffect; 
protected Color bulletColor; 
... // And some unrelated variables 

public EnemyBullet(SpriteBatch spriteBatch, Texture2D texture, Effect colorEffect, BulletType bulletType, (and other data, like velocity) 
{ 
    this.spriteBatch = spriteBatch; 
    this.texture = texture; 
    this.colorEffect = colorEffect; 
    if(bulletType == BulletType.ARROW_S) 
    { 
     bulletColor = Color.Red; // The bullet will be either red 
    } 
    else 
    { 
     bulletColor = Color.Blue; // or blue. 
    } 
} 

public void Update() 
{ 
    ... // Update positions and other properties, but not the color. 
} 

public void Draw() 
{ 
    colorEffect.Parameters["DestColor"].SetValue(bulletColor.ToVector4()); 
    int l = colorEffect.CurrentTechnique.Passes.Count(); 
    for (int i = 0; i < l; i++) 
    { 
     colorEffect.CurrentTechnique.Passes[i].Apply(); 
     spriteBatch.Draw(texture, Position, sourceRectangle, Color.White, (float)Math.PI - rotation_randian, origin, Scale, SpriteEffects.None, 0.0f); 
    } 
} 

C# - 子彈經理:

private Texture2D bulletTexture; 

private List<EnemyBullet> enemyBullets; 
private const int ENEMY_BULLET_CAPACITY = 10000; 

private RenderTarget2D bulletsRenderTarget; 

private Effect colorEffect; 

... 

public EnemyBulletManager() 
{ 
    enemyBullets = new List<EnemyBullet>(ENEMY_BULLET_CAPACITY); 
} 

public void LoadContent(ContentManager content, SpriteBatch spriteBatch) 
{ 
    bulletTexture = content.Load<Texture2D>(@"Textures\arrow_red2"); 

    bulletsRenderTarget = new RenderTarget2D(spriteBatch.GraphicsDevice, spriteBatch.GraphicsDevice.PresentationParameters.BackBufferWidth, spriteBatch.GraphicsDevice.PresentationParameters.BackBufferHeight, false, SurfaceFormat.Color, DepthFormat.None); 

    colorEffect = content.Load<Effect>(@"Effects\ColorTransform"); 
    colorEffect.Parameters["ColorMap"].SetValue(bulletTexture); 
} 

public void Update() 
{ 
    int l = enemyBullets.Count(); 
    for (int i = 0; i < l; i++) 
    { 
     if (enemyBullets[i].IsAlive) 
     { 
      enemyBullets[i].Update(); 
     } 
     else 
     { 
      enemyBullets.RemoveAt(i); 
      i--; 
      l--; 
     } 
    } 
} 

// This function is called before Draw() 
public void PreDraw() 
{ 
    // spriteBatch.Begin() is called outside this class, for reference: 
    // spriteBatch.Begin(SpriteSortMode.Immediate, null); 

    spriteBatch.GraphicsDevice.SetRenderTarget(bulletsRenderTarget); 
    spriteBatch.GraphicsDevice.Clear(Color.Transparent); 

    int l = enemyBullets.Count(); 
    for (int i = 0; i < l; i++) 
    { 
     if (enemyBullets[i].IsAlive) 
     { 
      enemyBullets[i].Draw(); 
     } 
    } 

    spriteBatch.GraphicsDevice.SetRenderTarget(null); 
} 

public void Draw() 
{ 
    // Before this function is called, 
    // GraphicsDevice.Clear(Color.Black); 
    // is called outside. 

    spriteBatch.Draw(bulletsRenderTarget, Vector2.Zero, Color.White); 

    // spriteBatch.End(); 
} 

// This function will be responsible for creating new bullets. 
public EnemyBullet CreateBullet(EnemyBullet.BulletType bulletType, ...) 
{ 
    EnemyBullet eb = new EnemyBullet(spriteBatch, bulletTexture, colorEffect, bulletType, ...); 
    enemyBullets.Add(eb); 
    return eb; 
} 

HLSL - 影響\ ColorTransform.fx

float4 DestColor; 

texture2D ColorMap; 
sampler2D ColorMapSampler = sampler_state 
{ 
    Texture = <ColorMap>; 
}; 

struct PixelShaderInput 
{ 
    float2 TexCoord : TEXCOORD0; 
}; 

float4 PixelShaderFunction(PixelShaderInput input) : COLOR0 
{ 
    float4 srcRGBA = tex2D(ColorMapSampler, input.TexCoord); 

    float fmax = max(srcRGBA.r, max(srcRGBA.g, srcRGBA.b)); 
    float fmin = min(srcRGBA.r, min(srcRGBA.g, srcRGBA.b)); 
    float delta = fmax - fmin; 

    float4 originalDestColor = float4(1, 0, 0, 1); 
    float4 deltaDestColor = originalDestColor - DestColor; 

    float4 finalRGBA = srcRGBA - (deltaDestColor * delta); 

    return finalRGBA; 
} 

technique Technique1 
{ 
    pass ColorTransform 
    { 
     PixelShader = compile ps_2_0 PixelShaderFunction(); 
    } 
} 

我會感謝任何人都可以幫助解決問題。 (或者優化我的着色器,我對HLSL知之甚少)

回答

2

在XNA 4中,您應該直接將效果傳遞給SpriteBatch,如Shawn Hargreaves' Blog中所述。

這就是說,在我看來,問題是,在將你的項目符號渲染到bulletsRenderTarget之後,然後使用相同的spriteBatch繪製RenderTarget,並且最後一個效果仍然有效。這將解釋爲什麼整個圖像被塗成藍色。

解決方法是使用SpriteBatch的兩個Begin()/ End()傳遞,一個帶有效果,另一個不帶。或者,不要單獨使用RenderTarget來開始,這在這種情況下似乎毫無意義。

我也是一個像素着色器的初學者,所以,只是我的2C。

+0

謝謝。我試過了,是的!有用!乾杯! (渲染目標用於添加混合項目符號,以便它們不會受到背景圖像的影響。)只要我知道使用太多的Begin()/ End()會傷害性能,我只嘗試使用大的渲染所有對象,但不幸的是,它會導致問題。 –