Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Image Modified

Sample Render using the custom Voronoi 3D Texture Node.

The following package contains all the material described in this tutorial: Custom_Hypershade_Nodes.zip


Section


Column

This tutorial explains how to create your own HyperShade node. Shader development in 3Delight for Maya is very easy:

  1. You don't need to install a compiler or any compilation tool.
  2. You don't need to perform any compilation steps while developing your shader or texture.
  3. Every created shader or texture is cross platform compatible.

quite simple. With OSL shaders, the process gets simpler yet, as 3Delight for Maya can automatically register as Maya shading nodes user-provided OSL shaders. 3Delight supports all the required functions to properly run OSL shaders including all the most advanced closures, refer to Performance Analysis for more informations.

This tutorial explains how to create your own HyperShade node. As an example, we show how to develop a simple voronoi noise pattern as a Maya 2D Texture. For more informations about procedural textures we recommend this modern classic: Texture & Modeling: a procedural apprachAs examples, we explain how to develop a simple voronoi noise as a Maya 3D Texture and a simple Lambert shader.


Column
width35%


Panel
borderColor#e0e0e0
bgColor#f0f0f0
titleColor#444444
titleContent:

Table of Contents

Main Components

Three components are needed to create a single shader:

  1. The shader or texture source code, in RenderMan Shader Language.

  2. Maya Python file to act as a plug-in that registers.
  3. Maya template file for the UI.

Optionally, it is possible to add icons to have a better visual representation of the node inside Hyerpshade and the Outliner.

The following table shows where each component should be installed for a Maya 2015 package.

ComponentInstallation Environment VariableDefault LocationRSL source code_3DFM_SL_INCLUDE_PATH

maxLevel2



Using the example package

Download and decompress Custom_Hypershade_Nodes.zip. To use the package files, you can either copy them to specific locations of your 3Delight installation, or define environment variables to indicate their locations.

Copying example files to your 3Delight installation

  • Copy the customShadingNodes directory itself into the maya folder of your 3Delight installation - for instance, place the directory copy into C:\Program Files\3Delight\maya\
rslMaya Python Plug-inMAYA_PLUG_IN_PATHC:\Program Files\3Delight\maya\2015\plug-insMaya Attribute Editor
Template FileMAYA_SCRIPT_PATHIconsXBMLANGPATH
  • on a default Windows installation of 3Delight Studio Pro, so to have it in C:\Program Files\3Delight\maya\
2015\scripts
  • customShadingNodes
  • Optional - copy the files from the icons directory into the icons folder of your 3Delight installation. Make sure to choose the subdirectory that corresponds to the Maya version you are using. For instance, on a default Windows installation of 3Delight Studio Pro, for Maya 2015, the correct destination location is 
  • C:\Program Files\3Delight\maya\2015\icons
  • .

Or, defining environment variables

The XBMLANGPATH definition is optional. On Linux, paths set in XBMLANGPATH must end with /%B .

Environment VariableValue
_3DFM_USER_OSL_PATHPath to the customShadingNodes directory of the downloaded package.
XBMLANGPATHPath to the icons directory of the downloaded package.

 

Lauch Maya and go to the Hypershade editor. The OSLVoronoi node should appear listed under 3Delight → Texture in the Create tab, and under 3Delight OSL Texture in the Create menu. Note that the Maya script editor will report the creation of the new OSL nodes.

Image Added

The Voronoi node viewed in the Create menu of the HyperShade

Image AddedThe Voronoi node viewed in the Node Editor. The place2dTexture node is automatically created and connected.

 

Image AddedVoronoi noise connected to the 3Delight Material.


Components of a custom shading node

There is only one required component to define a custom shading node: a compiled OSL shaderOptionally, it is possible to add icons to have a better visual representation of the node inside the Hypershade and the Outliner.

Components location

The following table shows the default location where 3Delight for Maya will look for components, and the environment variable that allows you to specify another location for them.

ComponentEnvironment VariableDefault Location
Compiled OSL shaders_3DFM_USER_OSL_PATHC:\Program Files\3Delight\maya\customShadingNodes
IconsXBMLANGPATHC:\Program Files\3Delight\maya\2015\icons (varies according to the Maya version being used)

The compiled shader

This is a standard Open Shading Language surface shader, complied with oslc (provided with the 3Delight Studio Pro package). The shader source code can be annotated to provide indications to 3Delight for Maya about the Maya shading node classification and its appearance in the Attribute Editor.

Warning

While annotations are optional, it is highly recommended to provide the shader annotation that specifies the type ID.

Expand
titleMore info...

