A simple tutorial on using expressions with Paint Strokes.
Using TCL expressions in Nuke can help us to evaluate mathematical operations, as well as link values together to create something new. However, an often forgotten feature in Nuke is the ability to add expressions to RotoShapes and Paint strokes (which are also splines under the hood).
Nuke's built-in "Tracker linking dialog" (pictured above), helps us to link individual vertices to various things in a Tracker node, and is doing so by automatically adding TCL expressions for us! However, what if we wanted to link things the other way around?
Every time you create a paint stroke, vertex points are created, and each one is assigned a unique index number. You can visualize these by first enabling vertex selection, and then enabling the label points option in the RotoPaint node's Viewer toolbar, as shown below.
We can add an expression to any of these points by right-clicking and choosing "add expression".
When the dialog box first opens, you can type "curve" to return the current value, as is the case with any other knob. However, if you simply type the name of the curve, curve.3.main.x
in this example, you'll get an error...
In order to access the coordinates of any vertex point on a spline, you must type the full path, like so:
As you can see, the Syntax is: <nameOfNode>.curves.<nameOfBrushStrokeOrSpline>.curve.<vertexNumber>.main.<axis>
As a quick example to demonstrate a few concepts: let's create an animated Snake with nothing but expressions.
We'll start by creating the head with a Roto node:
We should also create a transform node underneath, with the center-point automatically aligned to the center of the bbox. We can do that by adding an expression to the Transform node's center knob:
Bounding boxes in Nuke have a single X and Y coordinate, as well as W (width) and H (height) amounts. We're finding the bbox's center point by first getting the input node's bbox coordinates, and then adding half the width and height. No matter how our rotoshape changes, our center point will remain in the center!
Let's give our snake a body with a RotoPaint node. I'm painting a rough stroke, reducing the number of vertices, and then adjusting the brush strokes hardness to match with the head.
Now let's add some expressions to our paint stroke's vertices. Firstly, we'll link vertex 0 to the snake's head by adding the Transform node's center point and translate values.
It works! Secondly, we want the rest of the brush strokes vertices to move the same way, just offset in time.
For vertex 1, we're returning vertex 0's coordinates from 6 frames earlier. We're also offsetting the X value by -250, to move it to the left of the snake's head.
We will do the same thing for vertex 2, except we need to double the values...
Go ahead and do this for every vertex in the paint stroke, incrementing the frame + x-position offset each time.
Now we can animate the snake's head with our Transform node, and watch what happens! In the spirit of only using expressions, I'm using my bm_NoiseGen gizmo to generate a random animation curve, and am expression-linking the output to our Transform node's Y translate knob.
One again, click the GIF to view full-res!
We can also make our snake's head rotate so that it's pointing in the direction it's travelling, by using the derivative expression. This expression returns the slope, or speed of a curve on any given frame.
Lastly, our snake is just missing one thing: a name. I, for one, don't want to be held responsible for naming our snake. After all, we want the computer to do all the work for us, right? I grabbed a big list of names starting with S from the internet, and keeping with the snake theme, I wrote a couple of lines of Python to get Nuke to choose a name for me:
1import random
2
3names = """Saban Sabas Sabastian Sabelo Saber Sabien Sabin Sabino Sacha Sachiel
4Sachin Sachio Sadaat Saddam Sadiri Sadler Saerys Sagan Sagar Sage
5Sahak Sai Said Sailor Saimir Saint Sajiv Sajiva Sakai Sakari
6Samson Samu Samuel Samuele Samuli Samus Samvel Samwise Sanaullah Sancho
7Sandeep Sander Sanders Sanderson Sándor Sandro Sandy Sanford Sangeet Sani Sanjay Sanjeev Sanjeewa Sanjiv
8Sansao Sansleon Sansone Santana Santhosh Santiago Santino Santo Santonio Santos
9Sarantis Sarat Sarell Sargent Sargis Sargon Sarkis Sarp Sarpong Sascha
10Sasha Sasuke Satchel Sathish Satia Satin Saturn Saturnino Satwant Saul
11Saulo Saurabh Sava Saviero Savio Savion Savit Savvas Sawyer Saxby
12Saxon Saxton Sayers Saylor Scent Scevola Schroder Schroeder Schuler Schuyler
13Schylar Scipio Scooter Scorewin Scorpius Scot Scott Scottie Scotty Scout
14Scully Seabern Seager Seamus Sean Seanan Seanix Seath Seathrún Seaton Seattle Seaver Seb Sebastian Sebastiano Sebastião Sébastien Sebastion Sebi Seder Sedrick Seeger Seeley Seger Sehun Seiji
15Selassie Selby Selevan Selim Selleck Selmer Selwyn Sem Semaj Semih
16Senan Sendhelp Seneca Seoras Seph Sephiroth Sepp Septimus Serafin Serafino
17Seraph Seraphim Serge Sergei Sergio Serhat Serri Rafael Serry Rafael
18Servando Seryozha Seth Seton Sevan Sevastian Sevastyan Seven Severiano Severin
19Severn Severo Severus Sevin Seymarion Seymour Shad Shadden Shaddock Shade
20Shaden Shadi Shadley Shadow Shadrach Shae Shafer Shai Shaikh Shainen
21Shakespeare Shalev Shalom Shaman Shamar Shamus Shandy Shane Shannon Shanon
22Shantanu Shaokun Shaquille Sharif Sharnovon Shasta Shauftan Shaughnessy Shaun Shauni
23Shaw Shawinook Shawn Shay Shaydon Shayne Shea Shearjashub Shedrick Shelby
24Sheldon Shelton Shem Shemar Shep Shepard Shephard Shepherd Shepley Sheppard
25Sheraga Sheridan Sherlock Sherman Sherrill Sherwin Sherwood Shia Shiloh Shimon
26Shimshon Shinon Shipley Shirley Shiva Shivam Shiwoo Shlomo Shmuel Shohta
27Shola Sholom Sholto Shomari Shon Showles Shrikant Shukur Shulem Shye
28Shyloh Shylon Si Sian Siarl Siarles Siavash Sid Siddarth Siddharth
29Sidney Siegfried Sierra-117 Sierre Siger Sigge Sigismund Sigmund Sigurd Sigurdur Silas Siler Sillan
30Silvan Silvano Silvanus Silven Silver Silverio Silvestre Silviano Silvio Sim
31Simão Simba Simbiah Simcha Simen Simeon Simeus Simkha Simo Simon
32Simone Simpson Sinan Sinbad Sincere Sinclair Sindre Sindri Sinhue Sinjin
33Sinqua Sione Sipho Sir Sire Sirius Sisamila Sitka Sivan Sivert
34Sixten Sixto Sixtus Siyu Sjoerd Skandar Skate Skia Skip Skipper
35Skofi Sky Skye Skylar Skyler Slade Slaid Slane Slate Slater
36Slavko Slayden Slayton Sloan Slobodan Sly Smith Smokey Snowden Snyder
37Socrates Sodapop Soeren Sofian Sofiane Sofien Sogoro Sohan Söhnke Sohrab Sol Solace Solan Solanus Soleil Soloman Solomon Solon
38Somerled Somerset Sondre Sonnen Sonny Sony Sophian Sora Soren Sorin
39Sorley Sorrell Sorren Sotiris Soumil Soumyadeep Souta Spade Spalding Sparrow
40Spartacus Spearman Spellman Spence Spencer Spenser Spike SPinach Spirit Spiro
41Spiros Springer Spurgeon Spurrier Spyder Spyridon Squandro Squeag Squire St. John Stacey Stacy Stafford Stahley Stamm Stampley Stan Stanford Stanimir
42Stanislas Stanislaus Stanislav Stanislaw Stanley Stannis Stanton Star Stark Starlin
43Starsky Staton Stavrianos Stavros Steele Stefan Stefano Stefanos Stefen Steffan
44Stein Steinar Stejonte Stelios Stellan Sten Steno Stephan Stephanos Stephen
45Stephon Sterling Stetson Stevan Steve Steven Stevenson Stevie Stevieray Stewart
46Stian Stieg Stig Stijn Stiles Stirling Stjepan Stockholm Stockman Stockton
47Stohn Stojan Stokely Stone Stoney Storm Stormalong Strand Stratton Strauss
48Street Strider Striker Stringer Striver Strom Struan Stryker Stuart Sturt
49Stuyvesant Sudhir Sueban Sufjan Sufyan Suhail Suhas Sukhdeep Sukhvinder Sukhwinder
50Sukrajan Sulaiman Sulien Sullivan Sully Sulo Sultan Suman Sumit Summit
51Sumner Sun Sundara Sunil Sunjit Sunny Suraj Sutter Sutton Suvo
52Suzaku Svein Sveinn Sven Sverre Swain Swaine Sweeney Swinton Sy
53Sydney Syed Sykes Sylar Sylas Sylvain Sylvan Sylvana Sylver Sylvester
54Sylvestre Sylvio Symeon Synclair Szczepan Szebasztián Szymon Søren"""
55
56names_list = names.split()
57
58print "Your snake's name is "+random.choice(names_list)
Running this code in Nuke's script editor returned this:
Nice to meet you, Serge! May your handy expressions slither into everyone's workflow.
You can download an example nuke script here (right-click > save link as).
While this is a silly little demo, there are numerous ways you can take this knowledge and apply it in your every day work. Perhaps you're doing beauty work or costume fixes and need to track a texture to a specific RotoPaint vertex point? Maybe you need to expression-link a rotoshape from a Roto node to another rotoshape in a SplineWarp node to improve efficiency? Or maybe you just need a snake friend to keep you company during the COVID pandemic... The list is endless!