Rendering Overscan in Maya

There are a few attributes in Maya you can change in order to render the image with overscan. The first is resolution, while the second is either camera scale, focal length, field of view, camera aperture, camera pre-scale, camera post-scale or camera shake-overscan. I use camera scale as it’s more intuitive numbers you need to enter and it doesn’t mess with the camera aperture, focal length or field of view.

In order to render and work with overscan correctly, it needs to be done relative to your format your working with – this is typically your final output resolution inside Nuke, but it could also be the resolution of a matte-painting or a live-action plate. The way to figure out the amount of overscan to use is simple and we can use one of two methods, either based on a multiplier or based on the amount of extra pixels we want to use.

The simplest method to me is based on a multiplier. If our format size is 480*360 (as above) and we wanted to render the image with an extra 10%, we multiply the resolution by 1.1 and set the camera scale to 1.1. Like so…

Then in Nuke all we need to do is apply a Reformat node and set it to our original render format of 480×360, the resize type=none and keep preserve bounding box=on  – this has the effect of cropping the render to our output size but keeping the image data outside of the format. Or additionally you can set the reformat like so… type=scale; scale=0.90909091; resize type=none; preserve bounding box=on. Instead of typing in 0.90909091, you can also set the scale by just typing in 1/1.1 …

If we instead wanted to render an extra 32 pixels to the top, bottom, left and right of our image – making the image 64 pixels wider and higher – we need to do things a little bit differently as we need to change the camera aperture. The reason for doing this is that adding the same number of pixels to both the width and height results in a very slight change to the aspect ratio of the image.

new width = original width + extra pixels
new height = original height + extra pixels
overscan width = new width / original width
overscan height = new height / original height
new aperture width = original aperture width * overscan width
new aperture height = original aperture height * overscan height

So using our 480×360 example from above. If we wish to add an extra 64 pixels to the width and height we would calculate it like so…

480 + 64 = 544
360 + 64 = 424
544 / 480 = 1.13333333
424 / 360 = 1.17777777
1.417 * 1.13333333 = 1.606
0.945 * 1.17777777 = 1.113

Same as before in Nuke we then apply a Reformat node with the following settings. type=to box; width/height=480, 360; force this shape=on; resize type=none; preserve bounding box=on

More VRay Scene Access… or some more random tidbits

Following on from the last post. Here are another example of how you can mess around with VRay scenes using Python.

figure 1: transform += random() * 2, random() * 2, random() *2

This collection of cubes was created using only one cube, it’s been instanced 2500 times and moved about randomly, to do this I’ve used the random module in Python which is handy for doing random number things.

# figure 1
from vray.utils import *

import random as r
r.seed(1)

l=findByType("Node") # Get all Node plugins
v=Vector(0.0, 0.0, 0.0)
for x in range(2500):
	dupl = l[0].duplicate('dup' + str(x))
	t=dupl.get('transform')
	v = Vector(r.random()*2, r.random()*2, r.random()*2)
	t.offs += v
	dupl.set("transform", t)

The r.send(1) is used to create a seed point for any future calls to random module, this means that the random numbers chosen are going to be the same each time we render the image – if we’re making changes to the render we don’t want the position of the cubes to change each time we render.

The v variable is used to store the random number we’re using to offset the transform, at the moment this is just is using random.random() which produces random values between 0 and 1, in the above example this has the effect of moving the cubes only along the positive xyz axis. There is also random.uniform(min,max) which produces random values between the min and max numbers we give it.

figure 2: transform += uniform(-1,1) * 2, random() * 2, random(-1,1) * 2

Here the effect moves the cubes along positive and negative XZ. I’ve keep the Y axis in positive space so that the cubes don’t go through the ground plane.

# figure 2
from vray.utils import *

import random as r
r.seed(1)

l=findByType("Node") # Get all Node plugins
v=Vector(0.0, 0.0, 0.0)
for x in range(20):
	dupl = l[0].duplicate('dup' + str(x))
	t=dupl.get('transform')
	v = Vector(r.uniform(-1,1)*2, r.random()*2, r.uniform(-1,1)*2)
	t.offs += v
	dupl.set("transform", t)

