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!
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 tweetsAwesome XNA Videos – Lighting, Rendering, and game videos
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, Texture2Dlastprocessorsource) { 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 BlurColor += 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(); }}
Hey, you could probably just place the tick increment in the BeginFrameRendering method ;)
Philippe
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 :)