Been spending the last couple of days working on some geometric pixel shaders that I can use for various in game lighting effects to further juice up my explosions etc.
These may well be of use to others so I thought I’d get them into a serviceable state and do a mini-tutorial on their usage. OK, maybe ‘tutorial’ is too grand a word but I’ve commented the code thoroughly at least! Links to the HLSL source files for these shaders are included at the bottom of this article (scroll down).
I’m assuming the reader has a basic knowledge of HLSL – if not then there’s an excellent introductory tutorial here.
The shaders provided both draw a user-defined number of concentric shapes. The stroke width and spacing between the shapes can be set via user-defined parameters, as can the amount the spacing and stroke width increases at each iteration.
A parameter ‘multiply_increments’ allows the user to set whether the spacing/stroke width increment as applied linearly (by addition) or exponentially (by multiplication).
The supplied texture is used to draw the shapes (I often use a 2×2 white square), a user-defined tint can be applied to this.
All sizes, widths etc are calculated as a proportion of the texture size so usually between 0.0f and 1.0f though you can go larger than 1.0f if you wish some of your outer shape to be drawn outside of the texture (and therefore cropped).
Setting the shader parameters from your .net code would look something like the code below. Adjust these parameters over time to get the kind of trippy effects you see in some of the example GIFs. Maybe you could smooth these parameter changes using LERPing?

Two Geometric Shaders Overlaid
Microsoft.Xna.Framework.Graphics.Effect shader = Game.Content.Load (“circles”);
// The tint that will be applied to the texture – set all values
// to 1.0 to leave the texture untouched
Microsoft.Xna.Framework.Vector4 tint;
tint.W = 1.0f; // alpha – 0.0f – 1.0f
tint.X = 1.0f; // red – 0.0f – 1.0f
tint.Y = 1.0f; // green – 0.0f – 1.0f
tint.Z = 1.0f; // blue – 0.0f – 1.0f
shader.Parameters [“tint”].SetValue(tint);
// The size of the first shape to be drawn
shader.Parameters [“size”].SetValue ( 1.0f );
// The stroke width of the first shape to be drawn
shader.Parameters [“strokewidth”].SetValue ( 0.1f );
// The initial spacing between shapes
shader.Parameters [“spacing”].SetValue ( 0.1f );
// The number of shapes
shader.Parameters [“repeats”].SetValue ( 3 );
// The amount by which spacing increases for each consecutive shape drawn
shader.Parameters [“spacing_increment”].SetValue ( 0.0f );
// The amount by which stroke width increases for each consecutive shape drawn
shader.Parameters [“strokewidth_increment”].SetValue ( 0.0f );
// Whether the spacing/stroke width increment as applied linearly (by addition)
// or exponentially (by multiplication).
shader.Parameters [“multiply_increments”].SetValue ( false );
// Adjust depending on how you’re doing your rendering
SpriteBatch.Begin (…);
shader.CurrentTechnique.Passes[0].Apply ();
SpriteBatch.End (…);
Probably also worth mentioning are the settings required to get the ‘endless loop’ effect you see in these GIFs. It’s pretty straightforward if the spacing and stroke width of shapes is consistent, but if not you need to tween the strokewidth and spacing so that they are the same for the second shape at the end of the loop as they were for the first shape at the start of the loop. It took me a while to get my head round this.
The code below shows some example values – don’t try and cut/paste this as it uses my own tweening classes and a wrapper class for the shader itself. It should be good enough to get an idea of how to set things up though…
// Initial stroke width relative to texture size
float width = 0.0025f;
// My wrapper class for the shader
shader = GeometryShader.CircleShader ();
// Used by my wrapper class – the size I’m drawing the texture on screen
shader.ScaleX = 506;
shader.ScaleY = 506;
// Set up initial spacing and stroke width for the shader
shader.Spacing = width;
shader.StrokeWidth = width;
// Spacing and stroke width will increase by 50% for each concentric shape drawn
shader.SpacingIncrement = 1.5f;
shader.StrokeWidthIncrement = 1.5f;
shader.MultiplyIncrements = true;
// Sets up the values to tween the size of the outer shape over a 30 frame seamless loop
// First two values are the start and end size
shader.TweenSize = new Tween (1.0f, 1.0f + shader.Spacing/shader.SpacingIncrement + shader.StrokeWidth/shader.StrokeWidthIncrement, 30, Tween.Linear);
// Sets up the values to tween the spacing over a 30 frame seamless loop
// First two values are the start and end spacing
shader.TweenSpacing = new Tween (width, width / shader.SpacingIncrement, 30, Tween.Linear);
// Sets up the values to tween the stroke width over a 30 frame seamless loop
// First two values are the start and end stroke width
shader.TweenStrokeWidth = new Tween (width, width / shader.StrokeWidthIncrement, 30, Tween.Linear);
And here are the actual HLSL source files. Note that I am pretty much a beginner at this stuff myself so I make no guarantees as to the suitability of this code for any purpose and I would welcome any contributions towards making it execute more efficiently.
I have plans to add more shape types at a later stage and combine these into one uber-shader that also also shapes to be combined in different ways. Watch this blog for updates…
circles.fx | squares.fx
If this is of use to you I’d welcome more followers on Twitter.
Dev Time: 2 days
Total Dev Time: approx 123 days
previous|next

Two Circle Shaders Slightly Offset

Square Shader With Additional Rotation Applied