VRay Scene Access… or modifying your scene after you’ve hit render

Introduction

One of the lesser known features of VRay is it’s ability to access information about the scene and modify it after the render button has been pressed and before it is rendered. This ability to access the VRay scene and modify it allows you the ability to create some custom solutions to problems which might not be doable inside the 3d application itself. It can also be used to workaround bugs in VRay – but only as a temporary measure to get around bugs when a deadline is fast approaching.

Note: I’m using VRay for Maya. I am not sure how much of this is possible in tools such as Max or Softimage, hopefully this knowledge is easily transferable between 3d applications.

Examples…

Some simple examples of what you do with this include changing shader properties such as colour and texture information, duplicating and moving geometry around or even loading in extra geometry at render time.

All of these things you can do inside your 3d application, but might present problems if your dealing with lot’s of objects – for example you may have thousands of objects that you wish to do texture variants on, rather than create a shader for each object, you could set it up so that you can use one shader on all the objects and use an attribute on each object to specify which texture to use when you hit render.

To get a better idea of what is going on behind the scenes, the diagram below shows what happens when you hit render in your favourite 3d application. The Post Translate Python script is run during the translation process (the nodes in red).

In order to manipulate the scene data requires an understanding of the vrscene file format. The best way to do this is to turn on the Export to a .vrscene file setting in the Render Globals and have a read of the file it outputs.

The VRay Scene Structure and Nodes

The vrscene file describes the 3d scene in a human-readable ascii file. If you open it up in your favourite text editor you should be able to figure out what is going on quite easily, the section below determines the image width, height, pixel aspect ratio and it’s filename…

