2012-06-02 2 views
3

Справочная информацияC#/XNA/HLSL - Применение пиксельных шейдеров на 2D спрайтов влияет на другие спрайты на том же цели визуализации

Я только начал изучать HLSL и решил проверить, что я узнал из Интернета написав простую 2D-игру XNA 4.0 bullet-hell.

Я написал пиксельный шейдер, чтобы изменить цвет пуль.

Вот эта идея: оригинальная текстура пули в основном черная, белая и красная. С помощью моего пиксельного шейдера пули могут быть намного более красочными.

Idea

Но, я не знаю, как и когда шейдер применяется на SpriteBatch в XNA 4.0, и когда он заканчивается. Это может быть причиной проблемы. В XNA 3.x были pass.begin() и pass.end(), но pass.apply() в XNA 4.0 смущает меня.

Кроме того, это первый раз, когда я использую 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 вы затем рисуете этот RenderTarget, используя тот же spriteBatch, с последним эффектом, все еще находящимся в действии. Это объясняет, почему все изображение окрашено в синий цвет.

Решение состоит в использовании двух проходов Begin()/End() SpriteBatch, один с эффектом, а другой без. Или просто не используйте отдельный RenderTarget для начала, что кажется бессмысленным в этом случае.

Я также очень начинаю с пиксельными шейдерами, так что просто мой 2c.

+0

Спасибо. Я пробовал это и да! Оно работает! Ура! (Цель рендеринга используется для аддитивно-смешающих патронов, так что они не будут затронуты фоновым изображением.) Пока я знал, что использование слишком большого числа функций Begin()/End() может повредить производительность, я попытался использовать только большой, чтобы отображать все объекты, но, к сожалению, это вызывает проблему. –

Смежные вопросы