When no type ID is provided as annotation, 3Delight for Maya will attempt to generate one in the range reserved for studio internal node types Because this range is not large enough, it is possible that two different OSL shader names will result in identical type IDs; this will cause problems when reading a scene in Maya Binary format. For this reason, using the automatically generated IDs is not recommended for usage outside of prototyping purposes, refer to mayaid.autodesk.io to obtain a reserved block of IDs.

Expand the annotations section below and refer to the maya_typeID shader annotation for more details on how to define your type ID.



Expand
titleMore details about the OSL annotations...

Annotations in the OSL source code

The annotations are defined between double square brackets. Multiple, comma-separated annotations,  can be used in one set of double square brackets.

Shader annotation

This annotation is provided between the shader name and its parameter list. For instance:

surface OSLVoronoi [[ string maya_type = "texture", string maya_typeID = "0x00" ]] ( ... )

The supported shader annotations are:

string maya_typeID

Specifies the type ID used for the node registration. This is a integer that Maya uses to identify the node type, most notably when saving a scene in the Maya Binary format. Each OSL shader that defines a given shading node type should be assigned a unique type ID. You can chose a value between 0x0000 and 0x007F, or between 0x7F01 and 0x7FFF for your shading node type.

The IDs from 0x0000 to 0x7FFF are reserved for node types that are used internally in a studio. 3Delight for Maya will generate a type ID between 0x0080 and 0x7F00 if no maya_typeID annotation is provided. You can also request your own reserved node ID range to Autodesk, for free. This is also the recommended solution if you intend to share your nodes with users outside your studio.

You may use any ID between 0x0000 and 0x7FFF if you always provide the maya_typeID annotation for every custom OSL shader you define. In this case 3Delight for Maya will never need to generate a type ID.

string maya_type

Specifies the Maya shading node classification. The classification affects where the node is presented in the Hypershade tree lister and menus. Some classification types will also change the node creation mechanism to automatically create and connect related nodes - for instance, creating a surface shader will also create and connect a shading group.

The currently supported types are:

texture

The shading node will be classified as a texture node. The node will be classified as a 2D texture node, for which Maya automatically create and connect a place2DTexture node, if:

    • it contains a float[2] parameter that has the string maya_name = "uv" annotation, and
    • it contains a float[2] parameter that has the string maya_name = "uvFilter" annotation.

lens

The shading node will be classified as a lens shader. Lens shaders can be connected to a camera's lens shader 3Delight extension attribute.

surface

The shading node will be classified as a surface shader. It will be connected to a shading group upon creation.

Parameter Annotations

Parameter annotations are provided between a parameter's default value and the comma that ends its declaration. For instance:

float i_jitter = 1.0 [[ string maya_name = "jitter" ]],

The supported parameter annotations are:

string maya_type

Specifies the type of the Maya attribute related to the shader parameter. For now, only bool is supported to display an integer parameter as a checkbox.

string maya_name

Specifies the name of the Maya attribute related to the shader parameter.

string maya_label

Specifies the label to use for the Maya attribute in the various Maya editors (Attribute Editor, Node Editor, Channel Box, etc.).

string maya_group

Specifies the label of a Frame Layout into which the Maya attribute will be displayed in the Attribute Editor.

float maya_min

Specifies the soft minimum value for the Maya attribute attribute related to the shader parameter.

float maya_max

Specifies the soft maximum value for the Maya attribute attribute related to the shader parameter.

int maya_hidden

When set to 1, the Maya attribute will not be shown in the Attribute Editor. It will still be visible in the other Maya editors (Node EditorChannel Box, etc.).

Annotated OSL shader example
Expand
titleShow OSL code..

Creating a Custom 3D Texture Node

After correctly providing the three components, you will be able to render using the new texture as with any other Maya 3D Texture.

Image RemovedThe Voronoi node viewed in the Node Editor. Note how  place3dTexture node is automatically supported.

 

Image RemovedVoronoi noise connected to the 3Delight Material.

The RenderMan Source Code 

The Voronoi texture looks like a standard RenderMan Shading Language function but with some added structure:

  • A comment block at the beginning to tell 3Delight for Maya what are the inputs and outputs.
  • A naming convention for the function name.  

The source code for our voronoi texture follows. It should be included inside a directory defined by the environment variable _3DFM_SL_INCLUDE_PATH, as explained above, and named 3DelightVoronoi.h.

Code Block
languagecpp
themeEclipse
title3Delight RSL Header (3DelightVoronoi.h)
collapsetrue
#ifndef __3DelightVoronoi_h
#define __3DelightVoronoi_h
 
