Project 9.2: Rendertime Smoothing


Project Overview

Like non-linear animation, recent trends in computer graphics have led artists to largely abandon NURBs geometry in favor of subdivision surfaces, which offer the flexibility and ease of use of polygons, with the surface detail of spline surfaces. Different packages and renderers support subdivision surfaces in different ways, with Maya offering two possible ways of creating them. First are the hierarchical subdivision surfaces, the other being simple polygonal smoothing. It is interesting to note that the popularity of using polygonal smoothing in Maya is largely due to the work of Dirk Bialluch and his connectPolyShape MEL script.

Although there are many uses for the Maya subdivision surfaces, many artists still prefer or are required to work with polygon smoothed geometry. This could be because of the tools available only with polygons, using a renderer that does not support Maya subdivisional surfaces like Mental Ray, or a simple matter of artist preference. One feature that some other packages offer that Maya does not is the ability to have different smooth settings based on whether the model is in the viewport or being rendered. Some renderers, like PIXAR s Photorealistic RenderMan, tessellate the surface to an infinitely smooth level, based on the size of each polygon relative to the size of the final image. Other systems, like that found in discreet s 3ds max, let the user explicitly state a level of tessellation. Adding sub-pixel tessellation would require the functionality of the API, to add the ability to subdivide more than four times. Instead, we will add the second functionality to Maya using MEL, and at the same time expose a limitation of integrating MEL with rendering using the methods suggested by the documentation, and overcome it.

Definition and Design

We need to accomplish two distinctly different goals with our tool. First, we have to modify the polygon smooth node to allow a user some type of input. Because the smoothing is controlled by a node in Maya, specifically the polySmoothFace node, it is a simple matter of using the setAttr command to change the number of smoothing divisions. Although the commands to change the settings are a simple matter, we need to modify the Maya environment to automatically execute the script whenever a render is called. In addition, we will want to set the number of smoothing divisions back to the user level after the render is completed. The flowchart for the first aspect is seen in Flowchart 9.2.

click to expand
Flowchart 9.2: Adding the render smoothing attribute.

We could use a scriptJob that is executed whenever a new session of Maya is launched, or modify the command held in the Polygon menu, to automatically execute our script whenever the user invokes the polygon smooth command. However, we will be content explicitly executing the command.

The second script is much simpler in theory, but has to be called both before and after the render. It is seen in Flowchart 9.3.

click to expand
Flowchart 9.3: Flowchart of the attribute modification script.

It s interesting to note that we need to do similar sorting actions in both scripts to find the appropriate smoothing nodes. We can simplify matters by grouping the nodes together. Obviously, we aren t going to use a Maya group , so we need to find a way of grouping without grouping. While we could use a set , we will instead create artificial connections between objects. This is also useful for keeping track of objects within a script or expression, by avoiding explicit names . We will modify the design of the first script to make a connection between the polySmoothFace node and another node inside the Maya scene. Instead of using a user created node, we will, rather appropriately, connect to the renderGlobals node. The modified flowchart is seen in Flowchart 9.4.

click to expand
Flowchart 9.4: Modified flowchart.

Research and Development

Many aspects of the first script are built on the concepts learned to this point, such as finding the selection list and parsing through it. We have three things we need to know before proceeding to the first script:

  • How to get the history of an object

  • How to add attributes to an object via MEL

  • How to connect attributes of objects via MEL

By simply knowing these things, we can accomplish everything in the setup script. Fortunately, MEL provides commands for all three functions: listHistory , addAttr , and connectAttr .

The second script is much simpler, but requires us of the Pre Render Mel and the Post Render Mel capabilities of the Maya renderer. In the Render Globals window, under the Render Options section, you can see text fields labeled Pre Render and Post Render, as seen in Figure 9.9.

click to expand
Figure 9.9: The Render Globals window.

