This tutorial is a reply to the help request posted here: https://www.reddit.com/r/davinciresolve/comments/1xg6vmg/how_do_you_efficiently_integrate_text_to_an/
Since it uses a few lesser-known techniques, I thought it might be useful to more people.
The principle
There are only so many ways to make independent concentric rings. You need rings of constant width, where each ring's radius depends on a base radius and on its position in the stack. The reference is usually the outermost ring.
That leaves three approaches:
- Pure 2D: a set of masks and as many copies of the media, then you rotate each copy. It's a pain to animate and keep in sync.
- 2D/3D: start with a series of
sEllipse and Extrude3D nodes, then the Ribbon3D, Replicate3D, ReplaceMaterial3D and UVMap3D combo does the rest. It's easy to follow, but Fusion handles BevelWidth inconsistently: the bevel makes the object bigger instead of cutting into its body.
- Almost pure 3D: you repurpose the standard 3D shapes, flattened tori and spheres, to build the elements. The result looks more organic.
I'll go with solution 3, which also uses fewer nodes.
The ring index
Each ring is a copy of a base torus. Take a Shape3D1: every copy gives you Shape3D1_1, Shape3D1_2, and so on. (In Fusion Settings → General, tick Use Simplified Copy Names to avoid ugly names like Shape3D1_1_1_1. Without it, the expression below would only return the last number.)
To get that index, use this Fusion expression, which grabs the number after the underscore:
string.match(self.Name, "_(%d+)$")
The rings
Take a first Shape3D and set it to Torus:
- Section 0.04, for example, and Base Subdivision 100 for a smooth circle.
- In the Transform tab, set Rotation X = 90 (or -90) to stand the ring up, untick Lock X/Y/Z, and set Scale Y to 0.1. At 0.001 the ring is as flat as it gets. Go further and it gradually turns back into a torus, which can look good too.
Copy/paste the node to get the first clone (Shape3D1_1), then enter a few expressions to compute the radius and keep the other settings linked to the model Shape3D1:
- Radius =
Shape3D1.SurfaceTorusInputs.Radius-2*string.match(self.Name, "_(%d+)$")*SurfaceTorusInputs.Section In plain terms (Fusion's 3D control names are awful): radius1 − 2 × index × section1.
- Section =
Shape3D1.SurfaceTorusInputs.Section
- Scale Y (Transform tab) =
Shape3D1.Transform3DOp.Scale.Y
The hard part is done. Now make as many copies as you like, as long as the computed radius doesn't go negative. With the default radius of 1, the limit is about Radius/(2*Section) copies. With a Section of 0.04, I stopped at 11.
The centre
For the solid centre, use a sphere whose size depends on the last ring and on the Section:
- Radius =
Shape3D1.SurfaceTorusInputs.Section+Shape3D1_11.SurfaceTorusInputs.Radius (in my case _11 is the last copy)
- Scale Z =
Shape3D1.Transform3DOp.Scale.Y
Since it's a sphere, there's no need to rotate it by 90°: the torus's Y axis is the sphere's Z axis. If you'd rather keep the same setup, rotate the sphere 90° on X and apply the thickness on Y.
Putting it all together with Replicate3D
A Replicate3D places the objects connected to its inputs on every vertex of the 3D object connected to its yellow input (Destination). We need one vertex at the centre of the rings for each shape. The simplest way is a Ribbon3D (particles work too) with:
- Lines = 1
- Subdivisions = number of shapes − 1 (11 in my comp)
- Start X and End X = 0
Connect all the shapes to the Replicate3D. They come out perfectly concentric.
Cutting the hole in the clip
Next we need to cut a hole in the clip. Subtract a circle (sEllipse, Width/Height 0.5) from a sRectangle using an sBoolean, with the rectangle on the first input and the ellipse on the one to subtract, then extrude the result with an Extrude3D. Merge that Extrude3D with the Replicate3D, and adjust the Extrude3D's Scale X (4.134 here) until the hole lines up with the rings. I haven't worked out the exact relationship between sizes in Shape space and sizes in 3D space yet (I do know it for 2D/3D).
For a 16:9 composition, the rectangle is Width = 1, Height = 9/16 (0.5625).
EDIT: I looked for a more mathematical way to set the Extrude3D’s Scale so that the hole in the rectangle matches the outer circle of the rings exactly. That outer circle has a radius of 1 + 0.04 = 1.04 (torus Radius + Section), and the sEllipse has a radius of 0.25 (half its Width), so the ratio is 1.04 / 0.25 = 4.16. My by-eye value wasn’t far off. The Scale expression is:
(Shape3D1.SurfaceTorusInputs.Radius+Shape3D1.SurfaceTorusInputs.Section)/(sEllipse2.Width/2)
Remember that an sEllipse’s Width is a diameter, while a 3D radius is a radius.
Materials and UVs
Now add a ReplaceMaterial3D, with the clip as the material and the 3D composition as the scene. The clip then gets applied as a texture to every 3D shape. That's not what we want, and this is where the UVMap3D works its magic: it maps the texture across all the shapes as if they were a single object.
Given the orientation of the objects, set the Z axis in Planar mode and click Fit. If the scene changes shape over time, do the Fit on the reference frame.
Then tell the UVMap3D when the clip should look neat and tidy, since that's the whole point of the animation. I picked frame 50: tick Lock UV on Animated Object and enter 50 in RefTime.
Animation
Open the Jitter tab of the Replicate3D and change the Rotation Jitter Z: the rings spin with their texture. Pretty cool, isn't it? Since the Jitter can be keyframed (or driven by animation curves or any other modifier, but let's keep it simple), set the keyframes like this:
0 = 0, 10 = 0, 50 = 720 (2 × 360, so two full turns), 60 = 720, 100 = 0
then smooth the motion with easing in the Spline Editor.
Text
The text is easy: copy the ReplaceMaterial3D and the UVMap3D, connect a Text+ in place of the clip, and set the UVMap3D's RefTime to 0. Merge the outputs of the two UVMap3D nodes.
Finishing up
Add a DirectionalLight with Rotation X = 15 and Y = -5 (the Translation does nothing). When I do 3D motion graphics, I usually add a Camera3D set to Orthographic projection, with the Viewing Volume Size at 1.5364 to fill the frame.
Last step: connect the Renderer3D, enable Lighting and Shadows, and enjoy the result. :)
Alternative: the sEllipse + Extrude3D version
This version is easier to understand, but it forces you to work in 3D (you'll see why).
The outermost ring. Give it a Width of 0.5 and use the expression Width for Height, so the ellipse is always a circle. Choose a BorderWidth (I used 0.015 in the final comp). Connect it to an Extrude3D and use an expression to make Bevel Height equal to Bevel Width. That gives you the first bevelled 3D ring.
The other rings. Each ring's Width is the starting width minus n × 2 × the ring thickness, which in Fusion is sEllipse1.Width-2*BorderWidth*n.
The bevel trap. Counter-intuitively, adding a bevel to an Extrude3D makes the shape bigger by the same amount. You have two options: shrink the base shape, or account for the bevel in your calculations. I went with the second one, so the formula becomes:
sEllipse1.Width-2*(BorderWidth+2*Extrude3D1.BevelWidth)*n
Automating n. Editing this formula 15 or 20 times would be tedious, so we use the same trick. When you copy a node, each copy is named sEllipse1_1, sEllipse1_2, and so on, so n is the number after the underscore. For sEllipse1_1:
- Border Width =
sEllipse1.BorderWidth
- Width =
sEllipse1.Width-2*(BorderWidth+2*Extrude3D1.BevelWidth)*string.match(self.Name, "_(%d+)$")
- Height =
Width
Connect a copy of Extrude3D1 to it. The biggest part is done: copy the sEllipse1_1 / Extrude3D1_1 pair as many times as you need and the concentric circles build themselves. On the last ring, tick Solid to fill the centre.
The rest of the media. Make an instanced copy of sEllipse1 (Ctrl+C, then Ctrl+Shift+V), then de-instance Width and the Solid checkbox. For Width, enter sEllipse1.Width+4*Extrude3D1.BevelWidth and tick Solid. Subtract it from a sRectangle (Width 1, Height 9/16) using an sBoolean, then connect the result to an instanced copy of Extrude3D1. You now have a rectangle with a hole in the middle.
Joining and animating. From here it's the same as above: a Ribbon3D (Lines = 1, Start X = End X = 0, Subdivisions = number of shapes − 1) as the Replicate3D destination, the ReplaceMaterial3D with the clip, the UVMap3D (Z axis, Planar, Fit, Lock UV, RefTime), the Jitter keyframes, the text, the light and the Renderer3D.