Skip to content

FredMoreau/RSUV-Bit-Packer

Repository files navigation

RSUV Bit Packer - Overview

Using Renderer Shader User Value (RSUV), introduced in Unity 6.3, allows setting unique properties per renderer, to be used in their material shaders, with no performance cost when using the SRP Batcher (and GPU Resident Drawer).

The RSUV is a uint (32 bit unsigned integer), which requires packing data on the C# renderer side, and unpacking data on the HLSL shader side. While packing and unpacking data is trivial, the RSUV Bit Packer (package) aims at providing a user friendly workflow to design packing scheme and set Renderer Properties in the Editor or from a C# script.

Getting Started

  • Open the Package Manager, click on the + Icon, select "Add Package from Git Url".
    • Enter the package url:
      • git@github.com:FredMoreau/RSUV-Bit-Packer.git, or
      • https://github.com/FredMoreau/RSUV-Bit-Packer.git
  • Or clone the repo, select "Add Package from disk" and select the package.json file.
  • Or download as a zip, and unzip it in your project's Packages folder.

Then install the Samples.

Workflow

The package allows you to define a packing scheme by adding "Renderer Properties", easily changing precision to fit in the 32 bits, then pack those properties and assign them to Renderers's RSUV and eventually fetch the properties' values in Shader Graph, using:

  • a Property Sheet (asset) that eventually generates a Shader Include using the new Shader Function Reflection API to fetch the data in Shader Graph.
  • a Property Packer (component) that sets the value on renderers and exposes properties to Animation, C# and even Visual Scripting.

Property Sheet

Create a new Property Sheet using Assets/Create/Rendering/RSUV Bit Packer/Property Sheet. This asset allows defining a packing scheme to be reused with several Property Packers later.

Properties values serve as default values when assigning the sheet to a Packer component, and as preview values in Shader Graph.

Adding properties to a Property Sheet.

Capacity

Some properties are fixed sized, like the Boolean Property which will only use one bit. Other properties feature settings that allows adjusting their size, like the Scalar Property that features a precision setting.

At the top of a Renderer Property List, the Help Box displays how many bits are used, and the Add Menu grays out the fixed size properties that cannot fit.

Cannot add more properties.

Max Capacity.

If the properties exceed the capacity, the Help Box turns into a warning, and any property that doesn't fit turns red.

Over Capacity.

Shader Includes

The Property Sheet also allows generating a Shader Include (HLSL) to access the properties in Shader Graph.

Using Generated Nodes in Shader Graph.

If the "Split Functions" option is enabled, it'll generate one function per property, otherwise it'll generate one function for all properties.

Split Nodes in Shader Graph. Meta Node in Shader Graph.

Unity 6.3 - 6.4

In Unity 6.3 and 6.4, Shader Includes are generated using the Shader Graph Custom Function syntax for them to be used with Custom Function Nodes.

Unity 6.5 and above

In Unity 6.5, Shader Includes are generated using the Shader Function Reflection API syntax, which makes them automatically accessible in Shader Graph without having to manually configure a Custom Function Node.

Note: in Unity 6.5, to avoid potential collision with other HLSL functions, generated Shader Includes are namespaced, using the Assembly Definition namepace the hlsl file belongs to, if any, or the Project's root namespace.

Property Packer

Add a Property Packer to a GameObject using Component/Rendering/RSUV Bit Packer/Property Packer, and assign the Renderers it shall set the RSUV to.

For quick prototyping, the list of Renderer Properties can be defined in the component itself, and some generic shader functions are provided to fetch them in Shader Graph, providing the bit offset.

Adding properties to a Property Packer.

Assigning a Property Sheet on a Property Packer will make it inherit the properties from the sheet. Which allows using the same Property Sheet with several Property Packers for consistency. If the property sheet is further edited, the Packers will display a warning and a button to update their properties.

Setting a Property Packer with a Property Sheet.

Animation and Scripting

Properties are packed and set on the renderer when modified and/or animated in the Editor and at Runtime.

The API PropertyPacker.TrySetProperty() allows setting properties from C# at Runtime.

Note, it is good practice to store a property index using PropertyPacker.GetPropertyIndex(string propertyName) and use PropertyPacker.TrySetProperty(int index, value).

It is also preferred to use the generic method PropertyPacker.TrySetProperty<T>(int index, T value).