/*
begin inputs
	float jitter
	uniform matrix placementMatrix
end inputs
 
begin outputs
	color outColor
end outputs
 
begin shader_extra_parameters Pref_param
	varying point Pref = 0;
end shader_extra_parameters
*/
 
#include "texture3d.h"
#include "utils.h"
#include "noise_utils.h"
 
void maya_3DelightVoronoi(
		float jitter;
		matrix placementMatrix;
		output color outColor;)
{
float edgeDist;
float outside;
varying point pp = transformP(1, 0, placementMatrix, 1, edgeDist, outside);
 
point thiscell = point (floor(xcomp(pp))+0.5, floor(ycomp(pp))+0.5,
			floor(zcomp(pp))+0.5);
			
float f1 = 1000;
uniform float i, j, k;
for (i = -1;  i <= 1;  i += 1) 
	{
	for (j = -1;  j <= 1;  j += 1)
		{
		for (k = -1;  k <= 1;  k += 1)
			{
			point testcell = thiscell + vector(i,j,k);
			point pos = testcell + jitter * (vector cellnoise (testcell) - 0.5);
			vector offset = pos - pp;
			float dist = offset . offset; /* actually dist^2 */
			if (dist < f1)
				{
				f1 = dist;
				}
			}
		}
	}
	
f1 = sqrt(f1);
outColor = color (f1);
}
 
#endif /* __3DelightVoronoi_h */

The Python Plug-in

The python module is the plug-in source code per say. You must load this plug-in in Maya's Plugin Manager

Info

You need a node ID for your shader plug-in. If your plug-in is for in-house use only, simply choose one from the available internal node IDs (range from 0 to 0x7ffff). If your nodes are to be made public then you would need to reserve a node ID with Autodesk. All this is explained in the following page:

http://discussion.autodesk.com/cgi-bin/maya/MayaID.cgi

Code Block
languagepy
themeEclipse
titleMaya Python Plug-in (3DelightVoronoi.py)
collapsetrue
# imports
import maya.OpenMaya as OpenMaya
import maya.OpenMayaUI as OpenMayaUI
import maya.OpenMayaMPx as OpenMayaMPx
import math, sys
 
dlNodeClassify = "rendernode/delight/texture:texture/3d"
dlNodeId = OpenMaya.MTypeId(0x1)
 
# Node definition
class DL_Voronoi(OpenMayaMPx.MPxNode):
	# class variables
	
	jitter = OpenMaya.MObject()
	placementMatrix = OpenMaya.MObject()
	outColor = OpenMaya.MObject()
 
	def __init__(self):
		OpenMayaMPx.MPxNode.__init__(self)
 
	def compute(self, plug, dataBlock):
	
		return OpenMaya.kUnknownParameter
 
################################################################################
 
def nodeCreator():
	return OpenMayaMPx.asMPxPtr(DL_Voronoi())
 
def nodeInitializer():
	num_attr = OpenMaya.MFnNumericAttribute()
	matrix_attr = OpenMaya.MFnMatrixAttribute()
	
	# input jitter
	DL_Voronoi.jitter = num_attr.create( "jitter", "ji", OpenMaya.MFnNumericData.kFloat, 1 )
	num_attr.setKeyable(1)
	num_attr.setConnectable(0)
 
	# input placementMatrix
	DL_Voronoi.placementMatrix = matrix_attr.create ( "placementMatrix", "pm" )
	matrix_attr.setKeyable(1)
	matrix_attr.setReadable(0)
 
	# output color
	DL_Voronoi.outColor = num_attr.createColor( "outColor", "oc" )
	num_attr.setStorable(0)
	num_attr.setWritable(0)
 
	# add attributes
	DL_Voronoi.addAttribute( DL_Voronoi.jitter )
	DL_Voronoi.addAttribute( DL_Voronoi.placementMatrix )
	DL_Voronoi.addAttribute( DL_Voronoi.outColor )
 
	# attributes affects
	DL_Voronoi.attributeAffects( DL_Voronoi.jitter, DL_Voronoi.outColor )
	DL_Voronoi.attributeAffects( DL_Voronoi.placementMatrix, DL_Voronoi.outColor )
 
################################################################################	
 
# initialize the script plug-in
def initializePlugin(mobject):
	mplugin = OpenMayaMPx.MFnPlugin(mobject)
 
	# register node
	try:
		mplugin.registerNode("3DelightVoronoi", dlNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kDependNode, dlNodeClassify)
	except:
		sys.stderr.write("Failed to register node 3DelightVoronoi" )
		raise
 
