This tutorial explains how to create your own HyperShade node. Shader development in 3Delight for Maya is very easy:
- You don't need to install a compiler or any compilation tool.
- You don't need to perform any compilation steps while developing your shader or texture.
- Every created shader or texture is cross platform compatible.
As examples, we explain how to develop a simple voronoi noise as a Maya 3D Texture and a simple Lambert shader.
Main Components
Three components are needed to create a single shader:
The shader or texture source code, in RenderMan Shader Language.
- A Maya Python file to act as a plug-in that registers.
- A 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.
Component | Installation Environment Variable | Default Location |
---|
RSL source code | _3DFM_SL_INCLUDE_PATH | C:\Program Files\3Delight\maya\rsl |
Maya Python Plug-in | MAYA_PLUG_IN_PATH | C:\Program Files\3Delight\maya\2015\plug-ins |
Maya Attribute Editor Template File | MAYA_SCRIPT_PATH | C:\Program Files\3Delight\maya\2015\scripts |
Icons | XBMLANGPATH | C:\Program Files\3Delight\maya\2015\icons |
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.
The Voronoi node viewed in the Node Editor. Note how place3dTexture
node is automatically supported.
Voronoi 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
.
#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.
# 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.
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.
/*
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 */
# 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
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
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 
| HyperShade - Node Lister 
| HyperShade - Work Area 
|
---|
Icon Resolution | 20 x 20 pixels | 32 x 32 pixels | 128 x 128 pixels (up to 512 x 512 pixels) |
---|
Format | Transparent 24 bits PNG | Transparent 24 bits PNG | Transparent 24 bits PNG |
---|
Naming Convention | "out_" + <node_type> + ".png" | "render_" + <node_type> + ".png"
| <node_type> + ".png" |
---|
Example | out_3DelightVoronoi.png 
| render_3DelightVoronoi.png 
Note the transparent corners of the icon matching Maya built-in 3D Textures. | render_3DelightVoronoi.png 
|
---|
4 Comments
Paolo Berto
Swatch rendering should be covered too.
3Delight
Indeed.
Maxime Bourassa
3Delight swatch rendering is only supported for surface shader nodes at the moment.
Paolo Berto
Good point, it would be good to have swatch rendering for patterns too.
And the docs will need too a creating surface shader node, maybe when there is custom brdf API this would be a meaningful tutorial to do and we could contribute to it.