Extending the Post Processing

rated by 0 users
This post has 4 Replies | 3 Followers

Top 50 Contributor
Posts 98
SunBurn_Community_Licensee
Travon Santerre Posted: 10-19-2009 9:55 AM

So one task on my list of things to do this week is extend the Sunburn Post Processing.  If I am correct the HDR post processing affect does HDR and Bloom, what is a good starting point for creating my own sepia/blur/ etc effects.  I see in the Sunburn API that there is a few objects that look they might help, but I can't find much on the forums about this.  I can always dig up my old stuff I did but I am looking to make a component that is just like the HDR effect in the way it integrates into sunburn.

Thanks for the help!

Top 10 Contributor
Posts 4,854
Employee
SunBurn_Studio_Licensee

Hi Travon,

The post processing works like a stack of full screen effects each with its own render target.

This explanation is a little more involved than necessary, but I think it helps to know what's going on internally during post processing.


Overview

When BeginFrameRendering is called a processor is responsible for setting its render target as the active target, which is then used by the main rendering code to draw the scene, or (if other processors exist above it in the stack) by the processor directly above it in the stack.

During EndFrameRendering a processor minimally needs to restore the previously active render target (the one active before setting its own as active), resolve its render target, and return the texture.  During this call the processor should also do some sort of processing, usually using its resolved texture as source information and the previously active (and once again active) render target as the destination of the output.


The Easy Way

It's generally best to derive from BaseRenderTargetPostProcessor as it does everything mentioned above for you, including juggling the render targets.

Simply override EndFrameRendering with some code like this:


        public override Texture2D EndFrameRendering(Texture2D mastersource, Texture2D lastprocessorsource)
        {
            Texture2D source = base.EndFrameRendering(mastersource, lastprocessorsource);
           
            // do some post processing, rendering directly to the device as the targets are already swapped for you...
           
            return source;
        }


The parameters mastersource and lastprocessorsource are generally not needed, but provide two additional images to use during post processing, including the main rendered scene before any post processing and the composite image before the last post processor.  These can be null if no processors are above the processor in the stack.

There are two abstract members you'll need to implement SupportedTargetFormats and SupportedSourceFormats, these should contain the render target formats which are compatible with your post processing technique (for both the processor's render target and targets in the stack above the processor).


Let me know if this helps!

Follow me on Twitter – development and personal tweets
Awesome XNA Videos – Lighting, Rendering, and game videos

Top 25 Contributor
Posts 201
SunBurn_Community_Licensee
SunBurn_Contributor
SunBurn_Pro_Licensee

Here's the code for a post processor i've just written... It's a kind of broken glitchy night vision effect.

One thing to note with this post processor though is that it has an Update method which needs to be called since the effect is dynamic based on time. Other than that it should be a good start for developing post processors.

The C# side...

public class GlitchPostProcessor : BaseRenderTargetPostProcessor
    {
        private SpriteBatch _spriteBatch;     
        private Rectangle _fullScreenRectangle;
        private Effect _blurEffect;
        private MutantGame _game;
        private float _tick;
        private Random _rnd;
 
        public GlitchPostProcessor(MutantGame game, IGraphicsDeviceService graphics)
            : base(graphics)
        {
            _game = game;
            _rnd = new Random();
        }
 
        public override SurfaceFormat[] SupportedSourceFormats
        {
            get
            {
                return new SurfaceFormat[] { SurfaceFormat.Color };
            }
        }
 
        public override SurfaceFormat[] SupportedTargetFormats
        {
            get {
                return new SurfaceFormat[] { SurfaceFormat.Color };
            }
        }
 
        public override void ApplyPreferences(ILightingSystemPreferences preferences)
        {
            throw new NotImplementedException();
        }
 
        public override bool Initialize(List<SurfaceFormat> availableformats)
        {
            _supportedFormats = availableformats;
            _spriteBatch = new SpriteBatch(base.GraphicsDeviceManager.GraphicsDevice);
            Viewport viewport = base.GraphicsDeviceManager.GraphicsDevice.Viewport;
            _fullScreenRectangle = new Rectangle(0, 0, viewport.Width, viewport.Height);
            _blurEffect = _game.Content.Load<Effect>("Effects\\Template");
            return base.Initialize(availableformats);
        }
 
        public override void BeginFrameRendering(ISceneState scenestate)
        {
            base.BeginFrameRendering(scenestate);
        }
 
        public override Texture2D EndFrameRendering(Texture2D mastersource, Texture2D
lastprocessorsource)
        {
            Texture2D source = base.EndFrameRendering(mastersource, lastprocessorsource);            
            Render(source);
            return source;
        }
 
        private void Render(Texture2D texture) {
            _blurEffect.Begin();
            _blurEffect.CurrentTechnique.Passes[0].Begin();
            _blurEffect.Parameters["Tick"].SetValue(_tick);
            _blurEffect.Parameters["GlitchStart"].SetValue((float) _rnd.NextDouble());
            _blurEffect.Parameters["GlitchStop"].SetValue((float) _rnd.NextDouble());
            _blurEffect.Parameters["Blur"].SetValue((float)_rnd.NextDouble()/100);
 
            _spriteBatch.Draw(texture, _fullScreenRectangle, Color.White);
            _blurEffect.CurrentTechnique.Passes[0].End();
            _blurEffect.End();
            _spriteBatch.End();
        }
 
        public void Update(GameTime gameTime)
        {
            _tick+=1f;
        }
    }
 
 And the Shader

sampler TextureSampler : register(s0);
 
float Tick;
float GlitchStart = 0.5;
float GlitchStop = 0.6;
float Blur = 0.002;
 
float4 PS(float2 texCoord : TEXCOORD0) : COLOR0
{
float4 Color;
if (texCoord.y > GlitchStart && texCoord.y < GlitchStop ) {
texCoord.x+= sin((texCoord.y - GlitchStart) * 5); 
}
texCoord.x+= (sin(150*texCoord.y))/(500 * GlitchStart);
Color = tex2D(TextureSampler, texCoord);         
// Slight Blur
Color += tex2D(TextureSampler, texCoord.xy + (Blur));
Color += tex2D(TextureSampler, texCoord.xy + (Blur * 2));
Color += tex2D(TextureSampler, texCoord.xy + (Blur * 3));
Color = Color/4;
float noise = texCoord.x * texCoord.y * Tick * 1000;
noise = fmod(noise, 13) * fmod(noise, 123);
float dx = fmod(noise, 0.01);
float3 cResult = Color.rgb + Color.rgb * saturate(0.1f + dx.xxx * 100);       
 
cResult.rgb = dot(cResult.rgb, float3(0.2,0.2,0.2));       
cResult.g *= 3;
Color = float4(cResult,Color.a);
   return Color;

 
technique Glitch 
{
pass PostProcess

PixelShader = compile ps_2_0 PS(); 
}
}

Top 10 Contributor
Posts 1,214
SunBurn_Community_Licensee
SunBurn_Contributor
SunBurn_Pro_Licensee

Hey, you could probably just place the tick increment in the BeginFrameRendering method ;)

Philippe

Top 25 Contributor
Posts 201
SunBurn_Community_Licensee
SunBurn_Contributor
SunBurn_Pro_Licensee

That would make a lot of sense and keep everything self contained , the shader code is pretty gnarly too - probably a lot easier way to achieve the effect :)

Page 1 of 1 (5 items) | RSS