Tutorial 3.6 |
This tutorial makes a spiffy macro that allows you to blur an image at any angle, not just horizontally and vertically, to give a motion-blurred effect:
It serves as a nice example of how to make a macro. So what the hell is a macro? With macros, you can combine commonly used functions into a new function that you can use to thwart your enemies. You can control what parameters are exposed, and hide the ones you don't need to change. Macros are extremely powerful ways of modifying functions to suit your own needs. The first image below has four nodes, the Text node and the three nodes that do the blur effect. The second image has the completed macro attached to an icon that we insert into a tree like any other function, and the third window shows that macro, RotateBlur, in the tree. The last image shows that only the angle and the blur parameters are exposed from the two Rotate functions and the Blur function that we used to make the macro. The purpose of this tutorial is to make the RotateBlur macro.
|
||||||
I start off with an Image - Text node, just so I have something to play with. Then, I attach a Transform - Rotate, a Filter - Blur, and then a second Rotate. The Text node doesn't appear in the final macro, just the last three functions: The algorithm works by rotating the image, blurring the image on the X axis, and then rotating it back to its starting position. Therefore, if Rotate1 has an angle of 45 degrees, Rotate2 has to have an angle of -45 degrees. The best way to do this is to link Rotate1 to Rotate2 by entering a link in Rotate1's angle parameter. By putting the negative sign before it, I make the result the opposite of Rotate2's angle. -Rotate2.angle Case sensitivity is important. Shake's functions always have capitalized letters for each word in a function name, so QuickShape has a capital Q and S, for example. The parameters follow the same rule, except the first letter is lowercase, for example the parameter framesPerSecond only has the P and the S capitalized. As you develop macros, it is considered good etiquette to follow these guides, because you never have to wonder which letters are capitalized. It isn't like we'll club you if you don't, though. We have linked up the two Rotates. Go to the Blur parameters, and enter 200 for the xPixel blur and 0 for the yPixel amount. Also, toggle spread over to Outside Frame (1). Now go back to Rotate2, and change its angle parameter. The blurring should be rotating around, but the text stays in relatively the same spot. If Rotate2's angle is 67 degrees, then Rotate1's angle is -67. Be sure not to modify Rotate1's angle, because that will break the link to Rotate2. If it isn't working for some reason, don't sweat it - copy the following text and paste it into the Node View with 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 it into the interface. Innat' cool?
|
||||||
Assuming that worked, we are now ready to start creating the macro. First, create a User Directory that Shake will look into every time it launches. In this directory, you can place macros, preference settings, and gui modifications. Each time you use Shake, it will look into your personal User Directory for this information. The User Directory has a series of subdirectories, but you have to create them:
You can add files into other directories as well. For further information, jump to Customize Shake. Now to make the macro file. Go into the startup directory and open up a text editor (vi, emacs, jot, gvi, wordpad...). Go back to Shake, and select the bottom three nodes of the tree by dragging the mouse: Hit Ctrl+C to copy them, or select Copy with the right-mouse menu in the Node View. Here's the swell part: Go to the text editor you opened, and paste it in with either middle-mouse (SGI) or Ctrl+V (NT). You've just pasted the source code from Shake into the editor, and it should look something like this: 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); // 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" ); If you copy the first two lines in the text editor, i.e., Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2, 0, 0.5, 0); Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba"); you can paste it back into the Node View
with Ctrl+V. Anyway, that's to show how you can copy between script and GUI, which is, in our humble view, mighty swell. If you already know C programming, you might want to jump down to the finished macro, otherwise keep reading. The text you have in your editor is now raw script, meaning it isn't formatted as a macro. If you paste it back in, you get three more nodes. We want to format the text as a single function, a macro. First, blow away the layout information. This is everything including and below User Interface Settings. These lines tells Shake where to place the nodes if you paste them back in. We don't need them, so get rid of them. You should be left with this: 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); The left side of the argument supplies a variable name. In this example, we have three variables, Rotate1, Blur1, and Rotate2. These are assigned to Shake functions, the Rotate and the Blur functions. Following that are all of the values for the functions. The first argument is generally the incoming image, so we can see that the incoming image for Blur1 is the result of the first Rotate function, Rotate1. Rotate1 has a value of 0 as the incoming image, meaning there is no expected input. If you look up these functions in the documentation, you can find the scripting format for each one. For an example, jump to the Blur manual page. If we changed the variable names Rotate1, Blur1 and Rotate2, we would have to modify every reference to them, as in the following example: Marshall = Rotate(0, -Holly.angle, 1, width/2, height/2, 0, 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 that to what you see in the GUI's parameters and you get an idea of how Shake saves its parameters in a script. Now, we add some formatting. We have to declare the type of the macro, if its spits out an image, an integer (int), a number with decimal place ( float), or a word (string). We want to return an image. After that, we add the macro name. In this case, we name the macro RotateBlur, capitalizing each word for consistency. We also include two sets of parentheses. The first is where we will put incoming variables, and the second set, the curly brackets, surround the body of the macro. I always add them first because 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); } Now we need to declare what information we want to pass into the macro. We potentially can change all parameters, meaning the rotate angle, center of rotation, motion blur parameters, blur filter, etc. Practically speaking, we really only need to change the rotate angle and the blur amount, giving us two sliders. We also have to declare that an image is passed into the macro to be modified. If we didn't have one, it would be assumed that we are somehow creating a new image, like Ramp or ColorWheel do. Every time we add an image variable, we get a knot on top of the node in the Node View, meaning we can plug an image in. Every time we add a float, string or int, we get a new slider or text field in the Parameter View. When adding these variables, we 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 we mentioned earlier about the first letter being lowercase for parameters. Place the variables inside the first set of parentheses: 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 we plug the variables into the right spot. Instead of the number or word, we use the variable name instead: 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); } We now tell Shake what we want to spit out of the macro with the return line, and then the variable name of the image we want to get out of the macro. Don't forget the semicolon at the end of the line. In this case, we are returning Rotate2: 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; } Here is the final macro. As a final touch, we can also add default values for any parameter, so when you launch it without adding any values in the command-line it will take the default arguments. image RotateBlur(image input, float angle = 45, float blur = 150) { 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; } When this is done, save the file in your startup directory. Make sure the file name has a .h file extension. Some NT programs automatically append a .txt onto the end, so watch out for that. There is no correlation at all between the file name and the macro name. I have saved my file as: <HomeDirectory>/nreal/include/startup/my_macro.h Now to test the macro. Go to the command-line. If you are using NT, we strongly recommend you download a tcsh port that is available either from our web site or from the highend2d site. The following steps all work in DOS, but, hey, it's DOS... You might be saying to yourself right now, "Hey, why am I testing this in command-line mode?", which is a good question. All we have done so far is create the macro - we haven't actually plugged it into the interface, which gets built each time you launch Shake. We (meaning you, not meaning Nothing Real...) haven't written anything yet that tells Shake to build this function into the interface. We will be doing that in a moment. So in the command line, type: shake -help rotate If everything worked properly, you should get a listing of all functions that have the word "rotate" in them, including our function, RotateBlur. The command line is the only place where capitalization doesn't matter, because we wanted to save you the effort of having to hit Shift+Whatever all the time. -irotate If you didn't get this help message, it will return an error telling you what went wrong. If you can't tell from the error message, check the following list of possible problems:
If you still can't figure it out, go and copy my finished macro into your startup Directory. Now to try it on an image. Go and find an image somewhere. There are images in the <ShakeDirectory>/doc/pix. If you are using these images, try shake lisa.iff -rotateblur shake lisa.iff -rotateblur 100 300 shake lisa.iff -rotateblur "Linear(1,0@1,360@11)" 200 -t 1-10 shake lisa.iff -rotateblur "Linear(1,0@1,360@11)" 200 -gui The "Linear..." that we use for the angle of the blur is an animation curve, using a Linear format. The first 1 means we extend the curve into infinity. The second set of numbers means we have a value of 0 at frame 1, and then we have a value of 360 at frame 11. Do the last command, it will launch the command into the GUI so you can play around with it there. 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 function to it. We are going to use the last command from above to build ourselves an icon in the GUI. The icons for the Tabs have three qualities:
Therefore, starting from what we have loaded in, I added a Transform - Move2D, a Window set to 75x40, and then a Layer - AddText to add on the name of the macro. Finally, I add a Color-SetAlpha 0 to remove the alpha channel. Again, just copy this and paste it into the Node View: Move2D1 = Move2D(0, -216, -304, 0.046, 1, 0.237, 0.237, 0, 0, width/2, height/2, "default", xFilter, "trsx", 0, 0, 0.5, 0, 0, time ); Window1 = Window(Move2D1, 0, 0, 75, 40); AddText1 = AddText(Window1, "RotateBlur", "Courier Bold", 11, xFontScale, 1, img.width/2, 8, 0, 2, 2, 0.92, 1, 1, 1, 0, 0, 0, 45 ); SetAlpha1 = SetAlpha(AddText1, 0); You should end up with an image like this:
We now have to write this to disk, so attach an Image - FileOut.
You should have already created the icons directory previously
in the tutorial, but you can use the Create Folder <UserDirectory>/nreal/icons/Filter.RotateBlur.nri Make sure you are evaluating the FileOut node (its name should
be in the title bar of the Viewer), and hit the right mouse on the Viewer
Flipbook button We now have the two elements that we need, the macro, and the icon. We now want to write the code to place both into the GUI. You can quit the GUI, since you have to re-launch it when writing ui code anyway. Now go to your <UserDirectory>/nreal/include/startup/ui directory, and launch a text editor in there. Inside this directory, you can place any modifications to how the interface works. There are many settings that you can check out, but the code we are concerned with looks like the following section. nuiPushToolBox("Filter"); nuiToolBoxItem("RotateBlur", RotateBlur(0,0,0)); nuiPopToolBox(); //These set the slider ranges for the blur parameter //nuiDefSlider("RotateBlur.blur", 0, 400 ); I never can remember this stuff, so I am always copying it from one file to another. Therefore, copy this into your own new text file. The first line tells what tab the macro will go into, in this case the Filter tab. If the tab does not exist yet (i.e., the MySwankFunctions tab), it will create it for you. The second line adds the function. The first occurrence of "RotateBlur" is calling the icon file that we 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 would have to rename the icon to MySwankFunctions.RotateBlur.nri. If you don't have an icon, you can create a plain button with text on it by placing an @ sign before the word: nuiToolBoxItem("@OopsNoIcon", RotateBlur(0,0,0)); As this example shows, you can have different name between the button and the function it is attached to. The next occurrence of RotateBlur is the actual function, followed by its default values. Because we don't know what image is coming in, we place a 0 as the first (image) variable, and values of 0 for angle and blur. The next line closes up the tab. Save the file as rotateblur_ui.h in your <UserDirectory>/nreal/include/startup/ui directory, and re-launch Shake. In the Filter tab, you should see a new button: If there is a problem, check the following list:
If the function worked, you can play to your hearts content and sit around with a smug grin on your face like the twisted pup you are.
|
||||||
If you create the RotateBlur node, move the sliders for angle and blur. You can notice how the angle slider goes from -360 to 360, but the blur slider goes from 0 to 1, forcing you to use the Virtual Sliders (Ctrl+drag in the text field) to go beyond 1. You can set your own slider range with a bit of code we 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 //. By commenting it out, it is not read when Shake starts up. By removing the comments, we reactivate the line. You should end up with this: 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. Again, spelling and capitalization are important, which is why that tiresome rule I explained earlier starts to make sense. We don't have to set a range for angle, because it is already set in one of Shake's setup files to automatically go from -360 to 360. If you want to change this, simply add a line. 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 ); Save your file, and re-launch Shake. The sliders should now be set properly.
|
||||||
Well, now that you have done this by hand, here is how to do it automatically with the new MacroMaker. The MacroMaker is a fast way of creating reusable macros, but it is important to understand the steps it is doing so you can further modify it. Because you (might have) already created the macro by hand, it helps demystify the process that the MacroMaker follows. Open up Shake, and paste in the original algorithm again. Just copy this text and hit 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 ); Select the bottom three nodes which will make up the macro: When these are selected, either hit M, or select MacroMaker from the right-mouse menu in the Node View. This will launch the MacroMaker: The top part of the window allows you to enter the name of the macro, and what tab it will go in. By default, it is called UserMacro, and will create a new tab called User. Feel free to change any of this information: The next line tells Shake where to store the macro:
The output tells you what node is going to be spit out of the new macro. Shake usually makes a good guess on this, but you may need to specify it explicitly if you have multiple branches that you selected. In this example, Rotate2 is the proper output. This is the equivalent to the line return Rotate2; in the macro. The next box contains the list of parameters that you want to add, the names you want to give the variables, and the default values you want to supply. We want to see Blur1's xPixels parameter a and Rotate2's angle parameter (but not Rotate1's). Therefore, I open up Blur1, and change the name of the xPixel variable name, and set the default value to 200. Notice how the visibility light turns on, indicating that we will see that parameter as a slider in the macro. If you don't want to see it, turn it off again. Now do the same for Rotate2's angle parameter: Notice that Rotate1's image In parameter is turned on automatically. We want to keep this, because this is where we indicate where the user's image will be plugged into the macro. Hit OK when you are ready, and you should have a new tab named User with a button called UserMacro in it: If you created the macro locally, go back to your User Directory. You should have a file in your nreal/startup/ui directory called UserMacro.h. Open it up, and it looks like this: nuiPushMenu("Tools"); nuiPushToolBox("User"); nuiToolBoxItem("@UserMacro",UserMacro()); nuiPopToolBox(); nuiPopMenu(); Go back to How to Add an Icon and create a new icon, or just do this - Remove the @ sign: nuiPushMenu("Tools"); nuiPushToolBox("User"); nuiToolBoxItem("UserMacro",UserMacro()); nuiPopToolBox(); nuiPopMenu(); Now go find your Filter.RotateBlur.nri icon in your icons directory and copy it as User.UserMacro.nri. Re-launch Shake, and you are set, except of course that the icon says RotateBlur, but that's your problem... |