SettingsOutput vraySettingsOutput {
  img_width=450;
  img_height=337;
  img_pixelAspect=1;
  img_file="tmp/untitled.png";

Each section represents a node (plugin) that VRay recognises. The basic structure of each node is simply…

[Type] [Name] {
    [Attribute]=[Value];
}

So using the image settings example from above…

[Type] = SettingsOutput
[Name] = vraySettingsOutput
[Attribute] = img_width
[Value] = 450

As you move down through the vrscene you’ll move pass all your image settings, render settings, global illumination settings and down towards all your material, brdf, texture, transform and geometry nodes. For example you might see a few nodes which looks like this…

BRDFDiffuse lambert1@diffuse_brdf {
  color=Color(0, 0, 0);
  color_tex=lambert1@diffuse_brdf_color_tex@tex_with_amount;
  transparency=Color(0, 0, 0);
}

TexAColorOp lambert1@diffuse_brdf_color_tex@tex_with_amount {
  color_a=AColor(0.5, 0.5, 0.5, 1);
  mult_a=0.8;
}

MtlSingleBRDF lambert1@material {
  brdf=lambert1@diffuse_brdf;
  allow_negative_colors=1;
}

It’s the default Lambert shader in Maya, which is made up of three nodes, starting from the bottom we have the MtlSingleBRDF node, this is the top-level material which gets applied to our object. You’ll notice that the brdf attribute refers to the node at the top which is a BRDFDiffuse node, this node determines what type of shading model to use (diffuse, blinn, mirror, phong, etc). Finally is a TexAColorOp, this stores a colour value along with an alpha value – this value is used in the BRDFDiffuse node to give us our colour, this node is perhaps redundant as we can specify the colour directly in the BRDFDiffuse node. To visualize how these are all connected, think of them in terms of nodes inside Nuke or Houdini…

Finally we come to the object and geo nodes which look something like this…

Node pSphereShape1@node {
  transform=TransformHex("0000803F0000000000000000000000000000803F0000000000000000000000000000803FE8B64401FDEE7EA96AB5F3BF00000000000000000000000000000000");
  geometry=pSphereShape1@mesh1;
  material=lambert1@material;
  nsamples=1;
  visible=1;
  user_attributes="";
  primary_visibility=1;
}

GeomStaticMesh pCubeShape1@mesh2 {
  vertices=ListVectorHex("ZIPB600000001C000000e7X81OBd6CFA3Xb712GUVKO4a886dYKD2YAA1ODEU9");
  faces=ListIntHex("ZIPB9000000036000000e7XKOVAHC79E523BHGNecBbRYaFa5SUJLaNJQacU6BC2UaSI67LYXAYOQJb0N6MZL28O79N1GJAUV7eNT");
  normals=ListVectorHex("ZIPB2001000021000000e7X81OY3TG4S11YMEZWOUH5bdV54FLVaU4DFGXQZbALLSKXD2FA");
  faceNormals=ListIntHex("ZIPB9000000041000000e7XQH4YFC8PHFTACH6UCF0ZcdXZKE4VAEK4FJGKIST0AUZREYbbB0TCbCEbbVcTEFEUbbAZ3MQV8d7CKR6L49d785aCaLYIA4DA");
  map_channels=List(
      List(
          0,
          ListVectorHex("ZIPBB40000003C000000e7X81O60O9EAZVBdOBaeK1U3QBFUDPGV6aSF5d1b4UKbE4CJIaUGbEb53TUBWAS3aEJZ5468IcAFdHDT3AAA2b7ZaS"),
          ListIntHex("ZIPB900000003A000000e7XUUVDECQRH923NF053bdb10CKCZA2PWbKGdUKQDaOKaFQ7HV7J719DREK7DTQ23Od3Xd73I2L5UIMMAQTQZT2")
      )
  );
  map_channels_names=ListString(
    "map1"
  );
  edge_visibility=ListIntHex("ZIPB0800000010000000e7XTYQd97OLXZ3TBAAIU92PP");
  primary_visibility=1;
  dynamic_geometry=0;
}

The first node (Node) is our object node and includes information about the transformation, geometry and material on the object. The second node (GeomStaticMesh) is storing information about the mesh – it’s vertices, faces, uv’s and normals.  You’ll notice that the transform and mesh data attributes are being stored as hex values, this is to save space in the file – you can write out ascii data if you want to. With transform data it’s not so bad and looks something like this…

transform=Transform(Matrix(Vector(1, 0, 0), Vector(0, 1, 0), Vector(0, 0, 1)), Vector(-1.231791174023726, 0, 0));

But with mesh-data you probably only want to write out ascii information for debugging purposes. Otherwise it makes the vrscene long and difficult to read.

Getting Started

The easiest way to see this all in action is to take the first example from the VRay documentation and run it by copying it into the Post Translate Python script field, which can be found in the Common tab within the Render Globals…

Editing the Post Translate Python in Maya 2009

Note: This brief section only applies to Maya 2009, you can ignore this section if your using Maya 2011, 2012 or 2013.

If your like me and using Maya 2009 you’ll notice that the text entry field here can only take one line. This is because Maya 2009’s python interpreter can’t handle escape characters properly (in particular carriage returns). This script works-around the problem by removing the problem escape characters before setting the attribute correctly.

DOWNLOAD willVR_ptpEditor.mel HERE

Download the file, copy it to one of your Maya script folders and in the script editor run…

source "willVR_ptpEditor.mel";

A window will pop up that will allow you to edit the python code.

Users of Maya 2011+ can continue reading

The following python code…

from vray.utils import *

l=findByType("Node") # Get all Node plugins
p=l[0].get("material") # Get the material of the first node
brdf=p.get("brdf") # Get the BRDF for the material
brdf.set("color_tex", Color(1.0, 0.0, 0.0)) # Set the BRDF color to red

t=l[0].get("transform") # Get the transformation for the first node
t.offs+=Vector(0.0, 1.0, 0.0) # Add one unit up
l[0].set("transform", t) # Set the new transformation

All it does is change the colour to red and moves one of the objects up one unit – not particular inspiring or useful, but it is a good introduction to what you can do.

The ‘before’ render shows what the scene looks like when rendered without the modification, while the ‘after’ render shows what happens when I paste the above python code into the Post Translate Python field. There isn’t any performance hit with an example like this, but I can imagine that once you started getting into some fairly complicated python code and when your dealing with lot’s of nodes that it could create a performance hit.