Tutorial 3.6: Making Macros |
This tutorial makes a spiffy macro for a motion effect by blurring an image
at any angle.
![]() |
![]() |
So, what is a macro? Macros are commonly-used nodes that are combined into new nodes (that you can use to thwart your enemies). You control what parameters are exposed, and hide the ones you do not need. Macros are extremely powerful ways of modifying Shake to suit your own nefarious needs.
The following images demonstrate the creation of a macro. The first image has
four nodes, the Text node and the three nodes that combine to create
the blur effect. Following a long, arduous, painful, tedious, frustrating (like
this sentence) process, the second image shows the completed macro attached
to an icon (RotateBlur icon). The third illustration shows the completed
macro, RotateBlur, applied in the treeit looks like any other node.
The last image demonstrates that only the angle and the blur parameters
are exposed from the two Rotate nodes and the Blur node that comprise
the macro.
![]() |
![]() |
![]() |
![]() |
There is only one path to ultimate Enlightenment. Don't let anybody say it's some silly tripe like being nice to puppies, giving all your money to the poor, and calling your mother once a week (although, do not let this discourage you from trying that, too). Rather, it is achieved by dipping into the Shake scripting language with a text editor. This is detailed in the first three sections of the tutorial. What fun! Before your hand jumps over to the mouse to find another tutorial, the last part shows you how to interactively do the same thing inside Shake. There is a catch, however. To modify and enhance a macro, you must rely on the information found in the first parts of the tutorial. No rest for the wicked.
The first step in a macro is to build the effect using normal nodes.
Create an Image - Text node.
Attach a Transform - Rotate node.
Attach a Filter - Blur node.
Attach a second Rotate node.
The algorithm works by rotating the image, blurring the image on the X axis, and rotating back to the start position. Therefore, if Rotate1 has an angle of 45 degrees, Rotate2 must have an angle of -45 degrees. The best way to do this is by entering an expression to link Rotate1 to Rotate2.
Load the Rotate1 parameters.
In the angle parameter, enter the following expression:
-Rotate2.angle
Case sensitivity is important. In Shake, each word in a node name has initial capitalized letters. For example, QuickPaint has a capital Q and P. The parameters follow the same rule, except the first letter is lowercase. For example, in the parameter framesPerSecond only the P and the S are capitalized. As you develop macros, it is good form to follow these guides, as you never have to wonder which letters are capitalized. It's not like you'll get bludgeoned at the User Group Meeting you if you don't, though.
Load the Blur1 parameters
Set xPixels to 200.
Set yPixels to 0.
Toggle spread to Outside Frame (1).
Load the Rotate2 parameters.
Adjust the angle parameter. The blurring rotates, but the text stays in the same spot. If the Rotate2 angle parameter is 67 degrees, then the Rotate1 angle is -67. Do not modify Rotate1, because that breaks the link to Rotate2.
If it is not working for some reason, don't sweat itdelete your Rotate and Blur nodes, copy the following text, and paste it into the Node View with Command+V / Ctrl+V:
Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2,
0, 0.5, 0);
Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba");
Rotate2 = Rotate(Blur1, -36.9218, 1, width/2, height/2, 0, 0.5, 0);
Because Shake is a compiler, you can copy nodes from text and paste the text into the interface. Ain't it swell?
You are now ready to start creating the macro. First, create a User Directory that Shake looks in when launched. This directory is in your $HOME directory. You place macros, preference settings, and gui modifications in the User Directory. The User Directory has a series of subdirectories, which you can create in the Terminal.
Open the Terminal.
Note: You can add files to other directories as well using an environment variable. For further information, jump to Customize Shake.
Launch a text editor (TextEdit, vi, emacs, etc.).
In Shake, drag-select the Rotate1, Blur1, and Rotate2 nodes in the process tree.
Press Command+C / Ctrl+C to copy the nodes, or select Copy with the right-mouse (context) menu in the Node View.
You've just pasted the source code from Shake into the editor, and it looks something like this:
Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2,0, .5, 0); Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba"); Rotate2 = Rotate(Blur1, 42.61728, 1, width/2, height/2, 0, 0.5, 0); // User Interface settings SetKey( "nodeView.Blur1.x", "290", "nodeView.Blur1.y", "227", "nodeView.Rotate1.x", "290", "nodeView.Rotate1.y", "263", "nodeView.Rotate2.x", "290", "nodeView.Rotate2.y", "191" );
Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2,0, .5, 0); Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba"); you can paste it back into the Node View
with Command+V / Ctrl+V. This demonstrates that you can copy between script and GUI, which is, in our humble view, mighty swell. |
If you already know C programming, you may want to jump down to the finished macro, otherwise keep reading.
The text in your editor is raw script, meaning it is not formatted as a macro. If you paste it back in, you get three more nodes. You need to format the text as a single functiona macro.
Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2, 0, .5, 0); Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba"); Rotate2 = Rotate(Blur1, 42.61728, 1, width/2, height/2, 0, 0.5, 0);
The left side of the argument supplies a variable name. In the above example, there are three variables: Rotate1, Blur1, and Rotate2. These are assigned to Shake functions, the Rotate and the Blur functions. (Inside Shake, these are referred to as "nodes." Inside a script, they are referred to as "functions," as you can have other types of functions.) Following that are the values for the functions. The first argument is generally the incoming image, so you can see the incoming image for Blur1 is the result of the first Rotate function, Rotate1. Rotate1 has a value of 0 as the incoming imagethere is no expected input. If you look these functions up in the documentation, you find the scripting format for each function. For an example, jump to the Blur manual page. If you change the variable names Rotate1, Blur1, and Rotate2, you must modify every reference to them.
The following is used as an example only (it is not a step in the tutorial):
Marshall = Rotate(0, -Holly.angle,1, width/2, height/2, 0, .5, 0); Will = Blur(Marshall, 200, 0, 1, "gauss", xFilter, "rgba"); Holly = Rotate(Will, 42.61728, 1, width/2, height/2, 0, 0.5, 0);
Notice how the strings "gauss" and "rgba" are in quotation marks. Compare these to what you see in the interface parameters tab to get an idea of how Shake saves its parameters in a script.
Now, add some formatting. You must declare the type of the macroif the macro spits out an image, an integer (int), a number with decimal place (float), or a word (string). This macro needs to return an image, as it modifies an image. After that, add the macro name. In this case, name the macro RotateBlur, capitalizing each word for consistency. Also, include two sets of parentheses. Incoming variables are placed in the first set, and the second set (the curly brackets) surrounds the body of the macro. Add these first as they are easy to forget:
image RotateBlur( ) { Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2, 0, 0.5, 0); Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba"); Rotate2 = Rotate(Blur1, 42.61728, 1, width/2, height/2, 0, 0.5, 0); }
Next, you need to declare the information that is passed into the macro. You can potentially change all parametersthe rotate angle, center of rotation, motion blur parameters, blur filter, and so on. Practically speaking, only the rotate angle and the blur amount need to be set, giving you two sliders. You must also declare that an image is passed into the macro to be modified. If you don't have an image, it is assumed that you are somehow creating a new image, like a Ramp or ColorWheel. Each time you add an image input variable, a knot is created on top of the node in the Node View (so you can plug in an image). Each time you add a float, string, or int, a new slider or text field is created in the Parameter View. When you add these variables, declare the type of variable (again, either image, int, float, or string), and then the name. The names of the parameters should follow that pesky rule mentioned earlierthe first letter in the parameter name is lowercase:
image RotateBlur( image input, float angle, float blur ) { Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2, 0, 0.5, 0); Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba"); Rotate2 = Rotate(Blur1, 42.61728, 1, width/2, height/2, 0, 0.5, 0); }
Now, plug the variables into the right spot inside the macro body. Instead of the number or word copied from Shake, use the variable name instead. These values are later modified with sliders:
image RotateBlur( image input, float angle, float blur ) { Rotate1 = Rotate(input, -Rotate2.angle, 1, width/2, height/2, 0, .5, 0); Blur1 = Blur(Rotate1, blur, 0, 1, "gauss", xFilter, "rgba"); Rotate2 = Rotate(Blur1, angle, 1, width/2, height/2, 0, 0.5, 0); }
Next, indicate what you want to spit out of the macro with the return line. This is the final image (or float, int, string) that you want to extract out of the macro function. Remember the semicolon at the end of the line. In this case, return Rotate2, as it has the result you need:
image RotateBlur( image input, float angle, float blur ) { Rotate1 = Rotate(input, -Rotate2.angle, 1, width/2, height/2, 0, 0.5, 0); Blur1 = Blur(Rotate1, blur, 0, 1, "gauss", xFilter, "rgba"); Rotate2 = Rotate(Blur1, angle, 1, width/2, height/2, 0, 0.5, 0); return Rotate2; }
The following is the final macro. As a final touch, you should add default values for any parameter, so it launches without having to add values in the command line.
image RotateBlur( image input, float angle = 45, float blur = 150 ) { Rotate1 = Rotate(input, -Rotate2.angle, 1, width/2, height/2, 0, .5, 0); Blur1 = Blur(Rotate1, blur, 0, 1, "gauss", xFilter, "rgba"); Rotate2 = Rotate(Blur1, angle, 1, width/2, height/2, 0, 0.5, 0); return Rotate2; }
When this is done, save the file in your startup directory. Ensure the file name has a .h file extension. Some text editors automatically append an extension. This is bad. There is no correlation between the file name and the macro name.
/User/ImASpecialPerson/nreal/include/startup/my_macro.h
Now to test the macro. Go to the command line.
You might be saying to yourself right now, "Hey, why am I testing this in a Terminal?", which is a good question. All you have done so far is create the macroyou have not plugged it into the interface. You have not written anything that tells Shake to build this function into the interface. You do that in a moment.
shake -help rotate
If everything worked properly, you should get a listing of all functions that contain the word "rotate," including the new function, RotateBlur. The command line is the only place where capitalization doesn't matter, because it saves you the effort of having to press Shift all the time.
-irotate
[controlChannel] [invert]
-rotate [angle] [aspectRatio] [xCenter] [yCenter] [motionBlur]
[shutterTiming] [shutterOffset]
-rotateblur [angle] [blur]
If you didn't get this help message, it returns an error that tells you what went wrong.
The errors generally detail where you have a mistake or if you have a variable that is misnamed. |
If you can't tell from the error message, check the following list of possible problems:
If you still can't figure it out, copy the finished macro from above into your startup directory and save it as my_macro.h.
Now try it on an image. If you don't have an image, go to <ShakeDirectory>/doc/pix. If you are using these images, try
shake traffic_bg.jpg -rotateblur
shake traffic_bg.jpg -rotateblur 100 300
shake traffic_bg.jpg -rotateblur "Linear(1,0@1,360@11)" 200 -t
1-10
shake traffic_bg.jpg -rotateblur "Linear(1,0@1,360@11)" 200 GUI
|
The "Linear..." used for the angle of the blur is an animation curve, using a Linear format. The first 1 indicates the curve is extended into infinity. The second set of numbers indicates you have a value of 0 at frame 1, and then a value of 360 at frame 11.
The last command launches the tree into the interface. You can see your new RotateBlur node in action. However, you still can't actually click on anything to create a second node.
This section shows you how to create a button in the interface and attach a node to the button. The icons for the Tabs have three qualities:
In the Terminal, type:
shake -addtext RotateBlur -rotateblur
GUI
This launches the text into the interface with your RotateBlur
attached.
Attach a Transform - Fit to RotateBlur1 in the Node View.
Set the xSize parameter to 75.
Set the ySize parameter to 40.
Select Fit1 in the Node View and attach a Color - SetAlpha node.
Set alpha to 0 to remove the alpha channel.
The result is an image that is 75x40 pixels, no alpha channel.
Save the icon file.
Select SetAlpha1 in the Node View.
Attach a Image - FileOut node.
In the Browser, use the Directories pull-down menu to navigate to your $HOME/nreal directory (that is, /User/ImASpecialHumanBeing/nreal, whatever).
Continue to browse down to the icons directory. (You should have
created this directory in the first step of the tutorial. If not, use the
Create Folder
button to make this directory).
Save the image as <TabName>.<MacroName>.nri. (The .nri
extension stands for Nothing Real Icon. Aah, legacy.) To place the RotateBlur
function in the Filter tab, enter your file name as:
Filter.RotateBlur.nri
Select the Render FileOut Nodes command in the Render menu to render the file. Set renderFileOuts to All .
Click OK, and a 320x243 thumbnail appears.
You now have the two elements that you need, the macro and the icon. You now need to write the code to place both in the interface.
Quit Shake (since you have to re-launch it to write UI code anyway).
Go to the $HOME/nreal/include/startup/ui directory, and launch a text editor in the directory (or launch TextEdit from the Dock).
This directory contains all instructions to modify the interface. Paste (or type) the following into an editor:
nuiPushToolBox("Filter"); nuiToolBoxItem("RotateBlur", RotateBlur(0,0,0)); nuiPopToolBox(); //These set the slider ranges for the blur parameter //nuiDefSlider("RotateBlur.blur", 0, 400 );
|
The first line indicates the tab where the macro is placed, the Filter tab in this case. If the tab does not exist yet (for example, the MySwankFunctions tab), Shake creates it for you. The second line adds the function into the tab. The first occurrence of "RotateBlur" calls the icon file that you just created. It assumes you have matched the tab name with the file name, so if you actually did place this into the MySwankFunctions tab, you have to rename the icon file on disk to MySwankFunctions.RotateBlur.nri.
nuiToolBoxItem("@OopsNoIcon", RotateBlur(0,0,0)); As this example shows, you can have a different name between the button and the function it is attached to. |
The next part of the code, RotateBlur(0,0,0), is the function call, followed by its default values. Because Shake does not know what image it is attached to (until you actually press it), place a 0 as the first (image) variable. The second and third zeroes set the angle and blur parameters to 0. The next line closes up the tab.
Save the text file file as rotateblur_ui.h in your <UserDirectory>/nreal/include/startup/ui directory.
Launch Shake. In the Filter tab, a new RotateBlur button appears.
If there is a problem, check the following list:
If the function worked, walk with confidence and pride in the face of your enemies as you display your compositing prowess.
First, test the sliders.
Create the RotateBlur node.
Move the sliders for angle and blur.
Notice that the angle slider is from -360 to 360, but the blur slider is from 0 to 1, forcing you to use the Virtual Sliders (Ctrl+drag in the text field) to go beyond 1. Set your own slider range with a bit of code already included in the example from above. On the last line of your ui file, and only the last line, remove the comments, which are the //. When commented out, it is not read when Shake starts. By removing the comments, you reactivate the line. The result is the following.
nuiPushToolBox("Filter"); nuiToolBoxItem("RotateBlur", RotateBlur(0,0,0)); nuiPopToolBox(); //These set the slider ranges for the blur parameter nuiDefSlider("RotateBlur.blur", 0, 400 );
This sets the slider range from 0 to 400 for the blur parameter of the RotateBlur function. Spelling and capitalization are important, which is why that tiresome rule explained earlier starts to make sense.
You don't have to set a range for angle, because it
is already set between -360 and 360 in one the Shake setup files. To change
the angle slider range, duplicate the blur line and modify it to say angle.
This example sets it from 0 to 180:
//These set the slider ranges for the blur parameter nuiDefSlider("RotateBlur.blur", 0, 400 ); nuiDefSlider("RotateBlur.angle", 0, 180 );
For more information on customizing parameters, see the Customize section of the documentation.
Not the most fun way to do things, but it beats banging on rocks next to a road in the Georgia heat. This next part of the tutorial duplicates your labor by using the MacroMaker. It is a fast way of creating reusable macros, but it is important to understand the files it creates so you can modify the files. Because you (might have) already created the macro by hand, it helps demystify the process that the MacroMaker follows.
Open Shake, and, if you followed the above parts of the tutorial, load the script you created titled rotateblur_tree.shk.
If you don't have this script, paste in the original tree. Copy the
following text and press Command+V / Ctrl+V to paste it
into the Node View:
Text1 = Text(300, 200, 1, "Text", "Courier", 100, xFontScale, 1, width/2, height/2, 0, 2, 2, 1, 1, 1, 1, 0, 0, 0, 45 ); Rotate1 = Rotate(Text1, -Rotate2.angle, 1, width/2, height/2, 0, 0.5, 0 ); Blur1 = Blur(Rotate1, 181.9876, 0, 1, "gauss", xFilter, "rgba"); Rotate2 = Rotate(Blur1, 42.64842, 1, width/2, height/2, 0, 0.5, 0 );
You now have something like the following (again).
Place the cursor in the Node View and press M, or select MacroMaker from the right-mouse menu in the Node View.
The top part of the Shake MacroMaker window contains text fields for the name of the macro, and the tab it is placed in. By default, it is called UserMacro, and creates a new tab called User. Feel free to change this information.
In the Macro Toolbox text field, enter Filter .
Leave Store macro in set to User directory.
The "Store macro in" setting indicates where the macro is saved:
The output tells you what node is spit out of the new macro. Shake usually makes a good guess, but you may need to explicitly specify it if you have multiple branches selected. In this example, Rotate2 is the proper output. This is the equivalent to the line:
return Rotate2;
that you previously used in the first macro.
The next area contains the list of parameters you can expose to the user, with the name of the parameter, the default values, and the slider range. In this step, expose Blur1's xPixels parameter and Rotate2's angle parameter (but not Rotate1's).
Open the Blur1 subtree.
Change the name of the xPixel variable to blur (this doesn't conflict with the Blur function because of capitalization).
Set the default value to 0.
Ensure the Visibility light is on next to the parameter. Each visible parameter
appears as an input or slider.
Rotate1's In parameter is activated automatically. This is because an image is fed into it, but not accounted for by the nodes that you selected. This can cause problems (though not in this case), as all unattached image inputs are activated.
This process creates two files, which are similar to the files you previously created by hand:
You typically use the MacroMaker to create the initial files, and then modify the files with a text editor. You have created two macros, one by hand and an identical macro with the MacroMaker. Both of these processes create two nearly identical sets of files. The first files create the function, and appear in the $HOME/nreal/include/startup directory. The second set of files loads the macro into the interface, and is found in the startup/ui subdirectory. The interface files are separate as you may want the file to only appear in the command line. Why bother doing the function by hand? Not necessarily because you are being punished, but rather so you can expand your macros with custom behavior and buttons for the parameters, and to edit the functions. These processes are listed in the Customize and Scripting sections of the documentation.