ShaderFX Custom Code advanced example

This example demonstrates how to obtain existing shader code and customize it for a GLSL/CGFX environment in Maya, so that you can use it with ShaderFX in OpenGL mode. It then demonstrates how to customize the code for an HLSL environment, so that you can use it in DirectX 11 mode in Maya also.

The shader code used in this example is courtesy of Iñigo Quilez from https://www.shadertoy.com/view/ldB3zc. You can visit this site to obtain the following sample code and modify it for use in Maya.

A video demonstration of the following workflow is available. See Video tutorials below for more information.

float hash1( float n ) { return fract(sin(n)*43758.5453); }
vec2  hash2( vec2  p ) { p = vec2( dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3)) ); return fract(sin(p)*43758.5453); }

vec4 voronoi( in vec2 x, float w )
{
    vec2 n = floor( x );
    vec2 f = fract( x );

    vec4 m = vec4( 8.0, 0.0, 0.0, 0.0 );
    for( int j=-2; j<=2; j++ )
    for( int i=-2; i<=2; i++ )
    {
        vec2 g = vec2( float(i),float(j) );
        vec2 o = hash2( n + g );
        // animate
        o = 0.5 + 0.5*sin( iGlobalTime + 6.2831*o );

        // distance to cell
        float d = length(g - f + o);

        // do the smooth min for colors and distances
 
        vec3 col = 0.5 + 0.5*sin( hash1(dot(n+g,vec2(7.0,113.0)))*2.5 + 3.5 + vec3(2.0,3.0,0.0));
        float h = smoothstep( 0.0, 1.0, 0.5 + 0.5*(m.x-d)/w );
        m.x   = mix( m.x,     d, h ) - h*(1.0-h)*w/(1.0+3.0*w); // distance
        m.yzw = mix( m.yzw, col, h ) - h*(1.0-h)*w/(1.0+3.0*w); // cioloe
    }
    return m;
}

void main( void )
{
    vec2 p = gl_FragCoord.xy/iResolution.yy;
    float k = 2.0 + 70.0 * pow( 0.5 + 0.5*sin(0.25*6.2831*iGlobalTime), 4.0 );
    k = 0.5 - 0.5*cos(0.25*6.2831*iGlobalTime);
    vec4 c = voronoi( 6.0*p, k );

    vec3 col = c.yzw;
    col *= 1.0 - 0.8*c.x*step(p.y,0.33);
    col *= mix(c.x,1.0,step(p.y,0.66));
    col *= smoothstep( 0.005, 0.007, abs(p.y-0.33) );
    col *= smoothstep( 0.005, 0.007, abs(p.y-0.66) );
    gl_FragColor = vec4( col, 1.0 );
}

Working in GLSL/CGFX mode

To modify the code so that it is compatible with SFX_GLSL_*/SFX_CGFX_3 so that you can use it with ShaderFX in OpenGL mode in Maya, do as follows:

  1. Change vec2, vec3, vec4 to float2, float3, float4, respectively.
  2. Change gl_FragCoord.xy to UV.

    gl_FragCoord.xy is a global variable used in GLSL to obtain texture co-ordinates. In Maya, you use a UV set to obtain the texture co-ordinates.

  3. Remove iResolution, which defines the viewport resolution. It is not necessary in Maya.
  4. Use a function definition instead of defining a main.

    Change main (void) to a function definition, similar to the one provided by default with the Custom Code node:

    CustomCode1178Output CustomCode1178Func( float2 UV )
  5. Add a struct to provide the output for the node.
    struct CustomCode1178Output 
    { 
        float3 color; 
    };
  6. Instead of iGlobalTime, use the Time node in Maya as input.

    Replace iGlobalTime with time.

    Add float time to the function definition as an input parameter.

    Add float time to the voronoi function definition as an input parameter.

    Add time as an input parameter where voronoi is called.

  7. gl_FragColor in the original code defines the final output of the shader. Change it to OUT.color and return the output as follows:
    CustomCode1178Output OUT; 
    OUT.color = col;
    return OUT; 
  8. (Optional) To avoid conflict with other nodes, you can also customize the function name by doing as follows:
    1. Name this function VoronoiSmooth instead of the generic name CustomCodexxxx.
      Note: All functions should have unique names. Be careful not to have two Custom Code nodes that have the same function name.
    2. Add VoronoiSmooth_ as a prefix to your other functions, such as hash1 and hash2, and rename the function voronoi to VoronoiSmooth.
    3. Change the Function Name in the Attribute Editor to VoronoiSmooth.
  9. Note: By default, the Diffuse Color port of TraditionalGameSurfaceShader accepts a float3 input. If you choose to output a float4 from your Custom Code node instead, for example, you can convert the output from float4 to float3 by using a (Hw Shader Nodes > Values > Vector Component) node.

The modified code is as follows:

float VoronoiSmooth_hash1( float n ) { return fract(sin(n)*43758.5453); }
float2 VoronoiSmooth_hash2( float2 p ) { p = float2( dot(p,float2(127.1,311.7)), dot(p,float2(269.5,183.3)) ); return fract(sin(p)*43758.5453); }

