A Coherent System of Measurement for 3ds Max

Working on plugins for 3ds Max has lead to some very interesting realizations about the product. In particular, it has a number of functions and settings for converting units of length, allowing you to easily build scenes measured in leagues, fathoms, furlongs, or light-years. Internally, it stores all distances in inches and does the conversion when it displays them (probably — it can actually be changed to use a different internal measurement as well to make things more confusing). Furthermore, the fundamental unit of time in 3ds Max is the tick, 1/4800th of a second. The tick was so chosen because it evenly divides both 24 and 30 frames per second and because it’s annoying.

So, in summary we have the tick as a unit of time and the probably-inch as a unit of length. The problem is we have no measures for any other quantities, nor can we without a unit of mass. Since it is absolutely essential to do physical/engineering calculations in 3ds Max, I propose the following: adopt the ubiquitous teapot as a fundamental unit of mass. To be specific, we will use the standard Utah Teapot full of standard Utah Tea. Extensive research places this between 20 and 30 oz so for the sake of argument we’ll call it 710 grams. If you are from the Mountain View Computer History Museum and are unfortunate enough to stumble upon this, could you verify this for me?.

From these quantities, we can easily define a coherent system of measures as follows:

Quantity Unit Derivation Notes
Length Probably-Inch Fundamental unit Defined as being an inch if 3ds Max decides it should be so.
Time Tick Fundamental unit 1/4800th of a second
Mass Teapot Fundamental unit One standard Utah teapot full of standard Utah tea.
Velocity Tinch 1 probably-inch per tick
Square time Tock Tick2 It just makes sense
Acceleration Tonch 1 probably-inch per tock i.e. 1 tonch = 1 tinch per tick
Force Gigadormouse 1 probable teapot-inch per tock A dormouse is a creature that lives in a teapot. If you collected 1 billion of them, they might conceivably exert this much force (about 1.2 giganewtons or 269 million pounds of force).
Power Gigadormousepower 1 probable dormouse-inch per tick 91 horses are equivalent to one megadormouse.
Pressure 1 Gigadormouse per probable square inch Understandably, this is also a measure of stress.
Density 1 teapot per probable cubic inch This one is rather hard to visualize.

For the Greater Good of Humanity Itself, her are some interesting measurements:

  • Average air speed of an unladen swallow: 9.02 centitinches.
  • Acceleration due to gravity on Earth: 16.8 microtonches.
  • Power output of a 2012 Honda Cvic: 2.06 megadormousepower.
  • Atmospheric pressure at sea level: 32 kilodormice per probable square inch.

An Illustrative Shader for Unity

A huge amount of effort has been made in 3D video games to produce “realistic” looking graphics. The idea is that you should feel like you’re playing a movie and the rendering should look like it was filmed with a camera. While there are some exceptions, other artistic styles — such as trying to look like a cartoon, a painting, a drawing, etc. — used to be largely ignored, or at least restricted to 2D games. Thankfully, this is changing. Especially so in the indy crowd, but also in big budget games like Team Fortress 2. This is great for graphics programmers to explore different rendering methods and lets a shader become part of an overall creative goal instead of a simulation of a camera.

For my game Space Rocks, I decided that I wanted it to look like the cover of a 60’s science fiction novel: illustrated, and somewhat cartoonish, but at the same time not cell shaded and outlined like a comic book or a Disney cartoon. As I mentioned, Valve accomplished something like this in TF2. What’s more they published a very good paper on their work.

This is a tutorial on how to implement this kind of shader in Unity3D. The complete shader source is attached at the end of the document. If you’ve never done any shader before, you might want to find try a few simpler tutorials first. However if you know a bit about shading languages, but are perhaps not familiar with Unity, this should give you a good introduction. The complete source is given at the end of this page.

Paperwork: the bits that make up a Unity shader

We will be making a Unity3D “Surface Shader”. This isn’t so much a shader as a template from which Unity will create numerous vertex and fragment shaders for different rendering paths. To create a surface shader we specify a lighting model (in this case a custom one called LightingNPR) and a function that prepares the surface for lighting, surf. We will use a normal map (bump map) though it should be removed where it is not needed. Here’s our starting point:

The surf function is pretty standard, but we need to fill in LightingNPR .

The basics: Lambert’s model

Even if you have never heard of “Lambertian” shading, you’ve undoubtedly seen this if ever touched shaders before. It is the basis of almost all shaders and is used to compute diffuse illumination, \(I_d\), by taking the cosine of the angle between the surface’s normal vector, \(\hat{\mathbf{n}}\) and a unit vector towards the light \(\hat{\mathbf{l}}\). This makes sense because when the surface directly faces the light this angle will be zero and it’s cosine 1; if the surface is at right angle to the light then the cosine will be 0. This cosine is trivial to compute as it is simply the dot product of the two vectors: \[I_d = \max\{\hat{\mathbf{n}}\cdot\hat{\mathbf{l}}, 0\}\] Let’s add this to our lighting function, and include the surface colour, light colour, and attenuation:

This produces a smooth, realistic, and rather boring approximation of a matte surface, as illustrated by Suzanne the Blender Monkey:

Lambertian Diffuse

Lambertian Diffuse

Closer to illustaion: Gooch shading

Lambert’s model is used to directly compute the brightness of a surface. However, Gooch observed that artists often use other cues in addition to just light and dark to show illumination and shading. Specifically, warm colours (red, yellows, and oranges) show illumination and cool colours (blues, teals, and purples) show shading. We can incorporate these principles very easily using a light ramp: a one dimensional colour gradient imported into our shader as a texture.

We still compute Lambert’s diffuse term, but instead of using it as a measure of lightness, we use it as a position in the light ramp:

When we combine this with a ramp from a dark, cool colour to a warm, bright colour, we get a result much closer to an artistic rendering:

Simple Gooch cool/warm light ramp

Simple Gooch cool/warm light ramp

Simple Gooch cool/warm light ramp

This is an incredibly versatile tool for both illustrative and photorealistic shading. It can be used to produce some very unusual highlights:

An unusual light ramp

An unusual light ramp

An unusual light ramp

And it can even be used to create a cell-shaded effect if the texture contains only two colours and uses no filtering:

A two-pixel light ramp used for cell shading

A two-pixel light ramp used for cell shading

Controlling the transition: warped Lambert

The light ramp gives us excellent control, but for that final touch we want some finer control over how the shader traverses the ramp. To do this, we add a scale \(\alpha\), bias \(\beta\), and exponent \(\gamma\) to Lambert’s term: \[I_d = \left(\alpha(\max\{\hat{\mathbf{n}}\cdot\hat{\mathbf{l}},0\}) + \beta\right)^\gamma\]

This is called the “warped” diffuse model. By varying the scale, bias, and exponent parameters we can control the size of the shaded area, the total amount of difference between dark and light, and the sharpness of transition:

Scale: 0.5, bias: 0.5, exponent: 2

Scale: 0.5, bias: 0.6, exponent: 4

Defining edges

The shader still lacks some indication of the object’s edge. A common way to make the edges stand out more is to add a solid outline, but this clashes somewhat with the smoothness of the shading so far. Instead, we will use rim lighting. Similarly to Lambert’s, we compute this by looking at the cosine of the angle between the surface normal and the camera. Again, we can calculate the cosine using a dot product:

\[I_r = 1 – \max\{\hat{\mathbf{v}}\cdot\hat{\mathbf{n}},0\}\]

This will produce a smooth transition from the edge of the object to it’s face, regardless of the camera’s orientation. In order to make the transition a little sharper, we raise the whole thing to a power:

\[I_r = \left(1 – \max\{\hat{\mathbf{v}}\cdot\hat{\mathbf{n}},0\}\right)^q\]

This gives a little more definition to the object:

Rim lighting applied

Rim lighting applied

Rim lighting only

Rim lighting only

By varying the colour we can also produce interesting effects such as the alien glow on the asteroids in Space Rocks:

Space Rocks

Some Space Rocks with coloured rim lights to create an alien-looking effect.

Adding shininess

For shiny, glossy objects, we need a specular highlight. This is used to show a “reflection” of the light source in the surface. We will use Phong’s model as it is simple and widely used. Blinn’s model could be used and offers slightly better performance under some circumstances. To compute Phong’s specular highlight we need the vector \(\mathbf{\hat{r}}\) of the light direction reflected about the surface normal. This can be computed as \(\mathbf{\hat{r}} = 2(\mathbf{\hat{l}}\cdot\mathbf{\hat{n}})\mathbf{\hat{n}} – \mathbf{\hat{l}}\). However, Cg has a built-int reflect function. We compute the final highlight as the angle between the relfected vector and the camera. We raise the result to a “shininess” power to sharpen it (sometimes called the “Phong exponent”): \[I_s = (\hat{\mathbf{r}}\cdot\hat{\mathbf{v}})^p\]

Rim lighting and specular

Rim lighting and specular only. The diffuse colour has been changed from other images to accommodate the increase in brightness added by other light terms.

Rim lighting and specular only

Rim lighting and specular only


This shader is incredibly versatile. It easily accommodated the visual style I needed for Space Rocks and seems quite versatile. It can, of course, also be used to render in TF2’s style.


Our intrepid hero’s spacecraft.

A sinister alien

A sinister alien.

Complete Shader


New Website

Hello! I am Jonatron! I am a graphics and game programming in Victoria, BC, Canada and I will be populating this website with graphicsy/gamey/bloggy stuff. To launch it off, I’ve uploaded a release candidate of an asteroid blasting arcade game: Space Rocks. Play and enjoy.