I'm trying to implement a bloom effect. This requires three shader passes: a brightness threshold, a horizontal blur, and a vertical blur. It also requires resizes, but these are irrelevant to the question. I've also omitted the threshold, as the problem lies in the blurs.
Now, I've read that HLSL passes are just syntactic sugar for different shaders, and that switching between them is about as expensive as setting a new effect to the GraphicsDevice
. Still, I prefer to keep them in one .fx file.
The passes:
pass Pass0 { PixelShader = compile ps_2_0 PS_Threshold(); } pass Pass1 { PixelShader = compile ps_2_0 PS_BlurHorizontal(); } pass Pass2 { PixelShader = compile ps_2_0 PS_BlurVertical(); }
The blurring code:
private void Blur(SpriteBatch SpriteBatch, RenderTarget2D Source, RenderTarget2D Rendertarget1, RenderTarget2D Destination) { // Blur pass 1 Contents.BloomEffect.CurrentTechnique.Passes["Pass1"].Apply(); SpriteBatch.GraphicsDevice.SetRenderTarget(Rendertarget1); SpriteBatch.GraphicsDevice.Clear(Color.Black); SpriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, Contents.BloomEffect); SpriteBatch.Draw(Source, Rendertarget1.Bounds, Color.White); SpriteBatch.End(); // Blur pass 2 Contents.BloomEffect.CurrentTechnique.Passes["Pass2"].Apply(); SpriteBatch.GraphicsDevice.SetRenderTarget(Destination); SpriteBatch.GraphicsDevice.Clear(Color.Black); SpriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, Contents.BloomEffect); SpriteBatch.Draw(Rendertarget1, Destination.Bounds, Color.White); SpriteBatch.End(); }
Finally, the Draw loop:
protected override void Draw(GameTime gameTime) { GraphicsDevice.SetRenderTarget(RT1); GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, bloom); spriteBatch.Draw(android, new Rectangle(0, 0, 258, 294), Color.White); spriteBatch.End(); Blur(spriteBatch, RT1, RT11, RT1); GraphicsDevice.SetRenderTarget(null); GraphicsDevice.Clear(Color.Black); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, null, null, null); spriteBatch.Draw(RT1, RT1.Bounds, Color.White); spriteBatch.End(); base.Draw(gameTime); }
The output, however, is as follows:
It's obvious what happens: the last pass is applied twice. I've tested the passes individually by snipping them out of the shader, and they work fine. Is EffectPass.Apply()
simply not meant to be used with SpriteBatch, or is there another way to change what passes are applied (without putting the passes in different .fx files)?
This has all been in XNA. To make this even weirder, when I do the same thing in Monogame the opposite occurs: the shader will only use Pass0. However, I'm converting the shader to OpenGL there, so that might be a conversion quirk.
EDIT: I fixed this by putting the different passes in different techniques, and switching techniques instead of passes. That doesn't answer the original question though, so I'll leave it open.