These two fields allow a user to have Maya execute a command either right before or right after the render. This command could be anything from using system commands to copy and convert files to sending an e-mail to the user at the completion of a render. Because these two fields are actually attributes held on the node defaultRenderGlobals , we can use the setAttr command to automatically add the Pre Render Mel and Post Render Mel behavior to our scene, adding this to the setup script after the smooth node modification script is complete.

Implementation

Our first step, as always, is to create and validate a script for our tool, seen in Example 9.17. We will first create the setup script, called  setupSmoothRender.mel .

Example 9.17:     Declaration of our procedure.

 global proc setupSmoothRender ()     {     } 

Our first task is find the current selection list and parse through it, using the for-in loop seen in Example 9.18.

Example 9.18: Finding and parsing the selection list.

 global proc setupSmoothRender ()     {  // build selection list  string $selList[] = `ls selection`;  // parse the array holding the selection list  for ($eachItem in $selList)         {         }     } 

Within the loop parsing the selection list we need to get the history for each object, which is returned as a string array, and parse it to find whether any of the nodes contributing to the history of an object are polySmoothFace nodes. The main users of this tool will often have other nodes contributing to an object s history, such as a skinCluster node, so we have to parse the array storing the history of the object, building a new array of only the polySmoothFace nodes. We have to make the variable declaration outside the for loop parsing the selection list, to make it visible outside the loop. The structure for this is shown in Example 9.19.

Example 9.19: Finding the polySmoothFace nodes attached to our selection.

  // declare array to hold polySmoothFace nodes  string $smoothNodes[];     for ($eachItem in $selList)         {  // build array holding all the items contributing   // to each object as we parse the array  string $objHistory[] = `listHistory $eachItem`;  // parse the history, check to see if   // it is a smoothing node  for ($histNode in $objHistory)             if (`nodeType $histNode` == "polySmoothFace")                 $smoothNodes[`size $smoothNodes`] =                     $histNode;         } 
On the CD  

The text for this script is found on the companion CD-ROM as /project_07/setupSmoothRender/v01/setupSmoothRender.mel.

Using the size command inside the assignment statement allows the array to be sized dynamically without having to use an additional variable.

Now that we have a list of all the smoothing nodes, we need to add the attribute to the nodes to give the user input. We will also want to set the newly added attribute to be keyable. We do this for two reasons. First, because without being keyable, the dynamic attribute would not be viewable in the Channel Box, where the other attributes are accessible. Moreover, staying with the theme of keeping things consistent, because the attribute divisions is keyable, so should the newly added attribute, which we will call renderDivisions . Before adding the attribute we first will check if the attribute exists on the node. We do this to avoid Maya reporting an error if the user has an object that has already had setupSmoothRender executed on it. This simple code is seen in Example 9.20.

Example 9.20: Adding our dynamic attribute.

  // parse the list of smoothing nodes  for ($eachNode in $smoothNodes)  // check to see if the node already has the   // added attributes, prevents error  if (`attributeExists "renderDivisions"                 $eachNode` != 1)         {         addAttr             -longName renderDivisions             -attributeType long             -minValue 0             -maxValue 4             -defaultValue 0             $eachNode             ;         // make the attribute keyable so that it appears in         // the Channel Box         setAttr             -edit             -keyable true             ($eachNode + ".renderDivisions")             ;         } 

Note: On the CD

The text for this script is found on the companion CD-ROM as /project_07/setupSmoothRender/v02/setupSmoothRender.mel.

On the CD  

At this point, we can prepare some test data, such as that seen in the scene  poly_smooth_test.mb found on the companion CD-ROM under scenes/chapter09/project07/. We have four polygon objects that have smoothing applied. If we now run setupSmoothRender with the objects selected, we can look in the Channel Box to see a result that should be similar to that seen in Figure 9.10.

click to expand
Figure 9.10: The added attributes.

Obviously, at this stage the added attributes don t really do much, but we can at least be assured the setup script functions properly. Before moving on to the Pre Render Mel and Post Render Mel scripts, we will catalog all the polySmoothFace nodes we just added this attribute to.

On the defaultRenderGlobals , we will add an attribute called smoothRender . This attribute will be a multi-message attribute, which will allow for multiple connections. We will also need to create a corresponding attribute on the polySmoothFace nodes. To keep things efficient, we will include adding the attribute and make the connection to the defaultRenderGlobals node in the same for loop we add the renderDivisions attribute in. Because the attribute needs to exist on the defaultRenderGlobals node already, we will place it before the for loop, again testing first to avoid any possible errors. This code, similar to that seen in Example 9.20, is shown in Example 9.21.

Example 9.21: Adding more dynamic attributes.

  // check for attribute existence, and adds   // it if necessary  if (`attributeExists "sRender"             "defaultRenderGlobals"`!=1)         addAttr             -longName sRender             -attributeType message             -multi             defaultRenderGlobals             ;  // parse the list of smoothing nodes  for ($eachNode in $smoothNodes)  // check to see if the node already has the   // added attributes, prevents error  if (`attributeExists "renderDivisions" $eachNode` != 1)         {         addAttr             -longName renderDivisions             -attributeType long             -minValue 0             -maxValue 4             -defaultValue 0             $eachNode             ;  // make the attribute keyable so that it appears in   // the Channel Box  setAttr             -edit             -keyable true             ($eachNode + ".renderDivisions")             ;         addAttr             -longName sRender             -attributeType message             $eachNode             ;  // connect the sRender attributes for   // later tracking  connectAttr             -force             defaultRenderGlobals.sRender             ($eachNode + ".sRender")         } 
On the CD  

The text for this script is found on the companion CD-ROM as /project_07/setupSmoothRender/v03/setupSmoothRender.mel.

If we select the defaultRenderGlobals node and display its up and downstream connections in the Hypergraph, we should see something similar to Figure 9.11.

click to expand
Figure 9.11: The connections to defaultRenderGlobals .

Next , we will write the script to modify the polySmoothFace.divisions attribute to increase the tessellation of the smooth objects and return them to their previous state after the render is completed.

First, as always, we create and validate our script, as in Example 9.22.

Example 9.22: Declaration of our new procedure.

 global proc smoothRender ()     {     }polySmoothFacelistConnections 

Once this is accomplished, we can begin to add our functionality. First, we will build an array of the smooth nodes we have added rendertime division to. While we could take a brute force approach and search the entire scene for any polySmoothFace nodes that have the attribute added, that would ignore the advantage we have of having set up the attributes in the first place. We can use the command listConnections to gather our node names, and assign the return to an array, seen in Example 9.23.

Example 9.23: Find all the nodes connected to defaultRenderglobals.

  // Find all the smooth nodes with render time   // smoothing attributes  string $sNodes[] = `listConnections                             defaultRenderGlobals.sRender; 

Then, we simply parse the array and switch the values held in the divisions and renderDivisions attributes. This allows us to both set the divisions attribute to the appropriate value and easily store the old value, as well as creating a very simple script that does not need any arguments. While it s true that creating a more exacting script is possible, this will serve our purposes well enough. The code to switch the attribute values is seen in Example 9.24.

Example 9.24: Switching the two values.

 for ($eachNode in $sNodes)         {  // get the values stored in the two divisions   // attributes.  int $renderDiv = `getAttr ($eachNode                                         +                                         ".renderDivisions")`;         int $userDiv = `getAttr ($eachNode                                         +                                         ".divisions")`;  // switch the attribute values  setAttr ($eachNode + ".divisions") $renderDiv;         setAttr ($eachNode                 + ".renderDivisions")                 $userDiv;         } 
On the CD  

The text for this script is found on the companion CD-ROM as /project_07/smoothRender/smoothRender.mel.

With this script now in place, we can now call it from the command line and watch how our smoothed objects switch tessellation with each execution, assuming the renderDivisions attribute has been set to a different value.

If we now place the command smoothRender in both the Pre Render Mel and Post Render Mel sections of the Render Globals window, as seen in Figure 9.12, and then set the Divisions of our smoothed boxes to 0, and the Render Divisions to 4, our scene is now ready.

click to expand
Figure 9.12: The Pre and Post Render setup.
On the CD  

This scene file is contained on the companion CD-ROM under scenes/chapter_09/smooth/pre_post_setup.mb.

Now we can issue a render and immediately see the results of our work. Looking at Figures 9.13 and 9.14, we can see the differences between the working settings and the render settings.

click to expand
Figure 9.13: The viewport view.
click to expand
Figure 9.14: The rendered view.

While we could, at this point, modify the setup script to edit the preRenderMel and postRenderMel attributes on defaultRenderGlobals to use the smoothRender script as soon as we run the setup script, there is one fatal flaw. Most likely, in actual production, our models will have more complexity than the simple cubes used in our tests. If we use a more complex model and issue a render, we can notice a pause between when the request is made for the render and when rendering begins. This is due to the time required to calculate the new surface. This, of course, is unavoidable, but we have a problem when we issue a batch rendering.

The Pre Render Mel and Post Render Mel script is called at every frame. What this means is that after each frame, our object s tessellation is returned to the user setting, and then re-tessellates it before rendering the next frame. This is both the strength and the weakness of Pre Render Mel and Post Render Mel scripts. So instead, we will use a scriptNode . Note that if the artists are using a deformation system, like a skin cluster, it might be preferable to smooth every frame, despite the time costs. Situations, as always, will vary.

ScriptNodes are similar to scriptJobs , in that they execute a MEL command when a specific action is done. The significant difference is that because they are an actual virtual object inside the Maya scene, the command is stored with the scene file. Note that if you call a custom Maya script from a scriptNode , or the Pre Render Mel and Post Render Mel for that matter, the script should be transported with the scene. Often, you can fully imbed the command in the scene using a scriptNode .

In addition, scriptNodes have a much more limited ways of being called than scriptJobs . Therefore, it is sometimes necessary to embed a scriptNode that builds a scriptJob when the file is opened.

If we want to edit a scriptNode inside of Maya, we use the Expression Editor, which has filter options to allow the selection of scriptJobs . The Expression Editor is seen in Figure 9.15, with the filter set to see scriptNodes .

click to expand
Figure 9.15: Editing a scriptNode in the Expression Editor.

As we can see, the option exists to execute the command held in the scriptNode either before or after, or more specifically to hold separate commands to be executed before and after the scriptNode condition. ScriptNodes have the following conditions that can be used:

  • On demand

  • On file load or when the scriptNode is deleted

  • On file load or when the scriptNode is deleted when the Maya session is not in batch mode

  • On software render

  • On software frame render

There are also some internal conditions that are used for controlling the UI. The two conditions that should immediately catch our eye are the software render and the software frame render conditions. The software frame render condition is functionally identical to the Pre Render Mel and Post Render Mel commands. The software render condition, on the other hand, executes only once before a render, and once at the end of the render, depending on settings. We will first build a test scriptJob , and then integrate that into the setup script.

Creating a scriptNode via the Expression Editor is very easy.

  1. Open the Expression Editor under Window > Animation Editors > Expression Editor.

  2. In the Expression Editor, type the name testScriptJob " in the name field.

  3. In the command area, enter in smoothRender and click the Create button.

  4. Then, change the pulldown labeled as Type to Software Render.

  5. Finally, select the After button, enter in smoothRender again, and click Edit.

Now, we can issue a software batch render, and the smooth node tessellation is computed only once, at the beginning of the render. We can now open the setupSmoothRender .mel file and add the MEL commands to build the scriptNode whenever a user adds render time smoothing to the scene. The actual creation of the script node is similar to that of any object. At the end of the setupSmoothRender procedure, if we add the code seen in Example 9.25, we will create the scriptNode , but only if one does not already exist.

Example 9.25: Creating a scriptNode.

  // Check for the existence of the scriptNode and create   // it if it does not  if (`objExists smoothRenderScriptNode` != 1)         scriptNode             -scriptType 4             -beforeScript "smoothRender"             -afterScript "smoothRender"             -name smoothRenderScriptNode             ; 

This will now complete all the functionality of our script.

On the CD  

The text for this script is found on the companion CD-ROM as /project_07/setupSmoothRender/finished/setupSmoothRender.mel.

Project Conclusion and Review

Using MEL commands in the Pre Render Mel and Post Render Mel sections of the renderGlobals is a powerful, if somewhat narrow, way of taking control of the render process. The fact that the commands are issued before or after every frame is at the same time both a positive and a negative, depending on which behavior is needed. However, by using scriptNodes , we can embed MEL behavior right in the scene file itself. Once again, Maya s open node based structure has allowed us to solve one problem in multiple ways depending on the situation at hand.

Project Script Review

setupSmoothRender.mel

 global proc setupSmoothRender ()     {  // build selection list  string $selList[] = `ls selection`;  // parse the array holding the selection list  for ($eachItem in $selList)         {  // declare array to hold polySmoothFace nodes  string $smoothNodes[];         for ($eachItem in $selList)             {  // build array holding all the items   // contributing to each object as we parse   // the array  string $objHistory[] = `listHistory $eachItem`;  // parse the history, check to see if   // it is a smoothing node  for ($histNode in $objHistory)                 if (`nodeType $histNode`                         == "polySmoothFace")                     $smoothNodes[`size $smoothNodes`]                         = $histNode;             }         }  // Add attribute to render globals   // check for attribute existence, and adds   // it if necessary  if (`attributeExists "sRender" "defaultRenderGlobals"`         != 1)         addAttr             -longName sRender             -attributeType message             -multi             defaultRenderGlobals             ;     // parse the list of smoothing nodes     for ($eachNode in $smoothNodes)  // check to see if the node already has the   // added attributes, prevents error  if (`attributeExists "renderDivisions" $eachNode`             != 1)             {             addAttr                 -longName renderDivisions                 -attributeType long                 -minValue 0                 -maxValue 4                 -defaultValue 0                 $eachNode                 ;  // make the attribute keyable so that it   // appears in the Channel Box  setAttr                 -edit                 -keyable true                 ($eachNode + ".renderDivisions")                 ;             addAttr                 -longName sRender                 -attributeType message                 $eachNode                 ;  // connect the sRender attributes for   // later tracking  -force                 defaultRenderGlobals.sRender                 ($eachNode + ".sRender")             }  // Check for the existence of the scriptJob and create   // it if it does not  if (`objExists smoothRenderScriptNode` != 1)         scriptNode             -scriptType 4             -beforeScript "smoothRender"             -afterScript "smoothRender"             -name smoothRenderScriptNode             ;     } 

smoothRender.mel

 global proc smoothRender ()     {  // Find all the smooth nodes with render time   // smoothing attributes  string $sNodes[] = `listConnections                             defaultRenderGlobals.sRender;     for ($eachNode in $sNodes)         {  // get the values stored in the two divisions   // attributes.  int $renderDiv = `getAttr ($eachNode                                         +                                         ".renderDivisions")`;         int $userDiv = `getAttr ($eachNode + ".divisions")`;         // switch the attribute values         setAttr ($eachNode + ".divisions") $renderDiv;         setAttr ($eachNode + ".renderDivisions")             $userDiv;         }     } 



The MEL Companion
The MEL Companion: Maya Scripting for 3D Artists (Charles River Media Graphics)
ISBN: 1584502754
EAN: 2147483647
Year: 2003
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net