Thursday, May 8, 2014

Adding variation to objects using a single shader

I'm working on a scene with many (potentially hundreds) of objects which are pretty much the same. In fact there are many instances with the same shape node. I wanted some subtle variation in the color of the objects, not over the surface of an object, but on an object to object basis.


After doing some research, I encountered a method described here which uses a triple switch node. I've used shading switch nodes a little bit in Maya before so this seemed feasible. The biggest problem, however, was that the method doesn't work with instanced objects out of the box. So I set out to develop a "better" system that works even with instanced objects and combinations of instanced and non-instanced objects.

The idea is that you create a ramp that is piped into any attribute you want to vary. The ramp shows a range of colors and each object gets a random value which corresponds to a single location on the ramp. If you're not able to get a ramp looking random enough for the extent of variation you want, you can always play with the HSV Color Noise attributes or plug something else into the ramp.

1) The image below shows how to setup the initial shading network. Plug a V ramp into the attribute of choice in your material and then create a Single Switch which gets plugged into the place2Dtexture node (output > vCoord).

Edit Dec 2015: Probably simpler to skip the place2dTexture and plug the singleShadingSwitch.output directly to the vCoord of the ramp.



2) Then assign your material to everything you want to exhibit the variation.

3) Then open the AE for your shading switch and press the Add Surfaces button.

4) Then run this code. Just paste it into the script editor, select it all and "execute" or hit enter on the numpad.
string $switch = "singleShadingSwitch1"; //change this name to the switch you want
string $connectedAttr[] = `listConnections -c true -d false -t "shape" $switch`;
//print $connectedAttr; //DEBUG

for ($i = 0; $i < (size($connectedAttr)/2); $i++){

string $obj = $connectedAttr[($i*2)+1];
//string $parent[] = `listRelatives -p $obj`; //get parent transform for instanced transforms
// $obj = $parent[0]; //change object relationship to parent
//print $obj + "\n"; //DEBUG
if (!`attributeExists "randVCoord" $obj`){ //checks if attribute exists
  addAttr -at "float" -ln "randVCoord" -keyable true -hidden false -min 0 -max 1 $obj;
}
float $randCoord = rand(0,1);
setAttr ($obj + ".randVCoord") $randCoord; //set random number to custom transform attribute
if (!`isConnected ($obj + ".randVCoord") ($switch + ".input[" + $i + "].inSingle")`){
  connectAttr ($obj + ".randVCoord") ($switch + ".input[" + $i + "].inSingle");
}
}
You may have to edit the name of the singleShadingSwitch in the script. I was thinking making a custom input dialog but getting the functionality was difficult enough and I didn't want to delve into the whole GUI aspect.

5) Because this is all about visible connections, you can always tweak the ramp or even tweak the random values for individual objects. To do that, adjust the attribute called Rand VCoord to any value from 0–1. The effects probably won't show up in the viewport, but if you hit render the values should appear.

Here are a couple of examples:
Red to Blue ramp into the color
Fractal noise into the bump channel with shader switch controlling amplitude
 I was particularly concerned about instanced objects working with this and so I tested it on my molecular geometry. The first test only gave two different random values because it was the transform node that was itself instance above the coiled coil level of the hierarchy. So I pulled everything out of the hierarchy (not ideal, but works as a quick last step) which resulted in transform1 transform 2... transform 96 each with an instanced transform as a child. In order to put the random attribute on the proper transform, I just added two lines of code that changed the focus from the child to the parent (those are the two commented lines other than the debug print statements). Then I got the middle image, which is a bit extreme, but it works! So I tweaked my ramp a few times and got the image on the right in no time at all.


You can also use this method to add things other than random variation. Instead of a random value, you could assign the distance to the camera (mapped to 0–1 with setRange) or any other per-object transform info.

Later,
Stuart

4 comments:

  1. hi - i also get stucked with Zeth Willie's method.
    can you tell me how to connect the singleSwitch to the file2dtexture? not sure what attribute to connect ...

    and i also was not able to debug your mel script.
    perhaps some stupid formatting issue because of copy paste ... perhaps you can reupload it or?!

    hope to hear from you
    nice tut!!!


    thx matt

    ReplyDelete
    Replies
    1. I'm not sure what effect you are trying to achieve? This example is for a ramp (not a file texture), therefore the singleSwitch is just using a single value to define the position along the ramp to sample the color from. How do you plan to map your file texture across different objects?

      Also, what error message do you get when running the mel script?

      Delete
  2. I dont get how do you connect singleShadingSwitch node to place2dtexture?

    ReplyDelete
  3. In the attribute editor, drag the connection arrow from the Output circle on the singleShadingSwitch to the main input circle on the place2Dtexture and choose Other. You should be able to find Uv Coord in the list, expand it and choose vCoord. Alternately, I think it should work just as well (probably simpler) to skip the place2dTexture and plug the singleShadingSwitch output to the vCoord on the ramp node itself.

    ReplyDelete