-
Notifications
You must be signed in to change notification settings - Fork 3
Canvas Shaders
SimulationFramework can compile a subset of .NET CIL into shader languages used by the graphics backend (right now, just glsl).
Here's a simple shader:
using SimulationFramework;
using SimulationFramework.Drawing;
using SimulationFramework.Drawing.Shaders;
class MyShader : CanvasShader
{
public override ColorF GetPixelColor(Vector2 position)
{
return ColorF.Red;
}
}
This shader simply fills everything red.
Every shader must override the GetPixelColor(Vector2)
method in the CanvasShader
class. This is the entry point to your shader. It's called once per pixel for every shape it's used to draw.
To use a shader, just pass an instance of it to ICanvas.Fill
and draw some shapes:
public override void OnRender(ICanvas canvas)
{
MyShader myShader = new MyShader();
canvas.Fill(myShader);
canvas.DrawCircle(Mouse.Position, 50);
}
Shader langauges usually expose most functionality through a set of intrinsic functions. These are accessable via the ShaderIntrinsics
class:
class MyShader : CanvasShader
{
public override ColorF GetPixelColor(Vector2 position)
{
return new ColorF(ShaderIntrinsics.Sin(position.X / 200f), 0, 0, 1);
}
}
⚠️ Right now, only a limited subset of shader intrinsics are supported.
You can pass values to the shader through fields on the shader class.
using SimulationFramework;
using SimulationFramework.Drawing;
using SimulationFramework.Drawing.Shaders;
class MyShader : CanvasShader
{
public ColorF color;
public override ColorF GetPixelColor(Vector2 position)
{
return color;
}
}
To pass the value to the shader, just set the field!
public override void OnRender(ICanvas canvas)
{
MyShader myShader = new MyShader();
myShader.color = ColorF.Orange;
canvas.Fill(myShader);
canvas.DrawCircle(Mouse.Position, 50);
}
you can pass arrays of values to shaders.
‼️ ‼️ There are currently alignment problems with shader fields that are 12 bytes wide (ieCircle
,Vector3
). Don't pass values of these types through arrays as it will not work properly
class MyShader : CanvasShader
{
public Vector4[] circles;
public override ColorF GetPixelColor(Vector2 position)
{
ColorF color = ColorF.Black;
float minDist = float.PositiveInfinity;
for (int i = 0; i < circles.Length; i++)
{
Circle c = default;
c.Position = new(circles[i].X, circles[i].Y);
c.Radius = circles[i].Z;
float d = MathF.Abs(c.SignedDistance(position));
if (d < minDist)
{
minDist = d;
}
}
return color = new(1 - (minDist / 10f) * (minDist / 10f), 0, 0);
}
}
You can sample textures from shaders.
⚠️ Right now, texture sampling coordinates are in UV coordinates (ie 0-1). In future versions, texture sampling will use the texture's pixel coordinates. (UV sampling will still be available viaSampleUV
). To convert from a texture's pixel coordinates to UV coordinates, divide by it's size on each axis.
class MyShader : CanvasShader
{
public ITexture myTexture;
public override ColorF GetPixelColor(Vector2 position)
{
return myTexture.Sample(position * (1f/512f));
}
}
Most typical C# code cannot be reasonably translated into shader code. In these cases, an exception will be thrown when you try to use the shader.
These limitations include:
- Reference types are not allowed (classes, ref structs)
- limited exceptions include arrays and textures, which can be passed to a shader via a field but not assigned to variables or passed into methods.
Allowed types:
bool
int
float
Vector2
Vector3
Vector4
ColorF
- struct types consisting of only the above types
Canvas Shaders are currently in alpha and are therefore not feature complete. This means there are temporary limitations that should be lifted by the full release:
- Early returns are not supported in most cases.
- Most types (even primitives) are not supported.
- Only a limited set of intrinsics are supported.