We still have two color channels for lights. Green is for the fill light, which is a secondary light that doesn’t contribute as much as the key light. However, it can provide more color to add depth, or simulate something in the environment like a bonfire, a nearby colored lamp, or bounce light.
Add a directional light to the scene, and create a duplicate in the ToonLightDataView viewport. Connect them via a RemoteTransform
, and make the Viewport’s light color pure green (0, 255, 0)
.
We add a second ramp for the fill light and a new uniform for color.
uniform sampler2D fill_light_ramp : hint_black; uniform vec4 fill_light_color : hint_color;
Sampling from this ramp is the same as the key light, but it uses the diffuse’s green channel to sample from it instead of the red. Multiply that result with a color, and blend it back into the final color.
Since the fill light adds brightness to the regular color, we add the fill light on top of the final color.
float fill_light_value = texture(fill_light_ramp, vec2(diffuse.g, 0)).r; out_color += fill_light_value * fill_light_color.rgb;
The gradient for the fill light can be softer to give it a more diffuse feel. The color of the fill light should be darker, more subtle, and subdued. It’s there to provide volume, not to overshadow the original color.
Add a new DirectionalLight
, and a duplicate proxy in the Viewport, connected with a RemoteTransform
. This time the light should be a blue color (0, 0, 255)
.
The kick light’s purpose is to provide a bright highlight along the edge of the objects to help differentiate them from the background and their outlines. It should be pointing towards the back of the subject peeking in from the edge.
We add a new ramp and a new color, like the fill light.
uniform sampler2D kick_light_ramp : hint_black; uniform vec4 kick_light_color : hint_color;
The code for the kick light is the same as the fill light; we add its result on top of the ongoing color so it can be as bright as needed.
float kick_light_value = texture(kick_light_ramp, vec2(diffuse.b, 0)).r; out_color += kick_light_value * kick_light_color.rgb;
shader_type spatial; render_mode unshaded; //Data textures uniform sampler2D light_data : hint_black; uniform sampler2D specular_data : hint_black; uniform sampler2D key_light_ramp : hint_black; uniform sampler2D fill_light_ramp : hint_black; uniform sampler2D kick_light_ramp : hint_black; //Light colors uniform vec4 key_light_color : hint_color; uniform vec4 shadow_color : hint_color; uniform vec4 fill_light_color : hint_color; uniform vec4 kick_light_color : hint_color; void fragment() { //Data vec3 diffuse = texture(light_data, SCREEN_UV).rgb; //Key light float key_light_value = texture(key_light_ramp, vec2(diffuse.r, 0)).r; vec3 out_color = key_light_value * key_light_color.rgb; out_color = max(out_color, shadow_color.rgb); //Fill light float fill_light_value = texture(fill_light_ramp, vec2(diffuse.g, 0)).r; out_color += fill_light_value * fill_light_color.rgb; //Kick light float kick_light_value = texture(kick_light_ramp, vec2(diffuse.b, 0)).r; out_color += kick_light_value * kick_light_color.rgb; ALBEDO = out_color; }