MILES GERMER · TECHNICAL ARTIST · CINEMATIC SYSTEMS · AI-AUGMENTED PRODUCTION
← All work

Rim Light Driven by Light Direction

A character material where the rim light takes its color from whichever spotlight the camera is facing, and an underlight that puts the arena's candles on the legs.

Two Rift Fighters characters squaring off in a candle-lit temple, orange candlelight on their legs and violet rim light along their edges
01 · Problem

Rift Fighters stages its fights in a candle-lit temple lit by four colored spotlights: green and violet from the sides, blue from above, orange from the floor. A rim light with a fixed color ignores all of it. The character carries the same edge highlight no matter which way the camera swings, and that edge stops telling you anything about where the character is standing.

I wanted the rim color to come from the lights themselves. Swing the camera toward the green light and the character's edge goes green. Swing toward the violet light and it goes violet. Between the two, it reads as the blend.

Narrated demo of the system. WATCH ON YOUTUBE →
02 · Getting the lights into the material

A material cannot ask a light actor which way it is pointing, so something has to hand it that information. I built a blueprint, BP_Spotlights_DirCol, that holds the four spotlights as actor variables. On a custom event it casts each actor to a spot light, takes the world rotation of its root component, pulls the forward vector out of that rotation, and writes the vector into a material parameter collection called MPC_Light_DirCol. It writes each light's color into the same collection on the same pass.

Eight vector parameters come out of it: four directions and four colors. Everything after this point is material math reading those eight values, so a light can be moved, re-aimed, or recolored in the level and every character picks up the change the next time the blueprint runs, with nobody opening the material.

The BP_Spotlights_DirCol event graph, four parallel branches reading four spotlight actors
BP_Spotlights_DirCol. One branch per light, each writing a direction and a color into the collection.
Detail of the blueprint showing Cast To SpotLight, Get World Rotation, Get Forward Vector, and Set Vector Parameter Value nodes The MPC_Light_DirCol parameter collection listing Spotlight1_Dir through Spotlight4_Dir
03 · Building the rim light

The base is a Fresnel node at exponent 3, scaled by a thickness parameter and sharpened by a Power node whose exponent is a sharpness parameter, then clamped and used to lerp between black and white. That is a plain white edge on the whole character, and every color that follows is masked by it.

On top of that sit four identical chains, one per light. Each chain takes that light's direction vector out of the collection, normalizes it, multiplies it by -1 so it points back toward the light rather than away from it, and takes the dot product against the camera direction vector. That dot is 1 when the camera is looking straight down the light's axis, 0 when the camera is perpendicular to it, and negative when the light is behind the camera, so a clamp to 0-1 discards the back half. A Power node with the falloff-angle parameter as its exponent sets how wide the arc is before that light starts contributing.

What comes out is a single greyscale value covering the whole character: how much this light counts right now. Multiply it by that light's color, repeat four times, add the four results together, multiply the sum by the Fresnel mask, and scale the whole thing by a dim parameter.

The blend is a consequence of the math, not a case I had to write. Standing between the green light and the violet one, both dot products land partway, both colors contribute in proportion, and the rim crossfades as the camera moves instead of snapping from one color to the next.

The blueprint captures each light's color along with its direction, and driving the rim from those captured colors was the plan. I cut it for scope and left the four rim colors as their own parameters. That turned out to be the better setup. The rim palette is a look decision and the level lighting is a lighting decision, and keeping them apart lets me push the rim green harder than the light that motivates it without touching the light.

The Light Generated Rim Light section of the character material, showing the Fresnel base and four parallel dot-product chains
The rim light section. Fresnel across the top, one dot-product chain per light below it.
Material parameter collection entries for rim light dim, thickness, sharpness, and falloff angle Material parameter collection entries for the four rim light colors
04 · The underlight

The arena floor is covered in candles, and the characters needed to look like the candles were reaching them. A second Fresnel, at exponent 2, is multiplied by the dot product of the world-space vertex normal against straight down, which gives a mask that only exists on downward-facing surfaces. That drives a lerp into the orange color parameter.

What limits it is a vertical gradient: 1 at the feet, 0 at the top of the falloff, with the curve set by a parameter. A second gradient fades the underlight back off right at the feet, standing in for the occlusion where a character's own body blocks the candles nearest to it.

The gradient is currently read from a UV channel on the character, which means it is in character space and travels with the model. On the floor, where the fight happens, it reads correctly. Off the floor it does not: a character who jumps carries the candlelight up with him. The next iteration drives the same gradient from world height instead, so the underlight comes off on the way up and returns on the way down without an animation on it or a line of gameplay code.

The Underlighting section of the character material, showing the uplit effect, the vertical gradient mask, and the foot darkening mask
Three commented blocks: the uplit effect, the vertical gradient, and the foot darkening mask.
05 · Where it lands

The rim light and the underlight are two of three commented blocks in MAT_CH_001_V001_Skin. All three add together and go into Emissive Color, on top of the character's normal shading.

The full character material graph, showing the attack target masks, rim light, and underlighting blocks feeding the output node The output nodes of MAT_CH_001_V001_Skin, with the rim light and glow chains summed into Emissive Color
06 · Credits
Character material system, light-direction blueprint, rim light and underlighting logic, scene lightingMiles Germer
Character design, character models, textures, skinningJoel Blakely
Character art implementation, FX, additional lightingAngel Grajeda
07 · AI's role

I used AI to learn how to do this in Unreal: which nodes exist, what a material parameter collection can and cannot carry, how a blueprint writes into one. I placed and wired every node in these graphs myself, and I tuned every value in them by looking at the result.

08 · Result

Four colored lights drive the rim on every character using the material, and the whole system tunes from six scalars and four colors in one collection. Re-aim a light in the level and the characters pick up the new direction. The underlight puts the candles on the legs, with the height falloff and the foot occlusion each on their own parameter.

09 · What I learned

Four lights did not need four cases. It is one chain repeated four times and summed, and every in-between angle the camera can occupy is handled by the fact that a dot product returns a number instead of a yes. When the math produces the intermediate states, there are no intermediate states left to author.

I built the underlight falloff in character space and had been calling it world space until I sat down to write this page. The equivalent fix on The Lord of the Rings: War in the North in 2010 was driven by distance to the ground, and it did behave correctly when a character left the floor. Sixteen years and two engines later I reached for the cheaper mask and did not catch the difference until I had to state it plainly.