# uninitialize the script plug-in
def uninitializePlugin(mobject):
	mplugin = OpenMayaMPx.MFnPlugin(mobject)
 
	# deregister node
	try:
		mplugin.deregisterNode(dlNodeId)
	except:
		sys.stderr.write( "Failed to unregister node 3DelightVoronoi" )
		raise 

The MEL Attribute Editor Template

This template file creates the UI for the different parameters of your texture. In this case, we only have one such parameter. 

Code Block
languagecpp
themeEclipse
titleMaya AE Template (AE3DelightVoronoiTemplate.mel)
collapsetrue
global proc
AE3DelightVoronoiTemplate(string $node)
{
editorTemplate -beginScrollLayout;
	editorTemplate -label "Jitter" -addControl "jitter";

	editorTemplate -suppress "placementMatrix";	
	
	editorTemplate -suppress caching;
	editorTemplate -suppress nodeState;
editorTemplate -endScrollLayout;
}

Creating a Custom Shader Node

Writing a shader node is as easy as developing a texture node.  We provide the tree main components below, they follow the same logic and structure as for texture node above
.


Code Block
languagecpp
themeEclipse
title
3Delight RSL Header (3DelightExampleShader.h)
collapsetrue
/*
begin inputs
	color color
end inputs
begin outputs
	color outColor
	color outTransparency
end outputs
*/
 
#ifndef __3DelightExampleShader_h
#define __3DelightExampleShader_h
 
void maya_3DelightExampleShader(
		color i_color;
		output color o_outColor;
		output color o_outTransparency;)
{
	extern normal N;
	o_outColor = i_color * diffuse( normalize(N) );
	o_outTransparency = 0.0;
}
 
#endif /* __3DelightExampleShader_h */
Code Block
languagepy
themeEclipse
titleMaya Python Plug-in (3DelightExampleShader.py)
collapsetrue
# imports
import maya.cmds as cmds
import maya.OpenMaya as OpenMaya
import maya.OpenMayaUI as OpenMayaUI
import maya.OpenMayaMPx as OpenMayaMPx
import math, sys
 
dlNodeClassify = "swatch/delightShaderSwatchRender:shader/surface"
dlNodeClassify_2015 = "shader/surface:swatch/delightShaderSwatchRender"
dlNodeId = OpenMaya.MTypeId(0x0)
 
# Node definition
class DL_exampleShader(OpenMayaMPx.MPxNode):
	# class variables
	color = OpenMaya.MObject()
	outColor = OpenMaya.MObject()
	outTransparency = OpenMaya.MObject()
 
	def __init__(self):
		OpenMayaMPx.MPxNode.__init__(self)
 
	def compute(self, plug, dataBlock):
		if plug != DL_exampleShader.outColor and plug.parent() != DL_exampleShader.outColor:
			return OpenMaya.kUnknownParameter
 
		# Just transfer color to outColor
		resultColor = dataBlock.inputValue( DL_exampleShader.color ).asFloatVector()
 
		# set ouput color attribute
		outColorHandle = dataBlock.outputValue( DL_exampleShader.outColor )
		outColorHandle.setMFloatVector(resultColor)
		outColorHandle.setClean()
 
################################################################################
 
def nodeCreator():
	return OpenMayaMPx.asMPxPtr(DL_exampleShader())
 
def nodeInitializer():
	nAttr = OpenMaya.MFnNumericAttribute()
 
	# input color
	DL_exampleShader.color = nAttr.createColor( "color", "c" )
	nAttr.setKeyable(1)
	nAttr.setStorable(1)
	nAttr.setReadable(1)
	nAttr.setWritable(1)
	nAttr.setDefault(0.5, 0.5, 0.5)
 
	# output color
	DL_exampleShader.outColor = nAttr.createColor( "outColor", "oc" )
	nAttr.setKeyable(0)
	nAttr.setStorable(0)
	nAttr.setReadable(1)
	nAttr.setWritable(0)
 
	# output transparency
	DL_exampleShader.outTransparency = nAttr.createColor( "outTransparency", "ot" )
	nAttr.setKeyable(0)
	nAttr.setStorable(0)
	nAttr.setReadable(1)
	nAttr.setWritable(0)
 
	# add attributes
	DL_exampleShader.addAttribute( DL_exampleShader.color )
	DL_exampleShader.addAttribute( DL_exampleShader.outColor )
	DL_exampleShader.addAttribute( DL_exampleShader.outTransparency )
	DL_exampleShader.attributeAffects( DL_exampleShader.color, DL_exampleShader.outColor )