The main purpose of the non generic PropertyPacker.TrySetProperty(int index, object value) is for Visual Scripting support.

Visual Scripting

For the PropertyPacker component's methods to appear in Visual Scripting, Unity.RSUVBitPacker must be added to the Node Library in Project Settings/Visual Scripting.

Property Types

Boolean

A boolean value stored on one bit.

Integer

An integer value stored with custom precision. The length determines the maximum value. E.g.: 4 bits = max value 15, 5 bits = max value 31.

Scalar

A float value stored with custom precision.

Enum

Similar to int but displays as a dropdown. The precision depends on the number of entries. E.g.: 2 entries = 1 bit, 3-4 entries = 2 bits, 5-8 entries = 3 bits, and so on.

Color24 (RGB)

Stores a color over 24 bits (8 bits per channel).

Color (HSV)

Stores a color over variable precision per HSV channels. If the saturation or value precision is set to 0, it defaults to one.

Extras

Color Palette

A Color Palette is an asset that allows defining colors and generating an HLSL Shader Include.

Defining a Color Palette.

The generated HLSL allows getting a color from the palette in Shader Graph.

Getting color from Palette in Shader Graph.

Samples

To install samples, go to the Package Manager, select the package in the list and go to the Samples tab. The Samples use TextMeshPro. If no text appear in the scenes, go to Window/TextMeshPro/Import TMP Essential Resources. Some examples use OnMouseEnter() and OnMouseExit(), which only work with the (new) Input System in Unity 6.4 and above.

Examples

This contains several examples of the different Renderer Properties.

Samples - Examples.

More Renderer Properties

This contains:

  • a custom "Enum Color" Renderer Property, storing an index to a Color Array.
  • a custom "Power Scalar" Renderer Property, applying a power on the packed data to bias precision toward low range values.

Samples - EnumColor.

Visual Scripting

This contains an example of setting a Renderer Property on a Property Packer from Visual Scripting.

Samples - VisualScripting.

Supported Renderer Types

RSUV being implemented only on some Renderer classes, such as MeshRenderer and SkinnedMeshRenderer, this package contains an Extension that makes it easy to set the ShaderUserValue on a Renderer. 2D Renderers (Sprite, SpriteShape and Tilemap) are supported in Unity 6.5 and above, as well as in 6.3.13, but not yet in 6.4.

Writing Renderer Property Types

The package is easily extensible to add new Renderer Property types by simply providing their data encoding (C#) and decoding (HLSL). One can be created from template using Assets/Create/Scripting/RendererProperty.

using UnityEngine.RSUVBitPacker;
// RendererValue Attributes are used by the PropertyList.
[System.Serializable]
[RendererValueTypeName("NewRendererProperty")] //The name of the 'Add' Dropdown MenuItem.
//[RendererValueTypeLength(1)] // If used, the MenuItem will be grayed out if the length is greater than remaining bits available.
[RendererValueTypeTooltip("A single bit storing a boolean value.")] // 
public class NewRendererProperty : RendererProperty<bool> // the type defines the serialized value type
{
    // This is used to offset bitshift index.
    public override uint Length => 1;
    
    // This is expected to provide the data, as a uint, of which only the first n bits are used.
    public override uint Data => Value ? 1u : 0u;

    // The next two overrides are not mandatory. They are used by the HLSL generator.
    // If left unoverriden, the property value will not be featured in the generated Shader Include.
    // This is used to write the 'out' parameter.
    public override string HlslType => "bool";

    // This is used to write the parameter assignment in the shader function body.
    // rsuv is short for unity_RendererUserValue.
    // paramName is the name of the property as set by the user.
    // bitIndex is where the data storage begins in the rsuv uint.
    public override string HlslDecoder(string paramName, uint bitIndex) => $"{paramName} = (rsuv & (1 << {bitIndex})) != 0;";
}

Known limitations and potential future improvements

Property Names

There is no check on property names other than whitespace removal and default naming if left empty. Don't give several properties the same name, and don't name properties with HLSL types or intrisic functions like "float" or "dot".

Shader Graph Preview

Previews in Shader Graph use the default values set in the Property Sheet. Future improvement may add a Preview Value to the Nodes' settings.

IPropertyProvider

Considered for future improvement to add an interface for MonoBehaviours to provide renderer properties.

About

Framework facilitating the use of Renderer Shader User Value (RSUV) in Unity.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors