Chapter 9: Lighting and Rendering


 Download CD Content
click to expand

Rendering is perhaps the area of Maya that artists most infrequently associate with MEL. However, there are actually sections of Maya completely dedicated to running MEL scripts for rendering purposes. In this chapter, we will construct two tools associated with rendering, including taking advantage of the aforementioned rendering-only scripts. Although our main focus is on rendering, we will also learn about building and navigating node networks, handling system commands, and using control nodes in Maya, so even artists who do not work with rendering very often would be wise to not skip this chapter.

Project 9.1: Creating Cubic Reflection Maps

Project Overview

Many artists mistakenly believe that the only way to create accurate reflections for an object in Maya is to raytrace. This, however, is not the case. Maya actually provides multiple ways of projecting a reflection onto an object. However, due to the relative difficulty in creating the maps used in these environment mappings, they are not often used except to provide a general effect, such as to provide a metallic look to a material. In addition, as game technology progresses, more and more artists are being called upon to create environmental maps that accurately reflect the real environment.

In this project, we will create a script that will render out cubic reflection maps. In doing so, we will learn about accessing both internal Maya variables and will work with the system command to use MEL to access the operating system itself.

Definition and Design

Cubic reflection maps in Maya offer some major advantages over raytraced reflections. First, they support any number of technologies that cannot be rendered with raytracing , like Maya fur, Paint Effects, and shader glows . In addition, there are a variety of ways a skilled shading Technical Director can use cubic reflections to influence a material other than simply using it as a reflection. Moreover, because the cubic reflections are external image maps, they can be modified using image manipulation software to easily create blurred reflections, or create other stylized effects.

In Figure 9.1, we see a sphere contained in a geometric grid rendered with raytracing.

click to expand
Figure 9.1: A raytraced reflecting sphere.
On the CD  

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

We can compare that to Figure 9.2, the exact same scene with cubic reflection maps, where the reflections have been rendered at a low resolution, creating a blurred effect.

click to expand
Figure 9.2: A cubic reflection mapped sphere.
On the CD  

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

Although there are differences, they are negligible, and it is rare that the reflections in a production scene would be used as prominently as they are here. In situations where exact reflections are necessary, in all likelihood raytracing would actually be used.

The definition of our tool is rather simple. Given any object, or any number of objects, our tool will render out six images, oriented and named properly to create a cubic reflection map for that object. While we could easily add the functionality to create an envCube node and assign the textures to the appropriate slots, for this example we will simply create the image files. Because our reflection maps will be specific to an object, and a material might be assigned to multiple objects, if we were to automatically create and assign an envCube map to an object we could create unwanted rendering artifacts.

The flowchart for our project is seen in Flowchart 9.1.

click to expand
Flowchart 9.1: Auto-reflect flowchart.

Research and Development

Before we develop a way of creating reflection maps, we should understand how they are used by Maya. The cubic reflection map is entirely dependent on the normal of the shaded surface using it. If we imagine the place3dTexture node of the envCube material as surrounding our reflecting object and where to draw a vector from the center point of the place3dTexture node through our shaded surface until it intersected the volume of our place3dTexture node, we can determine which pixel of which texture map is supplying the color information to be used. This is visualized in Figure 9.3.

click to expand
Figure 9.3: A diagram of how reflection maps work.

We can extrapolate that concept to define the outermost points of any cubic map by projecting vectors out to the eight points that make up the cube defining our reflection environment, seen in Figure 9.4.

click to expand
Figure 9.4: The cube in cubic reflection.

To create the images, we need to create six cameras , each located at the center point of the object and facing all six of the axes: positive X, negative X, positive Y, negative Y positive Z, and negative Z. As important as the direction a camera is looking is the orientation of the camera, and, of course, the camera node's settings.

To properly orient the cameras, we will again first build the structures by hand in Maya, and then recreate them in code. This is, as has become obvious by now, a standard way in which tools are often constructed in MEL.

On the CD  

Now, we create a sphere and assign it a reflective material, using an envCube to drive the reflections. To each of the texture channels, we assign the image found on the companion CD-ROM in images/chapter_09/reflect/reflect_basic.tga. If we render that image, we see something similar to that in Figure 9.5.

click to expand
Figure 9.5: The guide image used as a reflection.

We can use the reflections of our guide image to orient six cameras (the following values are in degrees)

Right
Rotation X: 0
Rotation Y: “90
Rotation Z: 0

Top
Rotation X: 90
Rotation Y: 0
Rotation Z: 0

Front
Rotation X: 0
Rotation Y: 180
Rotation Z: 0

Left
Rotation X: 0
Rotation Y: 90
Rotation Z: 0

Bottom
Rotation X: “90
Rotation Y: 0
Rotation Z: 0

Back
Rotation X: 0
Rotation Y: 0
Rotation Z: 0

We also need to set the cameras to create a square image. Set the Angle of View for each of the cameras to 90 , and both the vertical and horizontal film apertures to 1.0.

On the CD  

If we now set the rendering settings to a square image, such as 256x256, and render each of the six views, we can create proper images to use as reflection maps. As a guideline, we will first create some geometry to aid in the confirmation of the orientation of our images. This is seen in Figure 9.6, and can be found on the companion CD-ROM under scenes/chapter_09/reflect/reflect_reference.mb.

click to expand
Figure 9.6: The reflection reference geometry.

After naming the image files to appropriate names , we create a reflecting sphere and assign the images to its envCube node. The results of this are seen in Figure 9.7, although we have artificially darkened the reflection maps to easily differentiate it from the white background. This creates accurate reflections for our sphere.

click to expand
Figure 9.7: The six renders assigned as the reflection maps.

Now that we have worked out the process and details in Maya, we can move on to the coding of our tool.

Implementation

Our first step, as always, is to create and validate a script for our tool. We will call this script generateCubicReflectionMap . We want to have an argument to control the resolution of the output images, so we will include an int variable in the procedure declaration, as seen in Example 9.1.

Example 9.1: Declaration of the global procedure.

 global proc generateCubicReflectionMap          (int $resolution)     {     } 

Next, referring to our flowchart, we see that we will need to carry out all our operations on all the selected objects. In Example 9.2, we add a variable that holds the selection list, and use a for-in loop to iterate through each object.

Example 9.2: Parsing through the selection list.

 global proc generateCubicReflectionMap         (int $resolution)     {  //build selection list  string $selection[] = `ls -selection`;  //iterate through the selection list  for ($eachItem in $selection)         {         }     } 

Next, we will create the cameras to render out each of the six views. In theory, we could use one camera and reorient it between each render, but this methodology more closely resembles the one we developed inside the Maya workspace. We know we have to create six cameras, so a for loop is used. Each time through, we then name the cameras, although we will not yet orient them. We instead capture the renamed camera name to a string array, visible outside the loop, shown in Example 9.3.

Example 9.3: Creation of the cameras

 global proc generateCubicReflectionMap         (int $resolution)     {  // build selection list  string $selection[] = `ls -selection`;  // iterate through the selection list  for ($eachItem in $selection)         {  // create the six cameras, and name them  string $camHold[], $refCams[];         for ($i = 0; $i < 6 ; $i++)             {             $camHold = `camera                 -centerOfInterest 5                 -focalLength 12.7                 -lensSqueezeRatio 1                 -cameraScale 1                 -horizontalFilmAperture 1                 -horizontalFilmOffset 0                 -verticalFilmAperture 1                 -verticalFilmOffset 0                 -filmFit Horizontal                 -overscan 1                 -motionBlur 0                 -shutterAngle 2.513274                 -nearClipPlane 0.000001                 -farClipPlane 100000                  -orthographic 0                 `                 ;  // declare string to append to camera name  string $direction[];             $direction[0] = "_right";             $direction[1] = "_top" ;             $direction[2] = "_front" ;             $direction[3] = "_left" ;             $direction[4] = "_bottom" ;             $direction[5] = "_back" ;             $refCams[$i] = `rename $camHold[0]                     ($eachItem + $direction[$i])`;             }     } } 
Note  

If the script is evaluated in its current state, remember to delete the cameras that are created. While they won't cause the script to malfunction, they simply clutter the scene.

On the CD  

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

With the cameras in existence, we will now orient them to the six axes. However, to do this, we must first determine whether the user is working in radians or degrees. Putting in a unit settings check such as this is usually only needed when dealing with angular measurements, although it can be necessary with some linear measurement calculations, such as gravity.

We can find the current angular settings by making a query with the command currentUnit . We then use some conditional statements to set the rotation attributes of the cameras, using inline string addition. This is a rather simple process at this point, seen in Example 9.4.

Example 9.4: Orienting the cameras based on user settings.

  // Rotate the Cameras  string $angSettings = `currentUnit -query -angle`;     if ($angSettings == "deg")         {         setAttr ($refCams[0] + ".RotateY") -90;         setAttr ($refCams[1] + ".RotateX") 90;         setAttr ($refCams[2] + ".RotateY") 180;         setAttr ($refCams[3] + ".RotateY") 90;         setAttr ($refCams[4] + ".RotateX") -90;         }     if ($angSettings == "rad")         {          setAttr ($refCams[0] + ".RotateY")             `deg_to_rad -90`;         setAttr ($refCams[1] + ".RotateX")             `deg_to_rad 90`;         setAttr ($refCams[2] + ".RotateY")             `deg_to_rad 180`;         setAttr ($refCams[3] + ".RotateY")             `deg_to_rad 90`;         setAttr ($refCams[4] + ".RotateX")             `deg_to_rad -90`;         } 
On the CD  

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

Also note that rather than explicitly stating the values for the radian settings, we use the MEL command deg_to_rad , which converts the values during the evaluation of the script. This allows for an improvement both in the readability of the script, and in the precision of the assigned values. Because the radian values are based on the irrational number pi, doing a conversion during the assignment allows maximum precision to be maintained .

Once the cameras are rotated into the correct orientation, we need to move them to the location of our object. We could use the xform command in query mode, but there are no assurances that the transformation information of the object has not been modified by the user. Instead, we will find the middle of the bounding box of the object. We do this by querying both the minimum and maximum values and finding the average of the values returned, shown in Example 9.5.

Example 9.5: Finding the center of the object bounding box.

  // find the center point of the object  float $minB[] = `getAttr         ($eachItem + ".boundingBoxMin")`;     float $maxB[] = `getAttr         ($eachItem + ".boundingBoxMax")`;     float $objectPosition[]= {         (($maxB[0] + $mink[0]) * 0.5),         (($maxB[1] + $mink[1]) * 0.5),         (($maxB[2] + $mink[2]) * 0.5)         }; 

We then group the cameras together under a single transform, and move that transform to the location we just calculated. We could move each camera individually, but this method eliminates the loop we would have to construct to carry out such an operation, and allows us to have one easily deleteable node later to clean the cameras out of the scene. In addition, should our script terminate due to unforeseen circumstances, such as the rendering crashing, the scene has one easily findable node for a user to delete. This process is seen in Example 9.6.

Example 9.6: Grouping the cameras.

  // create the group containing all 6 views  select -replace $refCams;     string $camGrouping = `group -name "cameraGroup"`;     xform -objectSpace -pivots 0 0 0 $camGrouping;  // move it to position  xform         -worldSpace         -transform         $objectPosition[0]         $objectPosition[1]         $objectPosition[2]         $camGrouping; 
On the CD  

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

Next, in preparation for rendering we have to do two things. We will want to set the resolution and the corresponding image aspect ratio. These values are actually stored on a node called defaultResolution . We can also modify the image prefix on the defaultRenderGlobals . The values for all these are just attributes, and can therefore be accessed with simple setAttr and getAttr calls. We first want to capture the user s settings for these, to restore them after our renderings are completed. Anytime a script modifies settings, which are stored in a file, it is good practice, as well as simple politeness, to return those values to their previous state before ending the script. Notice in Example 9.7 that when setting a non-numerical attribute we must explicitly state the attribute type.

Example 9.7: Setting the rendering attributes.

  // set the rendering attributes  float $currWidth =         `getAttr defaultResolution.width`;     float $currHeight =         `getAttr defaultResolution.height`;     float $currAspect =         `getAttr defaultResolution.deviceAspectRatio`;     string $currPrefix =         `getAttr defaultRenderGlobals.imageFilePrefix`;     setAttr defaultResolution.width $resolution;     setAttr defaultResolution.height $resolution;     setAttr defaultResolution.deviceAspectRatio 1;     setAttr defaultRenderGlobals.imageFilePrefix         -type "string"         ""; 

While we could set up variables and setAttr commands to modify a variety of rendering settings, such as to turn on high-quality anti-aliasing, filtering type, and so forth, we will just use the current settings. Otherwise , we would be forced to either remove these settings from easy user control or add an enormous amount of complexity to our script command.

The last step before doing the renders is to hide the object, so that it does not occlude the view of the cameras. We do this by changing the visibility attribute, as in Example 9.8.

Example 9.8: Hiding the object by setting the visibility attribute.

 setAttr ($eachItem + ".visibility") off; 

Now we get to do the actual rendering. We will use the for-in loop in Example 9.9 to execute the commands for each of our six cameras.

Example 9.9: Issuing the renders.

  // Do renders  for ($eachCam in $refCams)             render $eachCam; 
On the CD  

The text for this script is found on the companion CD-ROM as /project_06/v04/  generateCubicReflectionMap.mel .

This will render out six images to the current project s "images" directory. However, the images have such useful names as _pSphere1_backShape_tmp0.tga . We will have to rename the image file, because we cannot use an argument with the render command to explicitly name the output image. To do this we will use the system command. While we could use the Maya command sysFile -rename , which is provided as a system independent way to rename files, this provides us a good opportunity to learn about the system command. In addition, using the system command will allow us to automatically overwrite our old image files, allowing for easy updates to our reflection maps.

Note  

All of the following commands deal specifically with the structures and commands associated with Microsoft Windows. Please consult your operating system documentation to find the appropriate commands.

The system command is potentially the most powerful and dangerous command available to a MEL scripter. Using the system command, Maya executes operating system commands, as would be issued from a command prompt in Windows, the terminal in OSX, or the shell in Unix. If we combine this with an external scripting language like PERL or a command-line program, such as a image processing routine, or an e-mail program, we can vastly extend the capabilities of Maya. For example, we could combine the system command and an error to automatically e-mail us whenever a user in the system encounters an error with a script, although this would not be advisable for a publicly distributed tool. Alternatively, we could have Maya send a cell phone text message when a render or complex dynamics simulation is complete. Although it might be disturbing to receive a message from a workstation at 3 a.m. that the cloth simulation is complete, it is possible.

Each operating system might require a specific level of access, such as administrator or root, before every command becomes available. Moreover, anytime we deal with files or system commands in a publicly distributed script, any operating system restrictions should be made clear to the user, or we should build multiple command structures based on the current user's operating system, which can be queried with the about command.

While MEL provides a variety of commands to work with external data, like the aforementioned sysFile command or the dirName script, often not all the commands you want to use have MEL equivalents, and are easier to accomplish if we keep similar command structures throughout a script.

Before we carry out any actions on the newly rendered image file, we need to know what that file's name is. Luckily, the render command returns the name of the image, which we capture to a string, as in Example 9.10.

Example 9.10: Capturing the name of the rendered file to a variable.

 string $renderedImage = `render $eachCam`;  // actual render  

Next, we will want to extract the name of the file itself. When Maya returns the name of any file, it uses the forward slash, "/", since the backslash "\" is used to signify escape characters . We will tokenize the returned image name with the forward slash, and assign the last item in the array to a string called $imageName . In Example 9.11, we see this process, extracting the last item in the array created by the tokenize command, which is the name of the file.

Example 9.11: Tokenizing the file path .

 string $tokenBuffer[];             tokenize $renderedImage "/" $tokenBuffer;             string $imageName =                 $tokenBuffer[(`size $tokenBuffer` - 1)]; 

Next, we will build a valid Windows path, which needs the backslash in between the names of each directory and subdirectory. We use a for loop to dynamically build the path, regardless of how long it is. we do an inline mathematic statement to not add the last element of the array, the filename. Because we are using the backslash in a string, we have to use a double backslash; otherwise, the backslash escapes out the quote marks, leading to an error. This process is shown in Example 9.12.

Example 9.12: Escaping out the backslash to preserve it within the string.

 $path = "";     for ($i = 0;             $i < (`size $tokenBuffer` - 1);             $i++)        $path = ($path + $tokenBuffer[$i] + "\"); 

In Example 9.13 we build a string with the image name, called $old , which contains a valid Windows path to the rendered image.

Example 9.13: Rebuilding the path.

 string $old = ($path + $imageName); 

We now want to rename the image according to the camera, which was previously named according to the object and the direction, so the corresponding image will be as well. We again use the tokenize command to split up the string data into individual components , so that we can append the appropriate extension to the new filename. The code for this is seen in Example 9.14.

Example 9.14: Building the new filename.

 tokenize $imageName "." $tokenBuffer;     string $extension = $tokenBuffer[1];     string $new = ($path + $eachCam + "." + $extension); 

Finally, we build a string of our DOS command. We want to enclose the entire path names in quote marks, in case the user has used spaces in any of the directory names. We again use the escape character, \, to tell MEL that the quotes should be included in the string, not terminate it. We use the move OS command rather than the rename OS command to automatically overwrite any previously existing files, letting us quickly do new iterations of reflection maps. We then execute the system command, passing it the string $systemCmd . We finally add the last curly braces, closing the rendering for-in loop. See Example 9.15.

Example 9.15: Building the system command string.

 $systemCmd = ("move \""                     + $old                     + "\" \""                     + $new                     + "\"");     system ($systemCmd);     } 
On the CD  

The text for this script is found on the companion CD-ROM as /project_06/v05/  generateCubicReflectionMap.mel .

After closing out the loop, we do some cleanup, deleting the camera group and returning the rendering attributes to their previous values, as well as making the object visible once more. Finally, before ending the script, we reselect the user's original selection, good practice when not creating any permanent new nodes. The code for this is seen in Example 9.16.

Example 9.16: Cleaning up the nodes we created.

 delete $camGrouping;     setAttr ($eachItem + ".visibility") 1;     setAttr defaultResolution.width $currWidth;     setAttr defaultResolution.height $currHeight;     setAttr         defaultResolution.deviceAspectRatio $currAspect;     setAttr         defaultRenderGlobals.imageFilePrefix         -type "string"         $currPrefix;     }     select -replace $selection;     } 
