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 | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Main Components
Three components are needed to 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.
node only OSL shader is needed. 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 codeCompiled OSL shader path | _3DFM_SLUSER_INCLUDEOSL_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 componentscompiled OSL shader path, 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
RenderManOSL Source Code
The Voronoi texture looks like a standard RenderManOpen Shading Language function but with some added structure:
optional metadata:
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
#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 surface OSLVoronoi( float jitter; matrix placementMatrix; output color outColor;) { float edgeDist; float outside; varying point ppi_jitter = 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: |
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
# 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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
/*
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
# 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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
global proc AE3DelightExampleTemplate(string $node) { DLM_3delightMaterialSwatchDisplay $node; editorTemplate -beginScrollLayout; editorTemplate -label "Color" -addControl "color"; editorTemplate -suppress caching; editorTemplate -suppress nodeState; editorTemplate -endScrollLayout [[ string maya_name = "jitter", string maya_label = "Jitter", string maya_group = "3Delight Voronoi" ]], float i_scale = 0.1 [[ string maya_name = "scale", string maya_label = "Scale", string maya_group = "3Delight Voronoi" ]], float i_seed = 1.0 [[ string maya_name = "seed", string maya_label = "Seed", string maya_group = "3Delight Voronoi" ]], float i_uvCoord[2] = {GetS() / i_scale, GetT() / i_scale} [[ string maya_name = "uvCoord", int skip_init = 1 ]], output color outColor = 0.0, output float outDistance = 0.0 ) { float edgeDist; float outside; point pp = point(i_uvCoord[0], i_uvCoord[1], 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; } |
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 | Note the transparent corners of the icon matching Maya |