Skip to content

VRSL DMX: Creating Custom DMX Shaders

AcChosen edited this page Jan 27, 2023 · 23 revisions

CG Includes & Properties

VRSLDMX.cginc

VRSL DMX is intended to be used more like a data protocol rather than a discrete set of shaders and tools. As such, any shader property can be controlled by DMX, whether it's lighting, vertex transforms, screen space, etc. If it's a shader property, it can be controlled by VRSL. This page discusses what you need to add to your custom shaders to enable VRSL DMX functionality.

Here is the video tutorial version of this page.

VRSL DMX works similarly to audiolink in that there is a single CGINC file that you need to include to start creating DMX-compatible world shaders. The file is called VRSLDMX.cginc and you can use it with this strip of code:

#include "Packages/com.acchosen.vr-stage-lighting/Runtime/Shaders/VRSLDMX.cginc"

This file contains a few instanced defined properties, standard defined properties, and some functions needed to read DMX from the grid node. Once you have this file in, there are some properties you may want to define first before you get started writing.

The Main Properties

These two are the two properties most important:

  • _DMXChannel ("Starting DMX Channel", Int) = 1

    • The _DMXChannel instanced property is where you'll set the starting DMX Channel for your DMX shader. DMX Channels start reading from 1, so 1 is the default.
  • [NoScaleOffset] _DMXGridRenderTexture("DMX Grid Render Texture (To Control Lights)", 2D) = "white" {}

    • The _DMXGridRenderTexture is where the DMX Grid render texture will actually go. You can find the render textures in: Packages/com.acchosen.vr-stage-lighting/Runtime/Textures/RTs. There you can find the horizontal, vertical, and legacy versions of the textures. I recommend using the DMXRTViewer-Interpolated-Color+Intensity prefixed render textures as those are the slighting smoothed versions of the RAW render texture counterparts.

These two properties are required for all world-based DMX shaders (for now).

The Extra Properties

Here are some extra properties that are not required but are still highly recommended to have in your shader to enable standard features:

  • [Toggle] _EnableDMX ("Enable Stream DMX/DMX Control", Int) = 0

    • A simple instanced int toggle property that can be used to enable/disable DMX functionality at runtime. This toggle can be controlled by the VRSL DMX Udon Script.
  • [NoScaleOffset] _DMXGridStrobeTimer("DMX Grid Strobe Texture (To Control Strobing)", 2D) = "white" {}

    • The render texture property that enables the strobing functionality of VRSL DMX. If you want to enable the strobing function, this texture is required. You can find them in the same render texture folder as the standard DMX render textures and they have the prefix DMXRTViewer-StrobeTimings-.
  • [Toggle] _NineUniverseMode ("Extended Universe Mode", Int) = 0

    • An instanced toggle for nine/extended universe mode. This toggle is required for your shader to be used in extended universe mode. This toggle can be controlled by the VRSL Control Panel.
  • [Toggle] _EnableVerticalMode ("Enable Vertical Mode", Int) = 0

    • An instanced toggle for vertical mode. This toggle is required for your shader to be used in vertical mode. This toggle can be controlled by the VRSL Control Panel.
  • [Toggle] _EnableCompatibilityMode ("Enable Compatibility Mode", Int) = 0

    • An instanced toggle for legacy mode. This toggle is required for your shader to be used in legacy mode. This toggle can be controlled by the VRSL Control Panel.
  • [Toggle] _EnableStrobe ("Enable Strobe", Int) = 0

    • An instanced toggle for strobe functionality. Use this property if you want to have an instanced toggle for the strobing feature. This toggle can be controlled by the VRSL DMX Udon Script.
  • [HDR] _Emission ("Base DMX Emission Color", Color) = (1,1,1,1)

    • An instanced base emission color. Multiple your DMX color output by this to change the shader's base starting color. This color can be controlled by the VRSL DMX Udon Script.
  • _GlobalIntensity ("Global Intensity", Range(0,1)) = 1

    • An instanced 0-1 float value intended to control the base intensity of your DMX color output. This value can be controlled by the VRSL DMX Udon Script.
  • _FinalIntensity ("Final Intensity", Range(0,1)) = 1

    • An instanced 0-1 float value intended to control the base intensity of your DMX color output. Functionally, this is no different than Global Intensity, however, it is included in case you needed the base intensity to be controlled by more than one external script or animation. This value can be controlled by the VRSL DMX Udon Script.

The DMX Functions

There are a few functions inside of the VRSLDMX.cginc script that you will need to use to enable DMX compatibility.

