Справочная информацияC#/XNA/HLSL - Применение пиксельных шейдеров на 2D спрайтов влияет на другие спрайты на том же цели визуализации
Я только начал изучать HLSL и решил проверить, что я узнал из Интернета написав простую 2D-игру XNA 4.0 bullet-hell.
Я написал пиксельный шейдер, чтобы изменить цвет пуль.
Вот эта идея: оригинальная текстура пули в основном черная, белая и красная. С помощью моего пиксельного шейдера пули могут быть намного более красочными.
Но, я не знаю, как и когда шейдер применяется на SpriteBatch в XNA 4.0, и когда он заканчивается. Это может быть причиной проблемы. В XNA 3.x были pass.begin() и pass.end(), но pass.apply() в XNA 4.0 смущает меня.
Кроме того, это первый раз, когда я использую renderTarget. Это может вызвать проблемы.
Симптом
Он работает, но только если есть пули того же цвета в списке пули. Если выдаются пули разных цветов, это создает неправильные цвета.
Похоже, что пиксельный шейдер не применяется к текстуре пули, а применяется к рендерингу, содержащему все визуализированные маркеры.
Для примера: Здесь у меня есть красные пуль и синие пули. Последняя созданная пуля - синяя. Кажется, что пиксельный шейдер добавляет синий цвет на красные, делая их сине-фиолетовыми.
Если я постоянно создаю пули, красные пуль будут переключаться между красным и сине-фиолетовым. (Я считаю, что синие также переключение, но не очевидно.)
Код
Поскольку я новичок в 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.)
Спасибо. Я пробовал это и да! Оно работает! Ура! (Цель рендеринга используется для аддитивно-смешающих патронов, так что они не будут затронуты фоновым изображением.) Пока я знал, что использование слишком большого числа функций Begin()/End() может повредить производительность, я попытался использовать только большой, чтобы отображать все объекты, но, к сожалению, это вызывает проблему. –