s&box
RustCounter-Strike 2s&boxDeadlock
Wiki
Get s&box on Steam
Browse
OverviewGetting StartedCreating GamesEngine & ToolsDocumentationCommunity GamesResources
s&boxDocumentationRenderingShader GraphCustom NodesC Shader Nodes
SOURCE 2 · C#
Build and play games with s&box.

Facepunch's game engine and creation platform — make games in C#, or jump straight into community games.

Get s&box on Steam

s&box Wiki

  • Overview
  • Getting Started
  • Creating Games
  • Engine & Tools
  • Documentation
  • Community Games
  • Resources

Tools & Community

  • Getting Started
  • Creating Games
  • Engine & Tools
  • Resources

Platforms

  • RustBattle
  • CSBattle
  • Rust Game Wiki
  • CS2 Skins Wiki
  • Deadlock Wiki

About

  • Official Site
  • Developer Wiki
  • GitHub
  • Get on Steam
NOT AFFILIATED WITH FACEPUNCH STUDIOS · © 2026 s&box Wiki (community)
Docs/Rendering
Rendering

C# Shader Nodes

Just like Subgraphs, you can also create your own custom Nodes with C# (and shader code) to be used in Shader Graphs.

Custom Nodes must inherit from the ShaderNode class, and supports [Title], [Description], [Category], and [Icon] attributes.

[Title("My Custom Node"), Category("Custom"), Icon("favorite")]
public class MyCustomNode : ShaderNode
{
    // ...
}

Inputs are NodeInput properties decorated with the [Input] attribute to define the expected input type.


public class MyMathNode : ShaderNode
{
    [Input(typeof(float))]
    [Title("Value A")]
    public NodeInput InputA { get; set; }
    
    [Input(typeof(Vector3))]
    [Title("Direction")]
    public NodeInput Direction { get; set; }
    
    // The default value for InputA, which will be exposed in the Properties tab
    [InputDefault(nameof(InputA))]
    public float DefaultA { get; set; } = 1.0f;
}

Most of your typical Property Attributes can be used here such as [Title], [Range], and even [ShowIf]

Outputs are NodeResult.Func properties with the [Output] attribute to define the expected output type.


public class MyMathNode : ShaderNode
{
    // ...

    [Output(typeof(float))]
    [Title("Result")]
    public NodeResult.Func Result => (GraphCompiler compiler) =>
    {
        var a = compiler.Result(InputA, DefaultA);
        var dir = compiler.Result(Direction, Vector3.Forward);
        
        // Generate shader code
        return new NodeResult( NodeResultType.Float, $"dot({a}, {dir}.x)");
    };
}

Sometimes you'll want a variable amount of Outputs given the Inputs, or even a variable amount of Inputs given changes made in a node's Properties tab.


public class DynamicNode : ShaderNode
{
    [Hide]
    private List<IPlugIn> _dynamicInputs = new();
    
    [Hide]
    private List<IPlugOut> _dynamicOutputs = new();
    
    public override IEnumerable<IPlugIn> Inputs => _dynamicInputs;
    public override IEnumerable<IPlugOut> Outputs => _dynamicOutputs;
    
    // You can modify these lists however you want, whenever you want.
    // This is just a basic example of creating a new plug.
    public void AddInput(string name, Type type)
    {
        var info = new PlugInfo()
        {
            Name = name,
            Type = type,
            DisplayInfo = new DisplayInfo { Name = name }
        };
        _dynamicInputs.Add(new BasePlugIn(this, info, type));
    }
}

Implement IErroringNode to throw custom error messages from your Node when you've detected any invalid uses of it.


public class ValidatedNode : ShaderNode, IErroringNode
{
    [Input(typeof(float))]
    public NodeInput Value { get; set; }
    
    // From IErroringNode
    public List<string> GetErrors()
    {
        var errors = new List<string>();
        
        if (!Value.IsValid)
        {
            // Throw an error so the shader cannot compile until we get a valid input
            errors.Add("Value input is required");
        }
        
        return errors;
    }
}

You can override OnPaint to draw whatever you want on your custom node (like the built-in Binary, Unary and Texture nodes)

public class VisualNode : ShaderNode
{
    public override void OnPaint(Rect rect)
    {
        // Custom node rendering
        Paint.SetPen(Color.White);
        Paint.SetFont("Arial", 12);
        Paint.DrawText(rect, "Custom");
    }

}

Source: Facepunch/sbox-docs (CC-BY-4.0) · updated 2025-08-20. Read it rendered on the official docs.

More in Rendering

AmbientLight
Animated Effects
Attributes and Variables
BeamEffect
Beams
Bindless API
Classes
Command Lists
← All documentation

Community wiki — not affiliated with Facepunch Studios. For the latest and most authoritative information, see the official developer wiki.