Table of Contents

Material properties

Every public property of a shader class becomes a material property: a value that each material sets in the Material Inspector, and that the shader code reads.

[Shader]
public class Water : ShaderScript.Lit
{
    [Normal] public Texture2D NormalMap { get; } = Texture2D.Bump;
    [MainColor] public float4 DeepColor { get; } = new float4(0, 0.2f, 0.4f, 1);
    [Range(0, 2)] public float WaveSpeed { get; } = 0.5f;
    ...
}

Declare properties as read-only auto-properties, { get; }. The initial value is the property's default, used by new materials. In shader code, read the property like any other value: WaveSpeed * Time.y.

Property types

C# type Material property Shown as
float, half Float A number field.
int Integer An integer field.
float2, float3, half2, half3 Vector A field with 2 or 3 components.
float4, half4 Vector A field with 4 components, or a color picker with [Color].
an enum type Integer A dropdown of the enum values.
Texture2D Texture A texture slot.
Texture2DArray, Texture3D, Cubemap Texture A texture slot.
float2x2, float3x3, float4x4 — Not shown in the Inspector (see below).
Hdrp.DiffusionProfile Diffusion profile HDRP's diffusion profile field.
bool — Only with [TextureIsSet] (see below).

Default values

  • Numbers and vectors: a literal or a constructor, such as 0.5f or new float4(1, 1, 1, 1). A negative literal like -1f works too.
  • Enums: an enum value.
  • Texture2D: one of the built-in textures, used while no texture is assigned: Texture2D.White, Black, Gray, LinearGray, Red or Bump (a flat normal map, for normal map slots). Without an initial value, a gray texture is used.
  • Texture2DArray, Texture3D and Cubemap can't have an initial value.

Matrices

A matrix property isn't shown in the Inspector and isn't saved with the material, because Unity materials don't store matrices. Set it from a script with Material.SetMatrix, for example every frame.

Names and scripts

A property keeps its C# name in the material, prefixed with an underscore, as is Unity's convention: WaveSpeed becomes _WaveSpeed. Use that name to access the property from scripts:

material.SetFloat("_WaveSpeed", 2f);

Some names are reserved for the settings Shader Script generates itself, such as Surface, Blend or Cull. Using one of them is reported as an error.

Attributes

Attributes change how a property is shown and treated.

Attribute Applies to Effect
[DisplayName("Label")] any The label in the Inspector. "Section/Label" puts the property into a foldout named Section.
[Range(min, max)] float A slider.
[Color] float4, half4 A color picker.
[HDR] color, texture An HDR color picker, which allows values above 1.
[Gamma] float, vector The value is in gamma space and is converted to linear space in a linear-space project, like colors are.
[Toggle] float, int A checkbox: 0 is off, 1 is on.
[KeywordSwitch] enum, float, int A compile-time switch (see below).
[Inline] any but a texture Draws the property on the same line as the texture declared right before it, like Base Color next to Base Map. Up to two per texture.
[NoScaleOffset] Texture2D Hides the texture's Tiling and Offset fields.
[Normal] texture Warns in the Inspector, with a fix button, when the assigned texture isn't imported as a normal map.
[MainTexture] texture Makes it the material's main texture, reached by Material.mainTexture.
[MainColor] float4, half4 Makes it the material's main color, reached by Material.color. Implies [Color].
[HideInInspector] any Hides the property; scripts can still set it.
[PerRendererData] any The value comes from a MaterialPropertyBlock per renderer; shown read-only.
[Space], [Space(pixels)] any Adds space above the property.
[Header("Text")] any Adds a bold heading above the property. The text can contain letters, digits, spaces and underscores.
[Custom("...")] any Adds a ShaderLab attribute as is, for example a custom drawer: [Custom("PowerSlider(3.0)")].

[MainTexture] and [MainColor] matter for code that works with any material without knowing its property names, such as scripts that fade or tint objects. Mark the property that best represents the surface, usually the base color map and the base color.

Sections

By default, the properties are shown in the Surface Inputs foldout, in declaration order. A display name with a section prefix moves a property into a foldout of its own:

[DisplayName("Waves/Speed")] public float WaveSpeed { get; } = 0.5f;
[DisplayName("Waves/Height")] public float WaveHeight { get; } = 0.1f;

All properties with the same prefix end up in the same foldout. For full control over the Inspector, see Material Inspector.

Checkboxes and switches

[Toggle] and [KeywordSwitch] both show a checkbox for a number property, but they work differently:

  • With [Toggle], the shader reads the value at runtime like any other property. Both branches of an if that checks it are compiled.
  • With [KeywordSwitch], the value is a compile-time constant: each value compiles into its own shader variant, and only the branch for the material's value remains. On an enum property, each enum value gets its own variant.

Use [KeywordSwitch] to turn off expensive features without paying for them. See Compile-time switches.

Checking whether a texture is assigned

A bool property with [TextureIsSet] tells whether a texture is assigned to the material, as a compile-time constant:

[NoScaleOffset] public Texture2D MaskMap { get; }
[TextureIsSet(nameof(MaskMap))] public bool HasMaskMap { get; }

// In Frag: the texture isn't sampled at all when no mask is assigned.
float4 mask = HasMaskMap ? MaskMap.Sample(uv) : new float4(1, 1, 1, 1);

The texture must be declared before the flag. The Material Inspector updates the flag automatically. If you assign or remove the texture from a script, also enable or disable the material keyword _SSG_TX_<TextureName>, for example _SSG_TX_MaskMap.