Rim light
A glow along the silhouette that follows the camera.
- 01GoalLight
- 02TechniqueFresnel
- 03ExecutionRim light
Parameters
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.
dotDotProductdotdotdot NormalView01 Facing ratio
1.0 - xOneMinus1.0 - x1.0 - x1.0 - x In02 Invert
powPowpowpowpow In03 Narrow the band
*Multiply*** In04 Tint
How it works
Placeholder text for the walking skeleton. Israel replaces it.
A dot productMultiplies two vectors component by component and adds the results. For unit vectors it is the cosine of the angle between them, from 1 (same direction) to -1 (opposite). Dot product Multiplies two vectors component by component and adds the results. For unit vectors it is the cosine of the angle between them, from 1 (same direction) to -1 (opposite). Normal A unit vector perpendicular to a surface, pointing the way it faces. Lighting and Fresnel effects compare it with other directions.dot(a, b) = |a| |b| cos θ
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
#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);}// 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
- PBR: Theory (opens in a new tab)Article
- Schlick's approximation (opens in a new tab)Reference
- dot (opens in a new tab)Reference
- pow (opens in a new tab)Reference