Version: Unity 6.3 Beta (6000.3)
Language : English
USS transition
USS properties reference

USS filter

You can use USS filters to apply post-processingA process that improves product visuals by applying filters and effects before the image appears on screen. You can use post-processing effects to simulate physical camera and film properties, for example Bloom and Depth of Field. More info post processing, postprocessing, postprocess
See in Glossary
effects to a visual elementA node of a visual tree that instantiates or derives from the C# VisualElement class. You can style the look, define the behaviour, and display it on screen as part of the UI. More info
See in Glossary
, such as blur, color correction, or other custom effects.

Filters use materials to apply effects to a visual element subtree per-pixel. For example, to post-process a red element, its content and children are rendered to a texture, which can then be processed by a shaderA program that runs on the GPU. More info
See in Glossary
. The processed result is then reinserted into the visual element hierarchy. You can expand the processed texture’s bounds with margins to accommodate certain effects. In this example, a blurred texture has enlarged bounds to fit the blur radius.

Process of a blur effect applied to a red element and its child elements
Process of a blur effect applied to a red element and its child elements

Built-in filters

UI Toolkit provides a set of built-in filter functions that you can use to apply common effects to visual elements. They are similar to CSS filters.

filter: blur(<length>) | grayscale(<number>) | invert(<number>) | opacity(<number>) | sepia(<number>) | tint(<color>) | hue-rotate(<angle>) | contrast(<number>)

Note: The tint function is a Unity-specific filter function that tints the element with a color. This function isn’t supported in CSS.

USS doesn’t support the following CSS filter functions:

  • drop-shadow

To apply a filter to a visual element, use the filter property in a USS file. USS filters follow the CSS filter syntax. For example:

.builtin-blur {
    filter: blur(20px);
}

.builtin-blur-invert {
    filter: blur(5px) invert(100%);
}

The following example demonstrates different filter functions effects:

Original image blur(20px) grayscale(100%) invert(100%) opacity(50%) sepia(100%) tint(#ff0000) hue-rotate(90deg) contrast(200%)
Original image Blur filter Grayscale filter Invert filter Opacity filter Sepia filter Tint filter Hue rotate filter Contrast filter

Note: If you define multiple classes, and each class defines a filter, and apply them to an element: instead of combining the effects, the filter applied last takes precedence over the earlier ones. For example, if you have the following classes:

.blur {
    filter: blur(20px);
}

.sepia {
    filter: sepia(100%);
}

And you apply both classes to an element - <engine:VisualElement class="blur sepia" /> - the element only has the sepia effect applied. For more information, refer to Selector precedence.

To apply filters in UI Builder

  1. Open the UI Builder and create a new UXML file or open an existing one.
  2. Select the visual element you want to apply the filter to.
  3. In the Inspector panel, select Inline Styles > Filter.
  4. Click the Add (+) button to add a filter function.
  5. Select a built-in filter function from the dropdown menu, such as blur, grayscale, or invert.
  6. Set the parameters for the filter function, such as the blur radius or grayscale percentage.

Custom filters

You can create custom filters to apply any effect you want to a visual element.

Create a custom filter

To create a custom filter, follow these steps:

  1. Create a FilterFunctionDefinition asset. This asset is a simple ScriptableObject.

  2. Define filter parameters to set input values for the filter. The supported filter parameter types are float and color. When specified in USS, filter parameters such as numeric, pixelsThe smallest unit in a computer image. Pixel size depends on your screen resolution. Pixel lighting is calculated at every screen pixel. More info
    See in Glossary
    , percentages, durations, and angles are converted into float values. Parameters can be set in style or directly in the UI Builder. For example, the blur(5px) filter function has only one parameter: the blur radius. This custom swirl filter function has two parameters, the angle and the radius of the swirl.

  3. Provide an interpolation default value (optional). This value is used for smooth transitions between filter states.

  4. Add effect passes. Filters often need multiple passes to apply an effect. For example, a blur effect typically has separate horizontal and vertical passes. Each effect pass in the filter includes:

  5. The material to execute: In Unity, shaders and materials work together to define how surfaces look. A shader is a program that tells the GPU how to render graphics and determines the effects on surfaces. Materials store the settings that feed into the shader.

  6. The pass index for that material.

  7. Use data binding to link the filter’s parameters to the material properties. For example, to apply a tint effect, bind a color parameter directly to the material’s _Tint property. For more complex parameter handling, use a C# callback.

  8. Set margins (optional). Some effects, such as blur, require extra space around the texture for reading and writing. Define static margins if needed, or use a C# callback to compute margins dynamically.

For an example, refer to Create a custom swirl filter.

Create a custom filter in C# code

To create a custom filter in C#, define a class that inherits from FilterFunction and add the filter’s parameters and effects. The following creates a custom filter in C# for the swirl filter example:

var customSwirl = ScriptableObject.CreateInstance<FilterFunctionDefinition>();

customSwirl.parameters = new[] {
    new FilterParameterDeclaration() {
        interpolationDefaultValue = new FilterParameter(0.0f)
    },
    new FilterParameterDeclaration() {
        interpolationDefaultValue = new FilterParameter(0.0f)
    }
};

customSwirl.passes = new[]
{
    new PostProcessingPass()
    {
        material = SwirlMaterial,
        passIndex = 0,
        parameterBindings = new[] {
            new ParameterBinding() { index = 0, name = "_Angle" },
            new ParameterBinding() { index = 1, name = "_Radius" }
        }
    }
};

Apply the custom filter in stylesheets

To apply your custom filter to a visual element, use the filter property in a USS file. Reference the path to the custom filter asset, followed by the parameters needed for the filter. For example:

.custom-effect {
    filter: filter("/Assets/Filters/CustomFilter.asset" 10px);
}

You can combine custom filters with built-in filters in the same filter property. This allows you to create complex visual effects by stacking filters. For example:

.custom-effect-blur {
    filter: filter("/Assets/Filters/CustomFilter.asset" 10px) blur(25px);
}

Apply custom filters in C# code

The following example shows how to apply a blur filter and a custom filter to a visual element in C#:

// Create a blur filter using the built-in blur filter function
var blur = new FilterFunction(FilterFunctionType.BuiltinBlur);
blur.AddParameter(new FilterParameter(20.0f));

// Create a custom filter function definition
var custom = new FilterFunction(functionDef);
// Add parameters to the custom filter
custom.AddParameter(new FilterParameter(Color.black));

// Apply both the blur and custom filter to the visual element
element.style.filter = new List<FilterFunction> { blur, custom };

Dynamic margin

You can use a C# callback to compute margins dynamically for a filter.

The following C# code snippet sets dynamic margins for a blur function. Since the extra margins depend on the blur radius, static margins aren’t sufficient in this case.

Before calculating the render textureA special type of Texture that is created and updated at runtime. To use them, first create a new Render Texture and designate one of your Cameras to render into it. Then you can use the Render Texture in a Material just like a regular Texture. More info
See in Glossary
size, call this callback. This allows you to specify margins for the given FilterFunction. In this example, the blur radius is read as the function’s first parameter, which is then used to set the margins.

effect.computeRequiredReadMarginsCallback = ComputeMargins;
effect.computeRequiredWriteMarginsCallback = ComputeMargins;

// ...

static PostProcessingMargins ComputeMargins(FilterFunction func)
{
    float radius = func.GetParameter(0).floatValue; // Blur radius value
    return new PostProcessingMargins() {
        left = radius, top = radius, right = radius, bottom = radius
    };
}

Complex parameter

For complex parameters, use a C# callback. The following example demonstrates how to use a callback to handle a filter parameter. It converts a single sepia amount value into a full color matrix. By implementing a callback, you can manually set material properties instead of relying on automatic parameter binding. To achieve this, provide a MaterialPropertyBlock to the callback, which then sets the material properties directly, bypassing the automatic binding process.

effect.prepareMaterialPropertyBlockCallback = PrepareSepiaMatrix;

// ...

static void PrepareSepiaMatrix(MaterialPropertyBlock mpb, FilterFunction func)
{
    float s = func.parameters[0].floatValue; // Sepia value

    var colorMatrix = new Matrix4x4(
        new Vector4(0.393f + 0.607f * (1 - s), 0.349f - 0.349f * (1 - s), 0.272f - 0.272f * (1 - s), 0),
        new Vector4(0.769f - 0.769f * (1 - s), 0.686f + 0.314f * (1 - s), 0.534f - 0.534f * (1 - s), 0),
        new Vector4(0.189f - 0.189f * (1 - s), 0.168f - 0.168f * (1 - s), 0.131f + 0.869f * (1 - s), 0),
        new Vector4(0, 0, 0, 1));

    mpb.SetMatrix("_ColorMatrix", colorMatrix);
}

Custom filter UV region

UI Toolkit renders filtered elements into a texture atlas before applying filter shaders. As a result, the corresponding UV coordinates cover only a subregion of the full 0–1 range. When drawing the filters, the UVs provided to the fragment shader are adjusted to fit the UV region. In simple cases, it is sufficient to sample the texture with the provided UV coordinate.

However, if your custom filter requires UV manipulations, you need to convert the UVs to the 0-1 range and then back to the atlas UV rectangle. To do the conversion, first extract the filter UV rect index from the vertex data with the GetFilterRectIndex function:

#include "UnityUIEFilter.cginc"

v2f vert (FilterVertexInput v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    o.rectIndex = GetFilterRectIndex(v); // Pass filter UV rect index to fragment shader
    return o;
}

In the vertex or fragment shader, you can extract the UV rect with the GetFilterUVRect function, and then remap the UVs to and from the 0-1 range:

float2 NormalizeUVs(float2 uv, float4 uvRect)
{
    // Normalize UV coordinates based on the atlas rect
    return float2(
        (uv.x - uvRect.x) / uvRect.z,
        (uv.y - uvRect.y) / uvRect.w
    );
}

float2 MapToUVRect(float2 uv, float4 uvRect)
{
    // Map UV coordinates to the atlas rect
    return float2(
        uv.x * uvRect.z + uvRect.x,
        uv.y * uvRect.w + uvRect.y
    );
}

fixed4 frag (v2f i) : SV_Target
{
    // Get the UV rect from the index
    float4 uvRect = GetFilterUVRect(i.rectIndex);

    // Convert the atlas UVs to the 0-1 range
    float2 uv = NormalizeUVs(i.uv, uvRect);

    // Do UVs manipulations, (e.g. flip Y axis)
    uv.y = 1.0f - uv.y;

    // Convert the UVs back to the atlas region
    uv = MapToUVRect(uv, uvRect);

    return tex2D(_MainTex, uv);
}

Custom filter and force gamma considerations

When working in a UI Toolkit force gamma workflow in a linear color space (when you enable Force Gamma Rendering in the Panel Settings asset, you need to take extra considerations with the color output of custom filters. In this workflow, the last filter applied must linearize the color before output. UI Toolkit automatically enables the UIE_OUTPUT_LINEAR keyword on the material when the last filter is applied, allowing you to check for this keyword to determine when to linearize the color. Note that this is only necessary with the force gamma workflow.

fixed4 frag(v2f IN) : SV_Target
{
    half4 col = tex2D(_MainTex, IN.uv);

    // Apply filter effect ...

    // Linearize the color for the force gamma workflow
    #if UIE_OUTPUT_LINEAR
    col.rgb = GammaToLinearSpace(col.rgb);
    #endif

    return col;
}

USS filter transitions

USS filter transitions follow the CSS filter transition syntax, enabling smooth visual effects based on filter properties.

Matching filter functions

For a smooth transition, the filter functions in both states must match exactly, including the order and type of functions.

The following example creates a smooth transition between two states with matching filter functions. When you hover over the element, the blur and invert functions transition smoothly.

.effect {
    filter: blur(0px) invert(0%);
}

.effect:hover {
    filter: blur(10px) invert(100%);
}

Partially matching filter functions

When filter functions partially match, the system interpolates common functions and applies new ones. For example, the following example transitions between a blur and invert function.

.effect {
    filter: blur(0px); /* invert(0%) */
}

.effect:hover {
    filter: blur(10px) invert(100%);
}

In this example, the system pads the initial filter with an invert function, using a default interpolation value (0%). When you create a custom filter, you can specify the interpolation value in the asset definition.

Mismatched filter functions

If the filter functions don’t match in type or order—such as shown in the following example, transitioning from invert() and blur() to a blur() and invert()—interpolation won’t happen. Instead, the transition abruptly changes to the new filter state.

.effect {
    filter: invert(0%) blur(0px);
}

.effect:hover {
    filter: blur(10px) invert(100%);
}

Additional resources

USS transition
USS properties reference