Attributes are the bridge to pass information around the CPU to the GPU, like Variables, Textures or entire Buffers.
To declare a constant to be exposed to C# you simply append < Attribute("Foo"); >;
to that variable, like for example:
bool NoiseEnabled < Attribute("NoiseEnabled"); >;
float4 NoiseScale < Attribute("NoiseScale"); >;
float4x4 BoxTransform < Attribute("BoxTransform"); >;
Texture2D NoiseTexture < Attribute("NoiseTexture"); >;
This can then be set from C# side with Attributes.Set( "Name", variable );
The Material Editor provides a graphical interface to adjust and control attributes defined in your shaders, By setting a Default[1..4]()
value it will make it show up on the editor.
Certain attributes can have custom UI behaviors. For instance, UiType(Color)
turns a float3 into a color picker. Other UI hints can be used to provide default values and ranges.
float3 TintColor < UiType(Color); Default3(1.0, 1.0, 1.0) >;
The following attributes are supported for constants:
Default(arg1)
or Default1(arg1)
Defines a variable default1 with the value .
Default2(arg1, arg2)
Defines a float2/int2 with values { arg1, arg2 }.
Default3(arg1, arg2, arg3)
Defines a float3/int3 with default values { arg1, arg2, arg3 }.
Default4(arg1, arg2, arg3, arg4)
Defines a float4 with values { arg1, arg2, arg3, arg4 }.
Range( arg )
- Limit the UI to this range
Range2( arg, arg2 )
- Limits the UI of a float2/int2 to this range
Range3( arg, arg2, arg3 )
- Limits the UI of a float2/int2 to this range
Range4( arg, arg2, arg3, arg4 )
- Limits the UI of a float2/int2 to this range
UIType( type )
- Explicitly set which UI type to use for this variable
Slider
Color
CheckBox
DefaultFile( arg )
- Defaults to this file if none is specified, for Textures
You can define an image to use on the material editor UI as follows:
CreateInputTexture2D(
Name,
ColorSpace, // Color space: Linear or SRGB
Precision, // Precision per channel (e.g. 8 for 8-bit)
Format, // Format (e.g., DXT5, BC7)
"Suffix", // File suffix to filter in the UI
"Group,1/10", // UI group and ordering
Default(0.5) // Default value if none is provided
);
For example, this will create an "AlbedoImage" that can be exposed to the texture for compiling, the UI will filter for images that end with _albedo
suffix:
CreateInputTexture2D( AlbedoImage, Linear, 8, "", "_albedo", "Material,10/30", Default( 0.5 ) );
To expose the compiled texture from UI into a Texture2D you can use the following:
Texture2D Albedo < Channel( RGBA, Box( AlbedoImage ) ); >;
This compiles an image into a texture using box filtering for mipmaps.
You can pack channels of multiple images into a single texture specifying which channel the image will be sampled from
Texture2D RMA < Channel( R, Box( Roughness ), Linear );
Channel( G, Box( Metalness ), Linear );
Channel( B, Box( Occlusion ), Linear );
Channel( A, Box( BlendMask ), Linear ); >;
If you want to send a lot of information at once you can use Constant Buffers or Structured Buffers:
private struct Constants
{
public Vector4 Foo;
public Vector4 Bar;
}
private Rendering.CommandList ConstantsCommandList()
{
Rendering.CommandList commands = new("Example Commmand List");
var constants = new Constants();
constants.Foo = new Vector4( 0.0f, 1.0f, 2.0f, 3.0f );
constants.Bar = new Vector4( 4.0f, 5.0f, 6.0f, 7.0f );
commands.SetConstantBuffer( "Constants", constants );
return commands;
}
And in the shader it can just be defined as mirrored on C#, for Constant Buffers the attribute binding for it's name is already implicitly set.
cbuffer Constants
{
float4 Foo;
float4 Bar;
}
You can set attributes of a specific SceneObject, this will affect the materials that are being used on that object when rendering, it does not need to be in a render block.
void UpdateObject()
{
if ( !_sceneObject.IsValid() )
return;
_sceneObject.Attributes.SetCombo( "D_COMBO", 1 );
_sceneObject.Attributes.Set( "Foo", 1.0f );
_sceneObject.Attributes.Set( "Bar", new Vector4( 1.0f, 2.0f, 3.0f, 4.0f ) );
_sceneObject.Attributes.Set( "NoiseTexture", NoiseTexture );
}
You can use attributes on the entire render context or the rest of the pipeline when you set them on a Command List.
protected override void OnEnabled()
{
Camera.AddCommandList( ExampleCommandList(), Rendering.Stage.AfterDepthPrepass );
}
private Rendering.CommandList ExampleCommandList()
{
Rendering.CommandList commands = new("Example Commmand List");
var compute = new ComputeShader( "example_cs" );
commands.Set( "NoiseScale", NoiseScale );
commands.Set( "NoiseTexture", NoiseTexture );
commands.DispatchCompute( compute, ExampleRenderTarget.Size );
// Sets attribute for anything after Rendering.Stage.AfterDepthPrepass
// until end of frame for this view from the result of the compute operation
commands.SetGlobal( "Result", ExampleRenderTarget );
return commands;
}