The general use case, however, will be to use the ReadDMX() function with GetDMXChannel() and _DMXGridRenderTexture as your input. This will read DMX data from the _DMXGridRenderTexture at the specified DMX channel. The general workflow is to increment the DMX channel value to traverse the grid and sample from nearby channels to create a "fixture profile" for your shader that you can then mimic inside of the DMX software of your choosing.

The Main Functions

  • ReadDMX(uint DMXChannel, sampler2D _Tex)

    • Returns a 0-1 value from a specified DMX channel from the provided texture. Use this with GetDMXChannel() as input. You can increment GetDMXChannel() to get nearby channels to sample from for different properties. It is recommended to use the standard _DMXGridRenderTexture as the texture input.
  • GetDMXChannel()

    • Returns the instanced DMX channel property of your shader as a uint.

The Standard Functions

  • GetStrobeOutput(uint DMXChannel)

    • Returns an alternating 0-1 value with the rate controlled by the specified DMX Channel value. Multiply the output of this function by any other value to make it strobe from its current value to 0 and back.
  • GetDMXColor(uint DMXChannel)

    • This function callsReadDMX() 3 times, at 3 consecutive DMX increments (DMXChannel, DMXChannel + 1, DMXChannel + 2) to get 3 DMX channels for red, green, and blue. It then combines those values into a color (float4) that it returns.

The Property Functions

  • isDMX()

    • Returns the instanced toggle property, _EnableDMX.
  • getBaseEmission()

    • Returns the instanced color property, _Emission.
  • getGlobalIntensity()

    • Returns the instanced float property, _GlobalIntensity .
  • getFinalIntensity()

    • Returns the instanced float property, _FinalIntensity.
  • isStrobe()

    • Returns the instanced toggle property, _EnableStrobe.
  • getNineUniverseMode()

    • Returns the instanced toggle property, _NineUniverseMode.

Example Code

It is recommended, if possible, to get the DMX data in the vertex shader rather than the fragment shader. This is because since there is no UV mapping involved when sampling, it is generally faster to sample the data in the vertex shader and send it to the fragment shader through the v2f struct.

Here is an example:

`

        struct appdata
        {
            float4 vertex : POSITION;
            float2 uv : TEXCOORD0;
        };
        struct v2f
        {
            float2 uv : TEXCOORD0;
            float4 vertex : SV_POSITION;
            float4 finalColor : TEXCOORD1;

        };

        #include "Packages/com.acchosen.vr-stage-lighting/Runtime/Shaders/VRSLDMX.cginc"

        sampler2D _MainTex;
        float4 _MainTex_ST;

        v2f vert (appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            o.uv = TRANSFORM_TEX(v.uv, _MainTex);
            float4 col =  getBaseEmission() * getGlobalIntensity() * getFinalIntensity();
            if(isDMX())
            {
                int dmxChannel = GetDMXChannel();

                float intensity = ReadDMX(dmxChannel, _DMXGridRenderTexture); // channel 1: Intensity

                float red = ReadDMX(dmxChannel + 1, _DMXGridRenderTexture); // channel 2: Red
                float green = ReadDMX(dmxChannel + 2, _DMXGridRenderTexture); // channel 3: Green
                float blue = ReadDMX(dmxChannel + 3, _DMXGridRenderTexture); // channel 4: Blue

                float strobe = GetStrobeOutput(dmxChannel + 4); // channel 5: Strobe

                o.dmxIntensityStrobe.b = strobe;

                float4 rgb = float4(red, green, blue, 1);
                rgb *= intensity * strobe;
                col = rgb; // final DMX color output.
            }
            o.finalColor = col;
            return o;
        }`

In this example, we created a custom parameter in the v2f struct with float4 finalColor : TEXCOORD1;, which simply stores the final emission color of our DMX shader. We then use isDMX() to determine if we are using DMX at runtime, and adjust the final output of finalColor with dmx data accordingly.

This way, our fragment shader only needs to grab i.finalColor to get the final output of our DMX color.

You do not need to only send color data, you can also send the dmx data values themselves if needed through the same method.

Adding it to the VRSL DMX Script

To add your custom DMX shader/material to the VRSL DMX Script, VRStageLighting_DMX_Static, you will need to add the script to a game object, add your material to your target mesh object, and then add that object's mesh renderer to the obj renderers array at the bottom of the script. This will enable basic instancing functionality as well as allow your custom shader to appear as a fixture in the VRSL Control Panel.