float4 VoronoiSmooth( in float2 x, float w, float time )
{
	   float2 n = floor( x );
	   float2 f = fract( x );

	   float4 m = float4( 8.0, 0.0, 0.0, 0.0 );
	   for( int j=-2; j<=2; j++ )
	   for( int i=-2; i<=2; i++ )
	   {
		     float2 g = float2( float(i),float(j) );
		     float2 o = VoronoiSmooth_hash2( n + g );
		     // animate
		     o = 0.5 + 0.5*sin( time + 6.2831*o );

		    // distance to cell 
		    float d = length(g - f + o);

		    // do the smooth min for colors and distances 
		    float3 col = 0.5 + 0.5*sin( VoronoiSmooth_hash1(dot(n+g,float2(7.0,113.0)))*2.5 + 3.5 + float3(2.0,3.0,0.0));
		    float h = smoothstep( 0.0, 1.0, 0.5 + 0.5*(m.x-d)/w );
		    m.x = mix( m.x, d, h ) - h*(1.0-h)*w/(1.0+3.0*w); // distanec
		    m.yzw = mix( m.yzw, col, h ) - h*(1.0-h)*w/(1.0+3.0*w); // cioloe
	   }
	   return m;
 }

struct VoronoiSmoothOutput 
{ 
	   float3 color; 
}; 

VoronoiSmoothOutput VoronoiSmoothFunc( float2 UV, float time ) 
{ 
	   float2 p = UV;
	   float k = 2.0 + 70.0 * pow( 0.5 + 0.5*sin(0.25*6.2831*time), 4.0 );
	   k = 0.5 - 0.5*cos(0.25*6.2831*time);
	   float4 c = VoronoiSmooth( 6.0*p, k, time );

	   float3 col = c.yzw;
	   col *= 1.0 - 0.8*c.x*step(p.y,0.33);
	   col *= mix(c.x,1.0,step(p.y,0.66));
	   col *= smoothstep( 0.005, 0.007, abs(p.y-0.33) );
	   col *= smoothstep( 0.005, 0.007, abs(p.y-0.66) );

	   VoronoiSmoothOutput OUT; 
	   OUT.color = col;
	   return OUT; 
} 

You can now use this code for your custom code node. Connect it as follows, and witness its animation in both the ShaderFX editor and in Viewport 2.0 OpenGL mode.

Working in DirectX 11 mode

If you create the same ShaderFX network in DirectX 11 mode, and then try to perform swatch rendering, you will not be able to compile the shader swatch. To help debug, select Settings > Show Swatch Compile Errors and inspect the errors that appear.

To modify the code so that it is compatible with SFX_HLSL_3/SFX_HLSL_5 so that you can use it with ShaderFX in DirectX 11 mode in Maya, do as follows:

  1. Add the following if/endif statements at the beginning of your code:
    #if ( defined(SFX_HLSL_3) || defined( SFX_HLSL_5) )
    #define fract frac
    #define mix lerp
    #endif

The modified code is as follows:

#if ( defined(SFX_HLSL_3) || defined( SFX_HLSL_5) )
#define fract frac
#define mix lerp
#endif

float VoronoiSmooth_hash1( float n ) { return fract(sin(n)*43758.5453); }
float2 VoronoiSmooth_hash2( float2 p ) { p = float2( dot(p,float2(127.1,311.7)), dot(p,float2(269.5,183.3)) ); return fract(sin(p)*43758.5453); }

float4 VoronoiSmooth( in float2 x, float w, float time )
{
	   float2 n = floor( x );
	   float2 f = fract( x );

	   float4 m = float4( 8.0, 0.0, 0.0, 0.0 );
	   for( int j=-2; j<=2; j++ )
	   for( int i=-2; i<=2; i++ )
	   {
		     float2 g = float2( float(i),float(j) );
		     float2 o = VoronoiSmooth_hash2( n + g );
		     // animate
		     o = 0.5 + 0.5*sin( time + 6.2831*o );

		    // distance to cell 
		    float d = length(g - f + o);

		    // do the smooth min for colors and distances 
		    float3 col = 0.5 + 0.5*sin( VoronoiSmooth_hash1(dot(n+g,float2(7.0,113.0)))*2.5 + 3.5 + float3(2.0,3.0,0.0));
		    float h = smoothstep( 0.0, 1.0, 0.5 + 0.5*(m.x-d)/w );
		    m.x = mix( m.x, d, h ) - h*(1.0-h)*w/(1.0+3.0*w); // distanec
		    m.yzw = mix( m.yzw, col, h ) - h*(1.0-h)*w/(1.0+3.0*w); // cioloe
	   }
	   return m;
 }

struct VoronoiSmoothOutput 
{ 
	   float3 color; 
}; 

VoronoiSmoothOutput VoronoiSmoothFunc( float2 UV, float time ) 
{ 
	   float2 p = UV;
	   float k = 2.0 + 70.0 * pow( 0.5 + 0.5*sin(0.25*6.2831*time), 4.0 );
	   k = 0.5 - 0.5*cos(0.25*6.2831*time);
	   float4 c = VoronoiSmooth( 6.0*p, k, time );

	   float3 col = c.yzw;
	   col *= 1.0 - 0.8*c.x*step(p.y,0.33);
	   col *= mix(c.x,1.0,step(p.y,0.66));
	   col *= smoothstep( 0.005, 0.007, abs(p.y-0.33) );
	   col *= smoothstep( 0.005, 0.007, abs(p.y-0.66) );

	   VoronoiSmoothOutput OUT; 
	   OUT.color = col;
	   return OUT; 
} 

Creating a ShaderFX graph with the Custom Code node

After you have created a Custom Code node using the code above, you can create a network such as the one below.

For more details about creating groups and using the Default Value node, see Provide alternative inputs to the group node and label the group node ports.

In this example, we provide a default UV set and a default time node, while allowing the user to provide an alternate UV set.

Video tutorials

Watch the following videos for a visual demonstration of the above example workflow: