Setting up
Having played with a few GLSL shaders in C++, I thought that moving to a DirectX/HLSL solution sould be fairly simple.
Creating the most simple pixel shader was easy enough, and SlimDX is a decent wrapper for DX in C# – so after an hour or so I had the standard working triangle floating in space. I wanted to go further (obviously), and having spent the last few days delving into the demoscene and finding the most amazing site for pixelshader techniques (http://www.iquilezles.org/www/index.htm) I wanted to have a shader to work with (x,y) coordinates that were between (-1,1) rather than absolute screen coordinates.
This requires passing the current render area width and height to the shader to normalise with. This is done using Constant Buffers and on the shader side looks like:
cbuffer ConstBuffer : register(c0) { float2 resolution; } // Simple vertex shader float4 VShader(float4 position : POSITION) : SV_POSITION { return position; } // Pixel shader float4 PShader(float4 position : SV_POSITION) : SV_Target { // Get normalised (x,y) float2 p = -1 + 2 * (position.xy/resolution); // Spikes in the corner float c = abs(p.x*p.y); return float4(c,c,c,1.0f); }
To get the resolution variable into the register for the shader to use, you must create a ConstantBuffer in the program code and assign it to the shade. The Constant Buffer must be a size which is divisible by 16, so if your data is too small, just put it in a bigger buffer.
// Create data stream, we only need 8 bytes, but round up to 16 var resolution = new DataStream(16, true, true); // Fill the stream with width/height info - I'm using a renderform resolution.Write(new Vector2(form.ClientSize.Width, form.ClientSize.Height)); // Rewind the stream resolution.Position = 0; // Create and bind a buffer context.PixelShader.SetConstantBuffer(new Buffer(device, //Device resolution, //Stream 16, // Size // Flags ResourceUsage.Dynamic, BindFlags.ConstantBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, 4), 0); // Register
This lets us run the above shader, giving us:
Bonus
Having played on shader toy, I repurposed the Deform effect for a static image.
Shader:
cbuffer ConstBuffer : register(c0) { float2 resolution; } // Simple vertex shader float4 VShader(float4 position : POSITION) : SV_POSITION { return position; } // Pixel shader float4 PShader(float4 position : SV_POSITION) : SV_Target { // Get normalised (x,y) float2 p = -1 + 2 * (position.xy/resolution); // Deform focus float2 m = float2(0.2f, 0.1f); // Deform float a1 = atan((p.y-m.y)/(p.x-m.x)); float r1 = sqrt(dot(p-m,p-m)); float a2 = atan((p.y+m.y)/(p.x+m.x)); float r2 = sqrt(dot(p+m,p+m)); float2 uv; uv.x = 0.2 + (r1-r2)*0.25; uv.y = sin(2.0*(a1-a2)); float w = r1*r2*0.8; float c2 = abs(uv.x*uv.y); float4 col = float4(c2,c2,c2,1.0f); return float4(col.xyz/(0.1+w),1.0f); }
Result: