Skip to content
Light · FresnelBeginner

Rim light

A glow along the silhouette that follows the camera.

NormalViewSurface
  1. 01GoalLight
  2. 02TechniqueFresnel
  3. 03ExecutionRim light
Test mesh

Parameters

3.0
#59bfff
1.50

Copy for your platform

  • GLSLCopy
  • GodotCopy

Node chain

Built step by step, in GLSL style, with its node names.

Built step by step, in Godot style, with its node names.

No Unreal Engine 5 version of this card yet, so the nodes show the GLSL names.

No Unity version of this card yet, so the nodes show the GLSL names.

No Blender version of this card yet, so the nodes show the GLSL names.

  1. dotDotProductdotdotdot
    Normal
    View

    01 Facing ratio

  2. 1.0 - xOneMinus1.0 - x1.0 - x1.0 - x

    02 Invert

  3. powPowpowpowpow

    03 Narrow the band

  4. *Multiply***

    04 Tint

How it works

Placeholder text for the walking skeleton. Israel replaces it.

A dot product of the surface normal and the direction toward the camera is 1 where the surface faces you and 0 at the silhouette. Inverting it gives a value that is bright only at the edges. Raising that value to a power narrows the band, and multiplying by a color and an intensity tints it.

Make it in GLSL

Placeholder text for the walking skeleton. Israel replaces it.

Paste the function into a fragment shader that passes in the world-space normal and the direction toward the camera.

Make it in Godot

VisualShader also has a built-in Fresnel node that does the first three steps.

Placeholder text for the walking skeleton. Israel replaces it.

Paste the shader into a new ShaderMaterial on a MeshInstance3D. It is unshaded and writes the rim to ALBEDO, so the mesh shows only the rim color, with no lighting.

No Unreal Engine 5 tutorial for this card yet.

No Unity tutorial for this card yet.

No Blender tutorial for this card yet.

Code

rim-light.fragGLSL
#version 300 es// Fresnel → Rim light, web GLSL (GLSL ES 3.00). From Creative Center, MIT license.precision highp float;uniform float u_time;uniform float p_power; // Power: 3.0 (0.5 to 8.0)uniform vec3 p_color; // Color: vec3(0.35, 0.75, 1.0)uniform float p_intensity; // Intensity: 1.5 (0.0 to 4.0)in vec2 v_uv;in vec3 v_position;in vec3 v_normal;in vec3 v_view;out vec4 fragColor;struct Inputs {	vec2 uv;	vec3 position;	vec3 normal;	vec3 view;	float time;};// i.view points from the surface toward the camera, so dot(normal, view) is 1 where the surface faces the camera// and 0 at the silhouette. max() drops back faces, which would otherwise go above 1 after the inversion.vec4 effect(Inputs i) {	float facing = max(dot(i.normal, i.view), 0.0);	float edge = 1.0 - facing;	float rim = pow(edge, p_power);	vec3 glow = rim * p_color * p_intensity;	return vec4(glow, 1.0);}void main() {	Inputs i;	i.uv = v_uv;	i.position = v_position;	// Interpolation shortens the vectors between vertices	i.normal = normalize(v_normal);	i.view = normalize(v_view);	i.time = u_time;	fragColor = effect(i);}
rim-light.gdshaderGodot shader
// Fresnel → Rim light, Godot 4. From Creative Center, MIT license.shader_type spatial;// Unshaded shows the effect's color as is, like the site's preview; scene lights don't touch itrender_mode unshaded;uniform float power : hint_range(0.5, 8.0, 0.1) = 3.0;uniform vec3 color : source_color = vec3(0.35, 0.75, 1.0);uniform float intensity : hint_range(0.0, 4.0, 0.05) = 1.5;struct Inputs {	vec2 uv;	vec3 position;	vec3 normal;	vec3 view;	float time;};// i.view points from the surface toward the camera, so dot(normal, view) is 1 where the surface faces the camera// and 0 at the silhouette. max() drops back faces, which would otherwise go above 1 after the inversion.vec4 effect(Inputs i) {	float facing = max(dot(i.normal, i.view), 0.0);	float edge = 1.0 - facing;	float rim = pow(edge, power);	vec3 glow = rim * color * intensity;	return vec4(glow, 1.0);}void fragment() {	Inputs i;	// Godot's UV starts at the top-left; the effect expects GLSL's bottom-left	i.uv = vec2(UV.x, 1.0 - UV.y);	// VERTEX, NORMAL and VIEW are in view space here; the effect works in world space	i.position = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;	i.normal = normalize((INV_VIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);	i.view = normalize((INV_VIEW_MATRIX * vec4(VIEW, 0.0)).xyz);	i.time = TIME;	ALBEDO = effect(i).rgb;	// For a see-through effect, also write ALPHA = effect(i).a; (Godot then sorts it as transparent)}

No Unreal Engine 5 code for this card yet.

No Unity code for this card yet.

No Blender code for this card yet.

Further reading