Shader Dev Study 01: C++ & Arnold

Table of Contents

Abstract

  • This project is a study in C++ programming techniques to develop an Arnold Shader node for rendering in Autodesk Maya.
  • The shader is written and compiled in Professor Malcolm Kesson’s custom written script editor called “Cutter“.
  • The following implementation of the pattern node allows an artist 

    • to apply two colors to a surface based on a cutoff value in the ‘u’ texture direction. 

    • use a menu to choose the ‘u’ or ‘v’ or radial transition of the colors,

    • use a slider to perform a color blending of the two colors.

Algorithm

				
					/*
dd_redleft_pattern.cpp 
5 April 2021

Based on the shader code given in the Arnold doc "Creating a Shader".
https://docs.arnoldrenderer.com/display/AFMUG/Creating+a+shader#CreatingaShader-1.1.1InstallingaC++Compiler
*/

#include <ai.h>
#include <cstring>
#include <math.h>

AI_SHADER_NODE_EXPORT_METHODS(SampleMethods);
AtRGB mix(AtRGB a, AtRGB b, float alpha){
	return a*(1-alpha) +b *alpha;
	}	

namespace {
    enum paramIndex { k_pat_color, k_base_color, k_offset, k_direction, k_blur, k_urepeats, k_vrepeats, k_ushift, k_vshift};
    };

static const char* menu_items[] = { "U", "V","Radial", NULL };

node_parameters {
    AiParameterRGB("patternColor", 0.7f, 0, 0);
    AiParameterRGB("baseColor", 0.7f, 0.7f, 0);

    AiParameterFlt("offset", 0.5);
    AiParameterEnum("direction", 1, menu_items);
	AiParameterFlt("blur", 0);
	
	AiParameterFlt("u_repeats", 1); // u_repeats becomes "U Repeats"
	AiParameterFlt("v_repeats", 1);
	AiParameterFlt("u_shift", 0);
	AiParameterFlt("v_shift", 0);
    }

shader_evaluate {
    AtRGB base = AiShaderEvalParamRGB(k_pat_color);
    AtRGB pat = AiShaderEvalParamRGB(k_base_color);

    float offset = AiShaderEvalParamFlt(k_offset);
    int  dir = AiShaderEvalParamEnum(k_direction);
	float blur = AiShaderEvalParamFlt(k_blur);
	
	float u_reps = AiShaderEvalParamFlt(k_urepeats);
	float v_reps = AiShaderEvalParamFlt(k_vrepeats);
	float u_shift = AiShaderEvalParamFlt(k_ushift);
	float v_shift = AiShaderEvalParamFlt(k_vshift);
	
	float blend;
	// how all repeating patterns work...
	float u_copy = fmod(sg->u * u_reps,1);
	float v_copy = fmod(sg->v * v_reps,1);
	
	//row 
	int row = floor(sg->v * v_reps);
	bool rowIsEven = (row%2 ? 1:0); 
	if(rowIsEven)
		 u_copy = fmod((sg->u+u_shift) * u_reps,1);
	
	//column 	
	int col = floor(sg->u * u_reps);
	bool colIsEven = (col%2? 1:0);
	if(colIsEven)
		v_copy = fmod((sg->v + v_shift) * v_reps,1);
	
	//main
    if(dir == 0)
        //sg->out.RGB() = (sg->u >= offset) ? pat : base;
		blend = AiSmoothStep(offset - blur, offset, u_copy);//sg->u);
    else if(dir==1)
        //sg->out.RGB() = (sg->v >= offset) ? pat : base;
		blend = AiSmoothStep(offset - blur, offset, v_copy);//sg->v);
	else
		{
		// find the distance of the current shading point(u,v)
		// to the center of the uv space(0.5,0.5) and then 
		// test to the offset value.
		float a = v_copy - 0.5;//sg->v -0.5;
		float b = 0.5- u_copy;// 0.5- sg->u;
		float hyp = sqrt(a*a+b*b);
		blend = AiSmoothStep(offset - blur, offset, hyp);
	
		}
		sg->out.RGB() = mix(base,pat,blend);
    }

node_loader {
    if (i > 0)
        return false;
    node->methods        = SampleMethods;
    node->output_type    = AI_TYPE_RGB;
	node->name           = "dd_redleft_pattern";
    node->node_type      = AI_NODE_SHADER;
    strcpy(node->version, AI_VERSION);
    return true;
    }

// The remaining macros can be left "empty"
node_initialize { }
node_update { }
node_finish { }

				
			
				
					# This .mtd file is intended for use with Maya. If your shader will be used by
# Houdini the "style" of the .mtd can be set in Preferences->Languages-Arnold SDK.
# 
# This file is automatically updated by Cutter when its corresponding shader
# is compiled. Only edit the file immediately before publishing the shader. Make
# a backup of your edited .mtd file before recompiling the shader otherwise your
# edits will be over-written by Cutter.

[node dd_redleft_pattern]
	maya.id				INT		13
	maya.name			STRING	"dd_redleft_pattern"
	maya.classification	STRING	"Utility"
	[attr patternColor]
		maya.name		STRING	"patternColor"
#		desc			STRING	""
	[attr baseColor]
		maya.name		STRING	"baseColor"
#		desc			STRING	""
	[attr offset]
		maya.name		STRING	"offset"
#		desc			STRING	""
#		min				FLOAT		0
#		max				FLOAT		10
#		softmin			FLOAT		0
#		softmax			FLOAT		10
	[attr direction]
		maya.name		STRING	"direction"
#		desc			STRING	""
	[attr blur]
		maya.name		STRING	"blur"
#		desc			STRING	""
#		min				FLOAT		0
#		max				FLOAT		10
#		softmin			FLOAT		0
#		softmax			FLOAT		10
	[attr u_repeats]
		maya.name		STRING	"u_repeats"
#		desc			STRING	""
#		min				FLOAT		0
#		max				FLOAT		10
#		softmin			FLOAT		0
#		softmax			FLOAT		10
	[attr v_repeats]
		maya.name		STRING	"v_repeats"
#		desc			STRING	""
#		min				FLOAT		0
#		max				FLOAT		10
#		softmin			FLOAT		0
#		softmax			FLOAT		10
	[attr u_shift]
		maya.name		STRING	"u_shift"
#		desc			STRING	""
#		min				FLOAT		0
#		max				FLOAT		10
#		softmin			FLOAT		0
#		softmax			FLOAT		10
	[attr v_shift]
		maya.name		STRING	"v_shift"
#		desc			STRING	""
#		min				FLOAT		0
#		max				FLOAT		10
#		softmin			FLOAT		0
#		softmax			FLOAT		10

				
			

How to Install

  • Download this zip file.
  • Unzip and put the files under your Maya script folder:  Users/username/Documents/Maya/projects/arnold 

  •  In Maya > Hypershade > Arnold Utility, the node will be present.
* This node will only work on Mac OS.

Conclusion

Shader development and C++ has always been a giant mystery to me despite having Javascript coding experience. In this project, I was able to learn that C++ is not very different from other coding languages; though there are the exceptions of Dynamic Memory and Pointers which I have yet to understand completely. I do believe that in the weeks to come, I will develop a solid proficiency with C++ much like what I did with my Python programming studies. — 6 April 2021