# initialize the script plug-in
def initializePlugin(mobject):
	mplugin = OpenMayaMPx.MFnPlugin(mobject)

	classification = dlNodeClassify


	if (float(cmds.about(version=True)) >= 2015.0):
		classification = dlNodeClassify_2015


	# register node
	try:
		mplugin.registerNode("3DelightExampleShader", dlNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kDependNode, classification)

	except:
		sys.stderr.write("Failed to register node 3DelightExampleShader" )
		raise
 
# uninitialize the script plug-in
def uninitializePlugin(mobject):
	mplugin = OpenMayaMPx.MFnPlugin(mobject)
 
	# deregister node
	try:
		mplugin.deregisterNode(dlNodeId)
	except:
		sys.stderr.write( "Failed to unregister node 3DelightExampleShader" )
		raise
Code Block
languagecpp
themeEclipse
titleMaya AE Template (AE3DelightExampleShaderTemplate.mel)
collapsetrue
global proc
AE3DelightExampleTemplate(string $node)
{
DLM_3delightMaterialSwatchDisplay $node;
 
editorTemplate -beginScrollLayout;
	editorTemplate -label "Color" -addControl "color";
	editorTemplate -suppress caching;
	editorTemplate -suppress nodeState;
editorTemplate -endScrollLayout;
}
Adding Icons for the Outliner and Hypershade
OSL Shader Source (OSLVoronoi.osl)
/** Returns the U comp of the default UV set */
float GetS()
{
	float st[2];
	if( getattribute("st", st) )
	{
		return st[0];
	}
	else
	{
		return u;
	}
}

/** Returns the T comp of the default UV set */
float GetT()
{
	float st[2];
	if( getattribute("st", st) )
	{
		return st[1];
	}
	else
	{
		return v;
	}
}


surface OSLVoronoi [[ string maya_type = "texture" ]] (
		float i_jitter = 1.0 [[
			string maya_name = "jitter",
			string maya_label = "Jitter",
			float maya_min = 0.0,
			float maya_max = 1.0,
			string maya_group = "3Delight Voronoi" ]],
		float i_seed = 1.0 [[
			string maya_name = "seed",
			string maya_label = "Seed",
			float maya_min = 0.0,
			float maya_max = 2.0,
			string maya_group = "3Delight Voronoi" ]],
		float i_uvCoord[2] = {GetS(), GetT()} [[
			string maya_name = "uv",
			int maya_hidden = 1,
			int skip_init = 1 ]],
		float i_uvFilterSize[2] = {0, 0} [[
			string maya_name = "uvFilterSize",
			int maya_hidden = 1,
			int skip_init = 1 ]],

		output color outColor = 0.0 [[
			int maya_hidden = 1 ]],
		output float outDistance = 0.0 [[
			int maya_hidden = 1 ]] )
{
	float edgeDist;
	float outside;

	/* 
		i_uvCoord is multiplied by 10 to produce a visible result with default 
		place2DTexture. This is similar to setting RepeatU / V to 10.
	*/
	point pp = point(i_uvCoord[0] * 10, i_uvCoord[1] * 10, i_seed);

	point position;

	point thiscell = point( floor(pp[0])+.5, floor(pp[1])+.5, floor(pp[2])+.5);
	float f1 = 1000;
	for (int i = -1;  i <= 1;  i += 1)
	{
		for (int j = -1;  j <= 1;  j += 1)
		{
			for (int k = -1;  k <= 1;  k += 1)
			{
				point testcell = thiscell + vector(i,j,k);
				point pos =
					testcell + i_jitter * ((vector)cellnoise(testcell) - 0.5);
				vector offset = pos - pp;
				float dist = dot(offset, offset); /* actually dist^2 */
				if (dist < f1)
				{
					f1 = dist;
					position = pos;
				}
			}
		}
	}

	outDistance = sqrt(1 - f1);
	outColor = outDistance;
}
 



Anchor
icons
icons
The icons

You can add icons to both the Outliner and Hypershade (this applies to both texture nodes and shader nodes). The table below details the convention for creating the icons for our Voronoi Noise.

 

Outliner

Image Removed

Image Added

HyperShade - Node Lister

Image Removed

Image Added

HyperShade - Work Area

Image Removed

Image Added

Icon Resolution20 x 20 pixels32 x 32 pixels128 x 128 pixels (up to 512 x 512 pixels)
FormatTransparent 24 bits PNGTransparent 24 bits PNGTransparent 24 bits PNG
Naming
Convention
"out_" + <node_type> + ".png"

"render_" + <node_type> + ".png"

<node_type> + ".png"
Example

render_3DelightVoronoi.png

Image Modified

Note the transparent corners of the icon matching Maya
built-in

3D Textures

2D Texture nodes.