You can create your own post-processing components by using both C# and shaders.
Your post-processing component needs to be added to the same GameObject as the Camera and the other built-in effects.
The most basic C# component for any post processing effect uses Command Lists. You can have the Command List hook into any available render stage.
In this example you can draw a fullscreen quad using your shader with CommandList.Blit and Material.FromShader.
You're not limited to only drawing a fullscreen quad either, and can use anything in the CommandList class.
namespace Sandbox;
[Title( "My Post Processing" )]
[Category( "Post Processing" )]
[Icon( "grain" )]
public sealed class MyPostProcessing : PostProcess, Component.ExecuteInEditor
{
[Property] public Color Color { get; set; };
protected override Stage RenderStage => Stage.AfterTransparent;
protected override int RenderOrder => 100;
protected override void UpdateCommandList()
{
// Pass the Color property to the shader
CommandList.Attributes.Set( "mycolor", Color );
// Grab the FrameBuffer to later pass to the shader
CommandList.GrabFrameTexture( "ColorBuffer" );
// Blit a quad across the entire screen with our custom shader
CommandList.Blit( Material.FromShader( "shaders/mypostprocess.shader" ));
}
}
Here's a very basic shader that will output the passed color from our C# attribute. The framebuffer is also passed here which can be sampled. More on shaders.
MODES
{
VrForward();
}
FEATURES
{
}
struct VertexInput
{
float3 vPositionOs : POSITION < Semantic( PosXyz ); >;
float2 vTexCoord : TEXCOORD0 < Semantic( LowPrecisionUv ); >;
};
struct PixelInput
{
float2 vTexCoord : TEXCOORD0;
#if ( PROGRAM == VFX_PROGRAM_VS )
float4 vPositionPs : SV_Position;
#endif
#if ( ( PROGRAM == VFX_PROGRAM_PS ) )
float4 vPositionSs : SV_Position;
#endif
};
VS
{
PixelInput MainVs( VertexInput i )
{
PixelInput o;
o.vPositionPs = float4( i.vPositionOs.xy, 0.0f, 1.0f );
o.vTexCoord = i.vTexCoord;
return o;
}
}
PS
{
RenderState( DepthWriteEnable, false );
RenderState( DepthEnable, false );
// Passed framebuffer if you want to sample it
Texture2D g_tColorBuffer < Attribute( "ColorBuffer" ); SrgbRead( true ); >;
float3 vMyColor < Attribute("mycolor"); >;
float4 MainPs( PixelInput i ) : SV_Target0
{
return float4( vMyColor, 1 );
}
}