First published in 3DWorld Magazine 106 in 2008 – come on, I’m not that old, surely!? – This tutorial was set in motion by, apparently, someone called Dennis, in an email. Thanks, Dennis, you made me learn a truckload! Now you can all do it too.
Now, keep in mind that this was before Python Scripting was a big thing in Maya and that Komodo Edit is far better than some of the other free text editors out there. For more info on both of those click on the links above.
Right, it’s time to make a pin matrix, from nothing.
You can also get to the archive for this project here:
Sometimes, in the course of doing these Q&As, a question comes up that makes us have to use more than our current knowledge allows. This particular question is a case in point. Starting it, it soon became apparent that the original plan was not going to work out. It took a lot of searching and, in all honesty, a call to a colleague to get me on the right track. I mean, how was I going to get
individual UV sample information from an animation and apply it to an object? Answer: colorAtPoint.
Apparently, it’s been in the kit for quite some time but I have never needed to use it and, therefore, never knew it was there. I can now see a hundred uses for it. The colorAtPoint command is a simple way of extracting pixel information from a file based upon its UV coordinates. By sampling values from specific points across the surface of a texture, you can then pipe this info into an object to move it, scale it, whatever it. And if we use a script we can loop this to do it for every frame. Before we start building our pin matrix, however, there a few things that need to be understood.
We need to make sure that the aspect ration of our pins matches our image sequence. For this reason, I have decided to stick to a 640 by 480 image sequence. It is a standard resolution and is easily divisible. This way we can make a matrix that has 48 pins longitudinally and 64 pins latitudinally, and keep the aspect ratios the same so that our texture looks correct on our matrix. That makes us 3072 pins in all (ouch!) so is also important that you make sure to use NURBS for the pins as well as instances. (actually, you can do this with polygons these days [May 2017], but nurbs were still quite the thing at the time :)
Finally, and then we’ll get down to modelling, scripting and animating, a few words about UV coordinates. Think of them as X and Y in a front view pane. Your U values are like X and run horizontally and the V values are vertical.
Open a new scene in Maya. Now create a polygon plane with a width/height ratio of 64 to 48. Make the subdivisions the same. Now scale up the tile by 10 in all dimensions, rotate it 90 degrees in the X and move is 240 units in the Y. you should have something like the image above.
RMB click on the object to bring up its attribute marking menu and select Assign New Material > Lambert. In the Attribute Editor that pops up click on the Color checkerbox and navigate to your image sequence, or use the one supplied. Press six in the view pane. You should see your texture.
With the Attribute Editor still open, click the tickbox marked Use Image. Set your timeline to the length of your image sequence. In your animation Preferences, set the timeline animation playback speed to Play Every Frame. Now when you playback your animation you should see your animation playing on the tile.
In any view, create a nurbSphere with a radius of 5 at the origin. Rotate it -90 in the Y axis and then select the middle isoparm in the side view. From the Edit Nurbs menu select Detach Surfaces then delete the new back hemisphere. Select the remaining hemisphere and in component mode drag three new isoparms on the surface near the back. Now select Edit Nurbs > Insert Isoparms
In component mode still, switch your selection to Hulls and select the back three, scaling them down to about half their diameter. Now translate the last one back about 50 units until you have something that looks like a pin
In the front view move this new pin -315 in the X and to 5 in the Y until it fits in the bottom left hand square of your map grid. Rename it pin0 – that’s zero – and select Modify > Freeze Transformations followed by Edit > Delete By Type > History. Save your scene.
Selecting pin0, press CTRL+G to group it. Rename the group pins. Opening the group in the Outliner, select your pin and then select Edit > Duplicate Special > Option Box. In this option box be sure to set it to Instance and Parent. Change the translate Y to 10 and the Number of Copies to 47. Press Apply. Whoop-de-doo, you’ve got a vertical column of pins.
Select all 48 of these pins from the bottom up and in the duplicate window change the Y translate back to 0 and set the X to 10. Change the number from 47 to 63 and click Apply again. Eventually, you should have a grid of pins covering your grid. Save your scene. Select your pin group and under Display set Object Display > Bounding Box. Phew, that’s a bit faster to manipulate.
For the rest of the pin matrix, build a round edged front plate and set it about 50 unit in front of the pins. Duplicate it and make it twice as deep then move this behind the pins to make a back. Create four bolts with stems and put these in the corners. When you’re happy, group everything and sit it on the z-x plane for neatness. Or load in pinEdge.mb
colorAtPoint needs to specify an output (-o) of either RGB, RGBA or A where A is alpha, a U and V coordinate or samples(-u,-v and -su, -sv) and a source file. So, colorAtPoint -o RGB -u 0.5 -v 0.5 file1 returns the colour in the middle of file1 or colorAtPoint -o A -su 10 -sv 10 image1 returns 100 alpha samples uniformly spaced in 10 rows of 10.
Let’s try it. Open the script editor and type: colorAtPoint -o A -su 6 -sv 6 file1; Select all the text and press the numeric pad Enter key – This stops the text from being deleted as it runs the command. You should see a resultant string of 36 alpha samples corresponding to your image. They are sampled in the order you can see in the image, starting at the bottom left.
Now we need to find a way to use these results. Clear the Script Editor and type in: float $offsets = `colorAtPoint -o A -su 6 -sv 6 file1`;print $offsets; Press Enter as before and you get the same values but this time they are in an array called $offsets. Change print $offsets; to print $offsets; and you only get back 1 – the value of sample 20.
We have 3072 pins to animate here, so change your mel script to: float $offsets =`colorAtPoint -o A -su 64 -sv 48 file1`; print $offsets; Run this as before. To get these results takes longer, so we don’t want to use print statements. Move the print command onto a new line and put // to stop it from calculating.
Quick point about scripting. Scripting in the Script Editor is fine, but if you get more involved in scripting, use a text editor. You are able to save progressively without copying and pasting, but it can still be sourced and run in the script editor with ease. Notepad++ and Textpad (or Komodo Edit) are freely available and both handle syntax highlighting making commands easier to see.
We need to get this array into our pin’s Z translation. Add a few lines at the top of our script. Type on the first, string $obj = “pin”; and on the second string $attrib; $obj matches our pin name minus the zero. The $attrib will be used later on in our script, but declaring it at the top of a script is good practice in keeping all your variables together.
To use each array offset value in turn, we need to put them into a loop. Add this line: int $i; This sets us up a variable for the loop. Now add this line: for( $i = 0; $i < size($offsets); $i++ ) This loop starts $i at zero and finishes at the last offset array value. On two new lines add an open curly bracket and a close curly bracket.
Between these brackets create two new lines of script, the first: $attrib = ($obj + $i + “\n”);, the second print $attrib; The first takes our $name pin and adds the number of our offset onto the end of it then performs a carriage return starting a new line. The second line prints it. Select all the text and hit Enter. See all those pin names flashing by.
This is all well and good, but what we need $attrib to be is the pin Z transform attribute. Change the last part of the string \n to .translateZ so that the line now reads: $attrib = ($obj + $i + “.translateZ”); If you run this now you won’t have the carriage return, but your script editor will bring back the names pin0.translateZ through to pin3071.translateZ. This we can use.
Now comment out the print $attrib; Underneath it add this new line: setAttr $attrib $offsets[$i]; What this does is set each offset value to each point in turn through the loop. To make sure, move to around frame 75 in the timeline, select the script and hit Enter. If you look through the side view, you will see the points have moved a little in the Z, but not enough.
What we need is to increase the size of the offset. We do this be adding a multiplier to the amount attributed to $attrib. Change the last code line to: setAttr $attrib ($offsets[$i] * 10); Run the script again. The result is still too small. Change the 10 to 50 – after all our pin is 50 units long. Run the script again and check out the results. Dandy.
Assigning info at a single frame is fine, but we need to keyframe this if we want our pins to animate like our texture map. Add a new line under the last command: setKeyframe $attrib; If you run the script now, you’ll notice that it takes a little longer. If you move to another frame and repeat the script, you’ll see the keyframing results appear in the script editor.
What we have is a good script for a single frame. Now we need to loop this per script so it runs at every frame. Under the line $string $attrib add this: for ($frame = 1; $frame <= 100; $frame ++); On the next line put an open curly bracket. This creates a loop as long as our timeline and our animation sequence.
On the line after this curly bracket type in this command: currentTime $frame; The command currentTime is like clicking on a frame on the timeline, but in this loop it acts like a step forwards through the timeline. At the very end of the script put a closed curly bracket. Save your scene. Run your script. Now go have a cup of coffee. Or three. this could take some time to do. REally, a cup of tea…a big mug.
At each run of the setKeyframe command, the calculation time for the keyframing takes longer. This seems to be because of the accumulative effect required to add a new keyframe to your scene, so be prepared for a long wait. A useful idea is to break down the script into manageable chunks, say frames 1 – 20, 21 – 30 and so on so as to give you failsafe points to save your animation. Either that or, like a render, leave it running overnight, and wait for the results in the morning. Remember, in this instance alone, you are making 307,200 saved keyes for just four seconds of animation. Just keep reminding yourself, “Good things come to those who wait.”
When it comes to lighting the pin matrix, remember Chrome shaders do not really hold shadows well, so render off the shadow as a pass and comp it on later. Better than that, you might find that if you add a new coloured light to the right of the pin matrix, you’ll probably get more definition, as long as you make sure it does casts shadows.
First published in October of 2006 – wow, was it really 11 years ago? – This is a tutorial to recreate something like the book from Shrek. The question was sent to me by a Paul Greenwood, wanting to know how it should be done. Well, back in 2006 (heck) this is how I would have done it. Actually, most of this is still the same, so I’m putting it up here with only tiny edits.
Hope it’s of use!
..that “Shrek 2” took 10 million computer hours to generate its 1 hour and 45 minutes of CGI? Oh, you did. But did you know that a single frame of the city crowd scene took 35 hours to render? You knew that too. Okay, you got me on the trivia, but do you know, without sneaking a peek at the fact file above, how long it will take to recreate a book and page turns similar to the ones in the introduction to Shrek 2? Ah, you looked! That’s cheating.
Take a sneak peek at step 1, I won’t mind. Do you notice anything peculiar about it? That’s right. In the past I’ve given you something to start with but this time you’ve got nothing. Zilch. A big fat Zero. The reasons for this are twofold. In order to create a convincing page turn, you’ll need to create the book too. Secondly, and more to the point, if I just told you how to create a page turn it wouldn’t be much of a Q&A, would it? Page turns can be simple, a blendShape here, a non-linear deformer there, but opening a book and having the pages seem to fall comfortably into place is another kettle of fish entirely. That is why a page turn really begins with the book.
In this Q&A, the book is going to be a combination of polygons and nurbs. The book cover needs to be bound to a skeleton so we can animate the spine bending. To keep the rigging to a minimum we will use a polygon smooth Proxy. This is useful as we can also create clean UV mapping for texturing. However, because we do want smooth page turns these will be made from nurbs planes. That way we can increase their tesselation at render time. For all of these elements to animate collectively, we will also create a single node to drive everything.
Then we’ll make a double-sided shader so that our pages can accept shading on either side. Supplied for this is a finished scene with a weathered leather book cover. Something that would not look out of place in a land far, far away, actually. Speaking of far, far away, did you know that in the UK version of Shrek 2 the voice of the Ugly sis – oh, what’s the point…
Required: Maya 7 or above Difficulty: Intermediate Time took: 2.5 hours
Also required: Krita/Gimp/Photoshop for texturing
Starting from Scratch
Files for textures and reference scenes can be downloaded here: http://www.sip-sop.com/archive82/
Told you it was an empty screen. Create a polygon cube and move it 0.5 in both the X and Y axes. Extrude the top polygon up by four units creating four divisions. Now extrude out the top and bottom right-hand faces by 5 units adding five divisions. You should now have a very boxy C shape.
To round the corners, in the perspective view, select the front top corner vertex and the two vertices either side. Now select edit Polygons > Merge Vertices > Option box. Set the distance to 1 and Apply. Selecting the similar three points on the top and bottom of the mesh and repeat. Now select the eight vertices on the ends of the C-shape and drag them over by about 14 units.
Select the mesh object and scale it in the Z axis by about 28 or until you are happy with the dimensions. In the top view select the mesh and then select edit Polygons > Cut Faces. Holding down shift to snap to 45-degree angle increments, click on a vertical edge about 1 unit on from the top to create a horizontal cut. Repeat this one unit from the bottom.
UV Mapping and refining
Open up the UV Texture Editor. In an orthographic view select all the outer and edge faces of your book. Cylindrically map these polygons using a projection sweep of 180 with the spine central to the mapping. Scale down the Image Scale U and V to around 0.5 and move the projection down in the UV Editor. Repeat the process for the inside polygons, placing these UVs above the others.
In the front view, select the inner poly UVs deselecting the inner UVs afterwards in the UV Texture Editor. Still in the UV Texture Editor, change to a scale manipulator and stretch out the edges so they are visible around the edge. Add a Blinn shader with a checker in the colour to your mesh and pull around the UVs until you’re happier with the results.
Select your mesh and perform a Polygons > Smooth Proxy on it. Edit the proxy mesh until you like how the smooth mesh looks. Having created cleaner UVs in a simpler model, now add more faces by using the Cut Faces tool again, bisecting the book at regular intervals with 90-degree angles. Now add some randomisation to the CVs such as pulling up the corners slightly to simulate ageing.
Driven to animate
Make the smooth Mesh invisible for now. Create a skeleton like the one on the image above. The armature coming off into the book is for our fake page block and pages. Selecting the proxy mesh and then all of the joints except the armature, select Skin > bind skin > rigid bind > option box. Select Selected Joints and then hit apply. Now use Deform > Edit membership to clean up the bind.
Set a frame range of 1 to 25. Select joint1, group it to create a top node then rename this group to anim. With anim selected, open up the Attribute Editor and from its menu select Attributes > Add Attributes. In the new window that opens add the attribute name bookOpen, set the minimum to 0, the maximum to 1 and default to 0 and hit OK. This will be our animation driver.
At frame 1, click Animate > Set Driven Key > Set > option box. Select anim and click on the Load Driver button in the Set Driven Key window selecting attribute bookOpen. Select joint1 and shift select the translate and rotation axis, here rotateZ, in Set Driven Key window. Making sure anim.bookOpen and your rotate angle are at 0, click Key. Now set driven keys for the rotations down the joint chain.
At frame 25 set a keyframe on anim.bookOpen of 1. Now translate and rotate joint1, then rotate the other joints until you have a fully open book. Now repeat the Set Driven Key process from step 9 at frame 25. To check your keys, playback the 25 frames. Set Driven Key values appear in the Graph Editor and this is a good place to edit them for better results.
Go to Frame 1. Create a nurbsPlane with 5 U and V patches. Reposition its origin to its left edge and then point snap it to the armature joint. Scale it up to fit within the book. Now Modify > Freeze Transformations renaming it page1. Duplicate it. Move page2 down until it reaches the book back. Constrain > Parent page1 to the armature joint. Constrain > Parent page2 to joint1.
You can see that page 1 is being lifted by the armature. Using the same process above in steps 9 and 10, set driven keys for the armature so it transforms and rotates back towards to a slightly elevated angle. Now in component mode select the bottom isoparms of both pages and Create > Surfaces > Loft. Do the same for the top and side, to create our fake page block.
Deforming the page block
That top page looks a bit flat open don’t you think? Got to frame 1 and duplicate page1 renaming it page1Deformer. Remove any history and constraints and move it up in the y-axis. Select it and shift select page1 and then Deform > Create blend Shape. Go to frame 25, select page1 and in the Channel Box open the blendShape1 node setting its page1Defomer attribute to 1.
Edit the shape of the deformer until you get a nice page curl. This looks fine at frame 25, but at frame one it looks awful. To connect the blendShape.page1Deformer to the anim.openBook, we could use a driven key but as both nodes go from 0 to 1 we can use a quick expression. Open the Expression Editor and type in: blendShape1.page1Deformer = anim.bookOpen; then click Edit.
Now we’ve got a book that opens, but you may notice we haven’t got any pages. As preparation, we need to duplicate page1 at frame 1 and rename it turnPage. Delete any history and constraints and then move it up in the Y axis by a really tiny amount. Duplicate page1Deformer and move that up a similar amount, renaming in animPageDeformer. Now let’s make our first page turn.
Turning the pages.
Select animPageDeformer and shift select turnPage. Now select Deform > Create Blend Shape. Now select the page and Deform > Create nonlinear > Bend. Transform the Bend1Handle to the local origin of turnPage. Now select both turnPage and bend1Handle and group them twice calling the top group pageAnim1. Press Insert and curve snap their origins to the same spot as the bend1Handle. Now Constrain > Parent the top group to the page armature.
Keyframe turnPage’s blendShape to match page1’s. Add 25 frames to your timeline. At frame 20 Set the bend1.curvature to -1.1, it’s low bound to 0 and the high bound to around 3. Move it up to the top of your page so the curve bends upwards and inward but doesn’t change the page shape.
Keyframe the translate and rotate of bend1Handle and the curvature of bend1.
At Frame 30 rotate the bend1Handle clockwise 80 degrees, transform it down the page a little and set a new keyframe. At frame 40 move it to the bottom of the page and set a key on bend1.curvature of 0.8. At frame 50 set the curvature to 0. Now pickwalk up to the above group node and key its rotateZ from frame 24 to 50 to turn the page.
Select the blendShape2 node under turnPage and go to frame 30. Set a keyframe on the blendWeight. Got to frame 50 and now set a keyframe of -.5 to give a little shape to the page. Adjust your keys and their tangents to make everything smoother. Now select pageAnim1 and Edit
Duplicate > option Box checking the Duplicate Input Graph tickbox and Group under World radio button. Apply and repeat.
Now you’ve got three pageAnims, plus all their animation and deformation history. This means we now have two extra skeletons we don’t want. Go to frame 1 and delete all the pageAnim parent constraints and skeletons anim1 and anim2. Adjust them so pageAnim1 is just above pageAnim 2 which is above pageAnim3 as in the image. Now constrain them to the armature again as you did in Step 11.
Now when the pages turn they actually pass through each other because of their height order. Selecting the middle group of pageAnim1 set a transform keyframe as the page begins to turn. When it stops turning, move it left and down to the Y position of pageAnim3. Now pick the equivalent node under pageAnim3 and animate this up to the position of pageAnim1.
Congratulations, you’ve got a working book.
Double sided Shaders
Texture up your book, or open up step22.mb. Pages has two different sides but in 3D shaders work on both sides at the same time. What we need to do is create a shader which applies one material to one side and another material to the other. We can do this in Maya by creating a condition that uses an object’s normal direction as a switch.
Most of the scene is finished, but we need to apply shaders to turnPage1. Open the Hypergraph and selecting the page select Graph > Graph Materials On Selected Objects. You should now see a lambert shader called page1Mat in the Hypergraph. Create a Condition and a samplerInfo node.
Middle Mouse button click and drag the samplerInfo node onto the condition. Select Other as connection to open the Connection Editor.
In the Connection Editor, connect the flipped normal attribute of the samplerInfo node to the second term of the condition. This makes the condition register the facing normals as one value and the flipped normal as another. Now in a similar manner, connect condition1.outColor to the page1Mat.incandescence. Now double-click on conditon1 to open the Attribute Editor. Set its operation to Not Equal to distinguish our two normals from each other. Render a frame and you should see the page has a white and a black side. Create two Blinn shaders colouring one red and another green. Connect the red Blinn’s outColor to condition1.colorIfTrue and the green’s outColor to its colorIfFalse. Rename the green Blinn to page1Front and the red one to page1Back. You can now treat these as two normal, excuse the pun, shaders on one object.
For close proximity objects that appear to interpenetrate when rendered, increase the camera’s near clip and decrease the far clip making for more accurate rendering.
Yep, I’m that old. and as such I’ve had to find quite a few ways to get around things.
I could give you a list of ‘for instances’ here but instead of that I intend to give you a something much better. Each month I intend to put out a quick tutorial here to show you how to do all sorts of things. Some After Effects, some Photoshop, some Fusion, some 3D. All sorts of work arounds and hints, tips and tricks.
As well as tutorials, you’ll also get free elements and animation setups as well.
I’m also a superman of open source and free software, so I’ll be passing on as much of that as I can as well. Not just Blender, everyone knows about that, but other software that I use regularly to get around some of the more expensive routes to a great finished product.
I know I couldn’t have done some of things I’ve done without help, and I want to give back to you some of that as well.
Keep coming back to this page and you’ll find stuff appearing quite regularly.
See you soon.