This final project’s objective is to showcase the implementation of what our professor, Malcolm Kesson, calls  M.O.O.M. (Many Objects, One Material) shading. The following image is my artistic application of this technical concept.

Developed in Houdini and Maya, rendered with Arnold and composited in Nuke.

Concept Development

The original task mentioned the use of shipping containers. Later on, during the course it was permitted to change the concept to any other creative way of showcasing this utility. The idea of the shipping containers stuck in my mind, so I decided to go with it, but with a twist. I wanted to make some sort of space port. The sci-fi nerd in me thought that I could pull off something interesting with these concepts.

Procedural Modeling

I always want to keep pushing my grasp on proceduralism. So, with the excuse of this assignment, I started modeling the shipping container procedurally within Houdini. Since the idea was to feature many of them, I decided early on the development that I was going to keep everything fairly simple and low poly. Every object was going to be seen pretty small in the scene anyway.

The resulting model is very simple, but it was perfect for the goal. Also, funnily enough, I started with a hexagon and then decided to make it an octagon, thanks to procedural modeling, I just had to change the number of sides of the original geo, and magically it adapted itself. Proceduralism is awesome!

After I locked the design, I piled the containers in 4 blocks. I ramdomly removed some of them at the top to give some irregularity to the stacks to give realism to the scene. In order to have full control over the distrubution, all this was achieved procedurally of course!

Prototyping Textures

Variation was key for this project. On top of this, I wanted to make rich and believable textures for my shipping containers so the designer in me came up with 7 different imaginary shipping companies and made different logos for them.

With the logos in hand, I developed a base texture for the container and created 3 base variants of them. Next, I created 2 or 3 versions for each logo using the 3 base variants, but adding a different background color to them. With this I ended up with 17 different texture maps to assign to the containers.

Scene and Look Development

Now it was time to work on the overall general look of the containers. I used 2 ai Standard Shaders for them. One for the paint, one for the metallic parts. Clearly the paint job was going to be driven by the MOOM utility, but it needed additional details like a smudge/dirt map for controlling the specularity as well as a bump texture for the ridges of the surface.

Time to Implement the MOOM utility

The way this version of a MOOM shading utility is implemented is as it follows:

Arnold has a handy feature that allows it to read custom user data at render time. It is possible to take advantage of it to shade multiple objects with one material. In this particular case, by the use of a custom python script, it’s feasable to randomly add texture map paths to multiple individual elements (this attribute is added to the shape node of the object) to shade them differently using the same material (see graph below). Parallel to this, an OSL script is needed to read each individual path from the geometry shape. Finally to make it all render, the OSL utility is plugged to any input of an Arnold shader to use the texture paths to shade and render.

The two scripts featured in this project. One in OSL and one in Python.

faMoomTexture.osl
shader
faMoomTexture(
                string mappath="",
                color missingAttrColor = color(1,0,1),
                int linearize = 0
                [[
                string widget = "boolean"
                ]],
                float gamma = 2.2,
                int flipV = 0    [[
                string widget = "boolean"
                ]],
                output color resultRGB = 0)
{
  
string pathvalue;
if(getattribute(mappath, pathvalue) == 1) {
  
    resultRGB = texture(pathvalue, u, v);
    if(linearize == 1) {
        resultRGB[0] = pow(resultRGB[0], gamma);
        resultRGB[1] = pow(resultRGB[1], gamma);
        resultRGB[2] = pow(resultRGB[2], gamma);
    }
    if(flipV == 1) {
    resultRGB = texture(pathvalue, u, 1-v);
    }
    else resultRGB = resultRGB;
}
else
    resultRGB = missingAttrColor;
    
}
add_arnold_string.py
import maya.cmds as cmds
from random import uniform, choice
  
def add_string(attrname, paths):
    
    attrname = 'mtoa_constant_' + attrname
    shapes = []
    selections = cmds.ls(sl=True)
    for sel in selections:
        shapes = cmds.listRelatives(sel, shapes=True)
        # sel in a group
        if shapes == None:
            transforms = cmds.listRelatives(sel, children=True)
            shapes = []
            for tran in transforms:
                shapes.extend(cmds.listRelatives(tran,shapes=True))
                
        if len(shapes) == 0 or len(shapes[0]) == 0:
            print('Cannot find a shape to add attribute')
            continue
        for shape in shapes:
            if cmds.attributeQuery(attrname, node= shape, exists=True) == False:
                cmds.addAttr(shape, ln=attrname, sn=attrname, nn=attrname, dt="string")
            
            path= choice(paths)
            cmds.setAttr(shape + '.' + attrname, path, type ="string")

Running the Python script in Maya on my container geometry (see image below) created a custom attribute with the path to one of the 17 textures on each of the shapes.

In the Hypershade I created my custom MOOM utility and pointed it to the name of the custom attribute. Plugging the MOOM into Base color of an aiStandardSurface shades each individual object with one of the 17 texture maps in the diffuse.

With this, no matter how many containers were put into the scene, once the code was run on them and the shader assigned to them, they would always randomly have a different texture map on them. Below, a test with a few of them.

It worked pretty well! It was time to bring in the whole bunch of them!

With the bulk of the scene in place (and within Maya). I started developing a bunch of props to fill the scene around the piled containers. I was inspired by Cornelius Dämmerich 52Hz for my scene (mine is way more rudimentary than his, due to two facts, 1. Time and 2. He is undoubtedly better than me), so I wanted to have some lamps distributed around my containers. Again, I recurred to using Houdini for them to model them and place them around the containers.

While in development I showed this to my wife and she pointed out that there was no real way of knowing the scale of the scene without some sort of human element. Because of this I decided to prototype some low poly astronaut. I took one of Maya’s base meshes and from there I developed a simple astronaut, rigged it, and added a cloth simulation on it. I posed the rig differently a couple of times to extract some different postures from the asset.

Once I had the poses, I added some material and a texture to give them some shading. I’m obsessive, I would have liked to give them more detail and props, but again, this is a general scene, and with a very small timeframe so I kept my low-poly/low-detail mentality, called the astronauts done, placed them in the scene and moved on.

I added a couple of extra props, a camera, and shuffled the elements around the containers to create an interesting visual narrative.

After I was happy with the general layout of the scene, I added Jupiter in the background and worked on a couple of custom VDBs to give my space port some context and an atmospheric mood.

Rendering and Compositing

With the scene in place and lit, I went on and rendered it using some layers and the regular AOVs (direct, indirect; diffuse, specular etc…) to have control over the composition. This proved crucial when dealing with the background sky and fog volumes. Next a simplified breakdown showcasing the passes and compositing step for the project.

Conclusion

OSL and Python combined create an endless amount of creative solutions for shading and rendering obstacles.  They open endless possibilities to work around each 3D package’s and rendering engines limitations. However getting to a production valuable point as a competent artist wrangling this kind of solutions can be quite the challenge. It is necessary not only to think creatively, but also have a fairly comprehensive scripting knowledge. I hope to expand my logical thinking and expand my knowledge to overcome my gaps and limitations.