On the CD  

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

With that the script is finished. In Figure 9.8, we see the results of the script being executed two times on a collection of primitives.

click to expand
Figure 9.8: Multiple executions of the reflection script.

Because the objects already had a reflective material on them, with the reflection maps being updated with each execution of the script, an almost raytraced effect is achieved, without the time-consuming cost of raytracing. In addition, note the use of the Paint Effects strokes, which are also reflected in each of the surfaces.

Project Conclusion and Review

By creating cubic reflection maps, we can take advantage of the rendering features not supported by raytracing, avoid the rendering speed tradeoffs required to raytrace, and have finite control over the images used in the rendering process. More importantly, we learned how to take control of the operating system from within MEL. Whether it is to manipulate files, launch command-line tools, or even interact with another scripting language such as Perl, the system command is a powerful weapon in any scripter s toolbox.

Project Script Review

 global proc generateCubicReflectionMap                 (int $resolution)     {  //build selection list  string $selection[] = `ls -selection`;  //iterate through the selection list  for ($eachItem in $selection)         {         // create the six cameras, and name them         string $camHold[], $refCams[];         for ($i = 0; $i < 6 ; $i++)             {             $camHold = `camera                 -centerOfInterest 5                 -focalLength 12.7                 -lensSqueezeRatio 1                 -cameraScale 1                 -horizontalFilmAperture 1                 -horizontalFilmOffset 0                 -verticalFilmAperture 1                 -verticalFilmOffset 0                 -filmFit Horizontal                 -overscan 1                 -motionBlur 0                 -shutterAngle 2.513274                 -nearClipPlane 0.000001                 -farClipPlane 100000                  -orthographic 0                 `                 ;  // declare string to append to camera name  string $direction[];             $direction[0] = "_right";             $direction[1] = "_top" ;             $direction[2] = "_front" ;             $direction[3] = "_left" ;             $direction[4] = "_bottom" ;             $direction[5] = "_back" ;             $refCams[$i] = `rename                             $camHold[0]                             ($eachItem + $direction[$i])`;             }  // Rotate the Cameras  string $angSettings = `currentUnit -query -angle`;         if ($angSettings == "deg")             {             setAttr ($refCams[0] + ".RotateY") -90;             setAttr ($refCams[1] + ".RotateX") 90;             setAttr ($refCams[2] + ".RotateY") 180;             setAttr ($refCams[3] + ".RotateY") 90;             setAttr ($refCams[4] + ".RotateX") -90;             }         if ($angSettings == "rad")             {              setAttr                 ($refCams[0] + ".RotateY")                 `deg_to_rad -90`;             setAttr                 ($refCams[1] + ".RotateX")                 `deg_to_rad 90`;             setAttr                 ($refCams[2] + ".RotateY")                 `deg_to_rad 180`;             setAttr                 ($refCams[3] + ".RotateY")                 `deg_to_rad 90`;             setAttr                 ($refCams[4] + ".RotateX")                 `deg_to_rad -90`;             }  // find the center point of the object  float $minB[] = `getAttr                 ($eachItem + ".boundingBoxMin")`;         float $maxB[] = `getAttr                 ($eachItem + ".boundingBoxMax")`;         float $objectPosition[]= {                 (($maxB[0] + $minB[0]) * 0.5),                 (($maxB[1] + $minB[1]) * 0.5),                 (($maxB[2] + $minB[2]) * 0.5)                 };  // create the group containing all 6 views  select -replace $refCams;         string $camGrouping = `group -name "cameraGroup"`;         xform -objectSpace -pivots 0 0 0 $camGrouping;  //move it to position  xform -worldSpace transform             $objectPosition[0]             $objectPosition[1]             $objectPosition[2]             $camGrouping;  // set the rendering attributes  float $currWidth = `getAttr defaultResolution.width`;     float $currHeight = `getAttr defaultResolution.height`;     float $currAspect = `getAttr                     defaultResolution.deviceAspectRatio`;     string $currPrefix = `getAttr                     defaultRenderGlobals.imageFilePrefix`;     setAttr         defaultResolution.width $resolution;     setAttr         defaultResolution.height $resolution;     setAttr         defaultResolution.deviceAspectRatio 1;     setAttr         defaultRenderGlobals.imageFilePrefix         -type "string" "";     setAttr ($eachItem + ".visibility") off;  // Do renders  for ($eachCam in $refCams)             {             string $renderedImage = `render $eachCam`;  //actual render  string $tokenBuffer[];             tokenize $renderedImage "/" $tokenBuffer;             string $imageName =                  $tokenBuffer[(`size $tokenBuffer`-1)];            $path = "";            for ($i = 0;                    $i < (`size $tokenBuffer` - 1);                    $i++)                $path = ($path + $tokenBuffer[$i] + "\");            string $old = ($path + $imageName);            tokenize $imageName "." $tokenBuffer;            string $extension = $tokenBuffer[1];            string $new = ($path                            + $eachCam                            + "."                            + $extension);                             + $old                             + "\" \""                             + $new                             + "\"");             system ($systemCmd);             }  // Clean Up Scene  delete $camGrouping;     setAttr ($eachItem + ".v") 1;     setAttr         defaultResolution.width $currWidth;     setAttr         defaultResolution.height $currHeight;     setAttr         defaultResolution.deviceAspectRatio $currAspect;     setAttr         defaultRenderGlobals.imageFilePrefix         -type "string" $currPrefix;     } select -replace $selection; } 



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