Math in Nuke, the series: Merge & Premultiplication · The Grade Node · ColorCorrect · Saturation, Hue & HSV · Keying · Despill · Matching Colour & Light · Edge Extend · Lin, Log & Gamma · Filtering & Resampling · Edge Detection · Vectors · Trigonometry · Expressions & Curves
The Grade is the node we use the most, and the one we know the least. Its help text gives a short formula, but the node does a few more things: a clamp in the middle, a gamma that changes behaviour above 1 and below 0, a reverse mode with its own order. Foundry publishes the source code of the Grade as an example of its developer kit, so we can read exactly what happens to every pixel. This page goes through it line by line, with a Lab that runs the same code.
Useful Links
- Foundry NDK example: Grade.cpp (the source code)
- https://learn.foundry.com/nuke/content/reference_guide/color_nodes/grade.html
- Jed Smith: The Math of Color Grading in Nuke (video)
- The Compositor Mathematic: Gain
- The Compositor Mathematic: Gamma & contrast
- nuke-users: Equation of the Grade Node?
- https://natron.readthedocs.io/en/rb-2.5/plugins/net.sf.openfx.GradePlugin.html
- https://learn.foundry.com/nuke/content/reference_guide/color_nodes/ociocdltransform.html
- https://en.wikipedia.org/wiki/ASC_CDL
- Tony Lyons, Compositing Mentor: BlacksMatch
Index
- 01. Add, multiply, power
- 02. The formula in the help
- 03. What the code really does
- 04. The gamma above 1 and below 0
- 05. Clamps
- 06. Reverse
- 07. Every knob, one by one
- 08. Blackpoint, lift, offset and Toe
- 09. The gamma knob is inverted
- 10. Clamp and SoftClip
- 11. Grade and CDL
- Grade Lab (try it here)
- ColorCorrect, Saturation, Hue
- Nodes to paste in Nuke
01. Add, multiply, power
- Multiply (gain, exposure): every value is scaled, black stays black and the highlights move the most. In linear light it's like opening the aperture: one stop is ×2.
- Add (offset): every value moves by the same amount, blacks as much as whites. Like a veil of flat light.
- Power (gamma): 0 and 1 stay put and everything in between bends. The midtones knob.
The Grade is a combination of these three, in a precise order.
02. The formula in the help
The help of the node (and of the source code) says:
A = multiply × (gain − lift) / (whitepoint − blackpoint)
B = offset + lift − A × blackpoint
output = pow(A × input + B, 1/gamma)
A straight line (slope A, offset B) followed by a gamma. The line takes the input range blackpoint…whitepoint and stretches it onto lift…gain, then multiply and offset are applied on top. Written as a "fit", the first four knobs are:
(input − blackpoint) / (whitepoint − blackpoint) × (gain − lift) + lift
Good enough to understand the node. But if you rebuild it with an Expression node using this formula, values above 1 and below 0 don't match, as some artists found on the nuke-users mailing list. The source code tells us why.
03. What the code really does
For every channel, the Grade example in Foundry's developer kit does these steps, in this order:
1. A = (gain - lift) / (whitepoint - blackpoint) if whitepoint = blackpoint: A = 10000
A = A * multiply
B = offset + lift - blackpoint * A
2. the line: x = x * A + B
3. the clamps: black clamp: x = max(x, 0) white clamp: x = min(x, 1)
4. the gamma: G = 1 / gamma
x < 0 -> x (unchanged)
x < 1 -> pow(x, G)
x >= 1 -> 1 + (x - 1) * G (a straight line)
Three things are not in the short formula: the clamp happens before the gamma, negative values skip the gamma, and above 1 the gamma becomes a straight line with slope 1/gamma. Plus a safety net: if whitepoint and blackpoint are the same, the slope is set to 10000 instead of dividing by zero, which turns the Grade into a hard threshold.
04. The gamma above 1 and below 0
A real power curve explodes above 1: with gamma 0.5 (the exponent becomes 2), a highlight at 10 would go to 100, at 50 to 2500. On scene linear images full of values above 1, that would be a disaster. So the Grade continues the curve above 1 with a straight line that starts at 1 with the same slope as the power curve: 1/gamma. Highlights still move, but proportionally.
gamma 2 on a value of 3: 1 + (3 − 1) × 0.5 = 2 (a real pow would give 1.73)
gamma 0.5 on a value of 3: 1 + (3 − 1) × 2 = 5 (a real pow would give 9)
Below 0 the values pass through untouched (the power of a negative number makes no sense for an image). With a gamma of 0 or less the code takes another path: below 0 becomes 0, above 1 becomes infinity, the rest stays. Don't go there.
This is also why an Expression with pow(r, 1/gamma) doesn't match a Grade on bright CG: the Lab below draws the real curve, look at it above 1.
05. Clamps
black clamp is on by default: after the line, every value below 0 becomes 0. It protects the gamma from negative numbers, but it also kills the negative values of CG passes and of wide-gamut images (an ACEScg plate can have small negative values in saturated colours). On data passes and on anything you will grade again downstream, turn it off.
white clamp is off by default: on, it cuts everything above 1. Almost never what you want in scene linear.
Note the order: the clamp comes before the gamma, so a gamma can never create negative values, and a white clamp makes the linear segment above 1 unused.
06. Reverse
With reverse on, the node undoes itself: copy a Grade, paste it, tick reverse, and the image comes back. The code runs the steps backwards: first the inverse gamma (pow(x, gamma) between 0 and 1, and the line 1 + (x − 1) × gamma above 1), then the inverse line (x − B) / A, and only then the clamp.
It's exact as long as nothing was clamped on the way. A classic use: grade a plate to match a reference, comp on top, then reverse to give it back its original look.
07. Every knob, one by one
| knob (script name) | does | pivots around |
|---|---|---|
| blackpoint (blackpoint) | this input value becomes black | — |
| whitepoint (whitepoint) | this input value becomes white | — |
| lift (black) | where black goes | white (1 stays at gain) |
| gain (white) | where white goes | black (0 stays at lift) |
| multiply (multiply) | scales everything after the remap | 0 |
| offset (add) | adds to everything | — (moves all) |
| gamma (gamma) | bends the midtones, last step | 0 and 1 |
Note the script names: lift is black, gain is white, offset is add. They matter for expressions and Python (Grade1.white, Grade1.add). Every knob has four values, r, g, b and a, and each channel runs the maths on its own: a Grade never mixes channels, that's what the ColorMatrix is for. The default channels is rgb, so the alpha is left alone.
Lift vs offset. Both raise the blacks, but lift keeps white where it is (it rotates the line around 1), offset moves white too. In scene linear, where "white" is just 1.0 and not the brightest value, an offset is often the more honest way to add a veil, and lift the better way to match black levels.
08. Blackpoint, lift, offset and Toe
Four ways to "raise the blacks", and they are not the same thing. Here they are on the same graph, each set to 0.1:
- blackpoint works on the input: it says which input value should become black. Everything is stretched so that 0.1 lands on 0, and values below it go negative (the black clamp stops them at 0).
- lift works on the output: it says where black goes. The line rotates around white: 0 goes to 0.1, 1 stays 1, and above 1 the slope is below 1 (2 becomes 1.9). Contrast goes down.
- offset (add) shifts everything by the same amount: 0 goes to 0.1 and 1 goes to 1.1. Contrast in stops changes, in linear it doesn't.
- Toe is a separate node, and I measured it: a parabola that lifts black to the lift value and meets the original curve, with the same slope, at twice the lift.
Toe: x < 2 × lift: out = lift + x² / (4 × lift) x ≥ 2 × lift: out = x
Everything above twice the lift is untouched, so a Toe of 0.05 changes only the values below 0.1 and leaves the midtones and the highlights exactly as they were. It works per channel, negative values become 0 and a negative lift does nothing. That's why Toe is the right tool to milk the blacks of a CG render or a plate without changing its contrast, while lift is a global grade.
Tony Lyons took the same idea further in his BlacksMatch gizmo (Nuke Survival Toolkit), built to match the black level of an element to the plate. His two rules: nothing may fall below the black level, and the mids and highlights should be touched as little as possible. I measured it, and its curve is:
out = x + black × (1 − x / (reach × black))1/rolloff below reach × black, untouched above
And here is the nice surprise: with reach 2 and rolloff 0.5 this is exactly Nuke's Toe (I compared them in Nuke: identical to the last decimal). Toe is one member of a family, and BlacksMatch lets you choose the member: Tony's defaults, reach 4 and rolloff 0.35, give a longer and softer toe. Its other big idea is that the black can be an image: sample the darkest values around the element, so the black level follows the haze and the light of the plate across the frame. And his advice: don't add colour corrections after the black match, or the blacks move again.
The pairs are meant to be used together: blackpoint and whitepoint are measured on the image you have, lift and gain are the values you want. Pick the darkest and brightest pixel of plate A into blackpoint and whitepoint, the same pixels of plate B into lift and gain, and the Grade maps one range onto the other: the classic black and white level match.
09. The gamma knob is inverted
In maths, and on a monitor, "gamma 2.2" means x2.2: a higher gamma makes the midtones darker. Nuke's gamma knob, in the Grade and in the ColorCorrect, does the opposite:
Nuke gamma g: out = x1/g gamma 2 = square root gamma 0.5 = square
So in Nuke "more gamma" means "brighter midtones", which is what an artist expects from a slider. But it matters as soon as numbers travel: the power of a CDL is 1/gamma (a Grade gamma of 1.25 is a CDL power of 0.8), a formula written as pow(x, gamma) in an Expression does the opposite of the knob, and encoding linear to a "gamma 2.2" display means a Grade gamma of 2.2, not 1/2.2.
10. Clamp and SoftClip
Two ways to stop values from going above 1 (or below 0), with very different results.
Clamp is a hard cut: below minimum a value becomes the minimum, above maximum it becomes the maximum (or, with the "clamp to" checkboxes, any other value you choose). By default it clamps between 0 and 1 on all channels, alpha included. It's exact and predictable, and it destroys information: a highlight of 1.5 and one of 4 become the same flat 1, and the grain below 0 disappears. The black clamp and white clamp of the Grade do the same thing inside the Grade, before the gamma.
SoftClip compresses instead of cutting. Measured in Nuke 17, with m = softclip_min:
x ≤ m: out = x x > m: out = m + (1 − m) × (x − m) / (x − m + 1 − m)
The curve leaves the straight line at m with the same slope, so there is no visible kink, and it approaches 1 forever: a value of 4 still ends up brighter than a value of 2. With m = 0 the formula becomes exactly x / (1 + x), the classic Reinhard tone curve. A few surprises from the measurements: with the default values (min 0.8, max 1) and conversion set to none, the node does nothing at all; the curve kicks in only when softclip_max is above 1, and its value doesn't change the curve (1.01, 1.5 and 3 gave identical results); and in this mode the reverse checkbox gave the same result as forward, so don't count on it to undo the compression. The other conversion modes work on the colour as a whole rather than channel by channel, to limit hue shifts.
Use Clamp where the range is a rule: mattes, alphas, data passes, before a keyer or anything that breaks with values above 1. Use SoftClip where the image has to look good: hot CG highlights, fire, glows, a comp going to an 8-bit or display-referred delivery, where you want 1.5 and 4 to stay different.
11. Grade and CDL
The ASC CDL is the standard grade passed between set, DI and VFX: per channel, then a saturation.
out = (in × slope + offset)power then saturation with Rec.709 luma
It's the same shape as a Grade: a line and a power. Every Grade without clamps corresponds to slope = A, offset = B, power = 1/gamma, but only exactly between 0 and 1: above 1 the Grade's gamma becomes a line, while the CDL power keeps curving. With gamma 1 they're identical everywhere. In Nuke the CDL node is OCIOCDLTransform; it can clamp negative values when a power is used, as the author of the "ASC-CDL no clamp" Blink tool on Nukepedia points out.
Grade Lab
This Lab runs the same steps as the Grade source code, including the clamps and the gamma above 1. The curve goes from 0 to 1.5: watch it turn into a straight line above 1 when you change the gamma. Drag on the image to compare.
ColorCorrect, Saturation, Hue
The other colour nodes get their own pages:
The ColorCorrect node Saturation, Hue & HSV
Nodes to paste in Nuke
Click a box, copy it (Ctrl+C / Cmd+C) and paste it in Nuke's Node Graph (Ctrl+V / Cmd+V).
The Grade rebuilt in an Expression. The same steps as the source code (line, black clamp, gamma with the straight line above 1), with Grade-like knobs in the User tab: perfect to understand it, or to change one step.
CDL. An OCIOCDLTransform with slope, offset, power and saturation, ready to receive the numbers from set or from the Grade Lab.
Black toe. The BlacksMatch curve of chapter 08 in one Expression: the black level in the User tab, reach and rolloff. With reach 2 and rolloff 0.5 it is exactly Nuke's Toe.
The math series
The other pages about maths in Nuke:
Merge & Premultiplication
ColorCorrect
Saturation, Hue & HSV
Keying
Despill
Matching Colour & Light
Edge Extend
Lin, Log & Gamma
Filtering & Resampling
Edge Detection
Vectors
Trigonometry
Expressions & Curves
And if you like this, have a look at the Matrices in Nuke series.