Creating a Custom Dynamic Lighting Material in the Source Engine 2013
Learn how to build, compile, and test a custom dynamic lighting material in the Source Engine 2013. Follow a concise step‑by‑step guide that covers shader files, material dictionary settings, hot‑reload, and in‑game verification.
18 Sept 2025, 19:47 UTC

Desired Outcome
Create a material that uses custom vertex and fragment shaders to produce dynamic lighting on a surface at runtime. The material should compile without errors, hot‑reload when files change, and show correct lighting when a light source is placed nearby.
Prerequisites
- Valve Source Engine 2013 SDK installed and a game build that supports custom materials (e.g., Half‑Life: Alyx or a custom mod).
- Basic understanding of GLSL and the Source Engine material system.
- Text editor capable of saving UTF‑8 files.
- Access to the game’s
materialsdirectory and the ability to open the in‑game console.
Step‑by‑Step Procedure
- Write the Vertex Shader (
dynamic_light.vsh)// dynamic_light.vsh #version 120 // Input vertex attributes in vec3 in_Position; in vec3 in_Normal; in vec2 in_TexCoord; // Uniforms uniform mat4 modelview; uniform mat4 projection; // Outputs to fragment shader out vec3 v_normal; out vec2 v_texcoord; void main() { vec4 worldPos = modelview * vec4(in_Position, 1.0); gl_Position = projection * worldPos; v_normal = normalize((modelview * vec4(in_Normal, 0.0)).xyz); v_texcoord = in_TexCoord; }Save this file in the
materialsfolder. - Create the Fragment Shader (
dynamic_light.fsh)// dynamic_light.fsh #version 120 in vec3 v_normal; in vec2 v_texcoord; uniform sampler2D diffuse; uniform vec3 lightPos; out vec4 fragColor; void main() { // Basic Lambertian diffuse vec3 lightDir = normalize(lightPos - gl_FragCoord.xyz); float diff = max(dot(v_normal, lightDir), 0.0); vec4 base = texture(diffuse, v_texcoord); fragColor = vec4(base.rgb * diff, base.a); }Place this file alongside the vertex shader.
- Define the Material Dictionary (
dynamic_light.vmt)// dynamic_light.vmt Shader "VertexColor" { // Reference the custom shaders VertexShader "dynamic_light.vsh" FragmentShader "dynamic_light.fsh" // Enable dynamic lighting features Lightmap "Dynamic" VertexColor "True" Diffuse "textures/dynamic_light_diffuse" }Ensure the texture referenced exists or replace it with a placeholder.
- Place All Files in the Correct Directory
Copy
dynamic_light.vsh,dynamic_light.fsh, anddynamic_light.vmtto<game>/materials/. If using a mod, keep them in the mod’smaterialssubfolder. - Hot‑Reload the Material
Launch the game, open the console (usually
~), and run:r_materials_updateThis forces the engine to re‑compile all materials. If the files are valid, you’ll see a message like:
Material compiled successfully: dynamic_light - Verify in‑Game
Place a point light or a dynamic light source near a surface that uses
dynamic_light.vmt. Observe the shading; the surface should brighten toward the light. If the material fails to compile, the console will show:Shader compile error: dynamic_light.fsh: line 12: undeclared identifier ‘lightPos’Use
mat_dumpto confirm the material is active:mat_dump dynamic_light - Optional: Full Restart for Persistent Issues
If hot‑reload leaves stale binaries or the shader still fails, close the game and restart. This clears the shader cache and forces a fresh compilation.
Expected Checks
- Console output contains
Material compiled successfullyafterr_materials_update. - No
Shader compile errorlines appear. - The new material appears in
mat_dumpoutput with the correct shader paths. - Dynamic lighting behaves as expected when a light source is moved.
Recovery Options
- If compilation fails, revert to a previously working
.vmtor delete the new shader files to restore the default material. - Check the shader syntax: ensure all inputs are declared and match the Source Engine’s expected attribute names.
- Verify that the texture path in the
Diffuseentry exists; missing textures can cause fallback to a black material. - After making corrections, run
r_materials_updateagain or perform a full restart if the problem persists.
Limitations & Practical Verification
- Source Engine 2013 supports only GLSL 1.20; using newer extensions (e.g.,
#extension GL_ARB_gpu_shader5 : enable) will be ignored or cause errors. - Dynamic lighting requires the
Lightmap "Dynamic"entry; omitting it disables per‑pixel light calculations. - On older GPUs, high‑precision qualifiers (
highp) may be unsupported; stick tomediumporlowpwhere possible. - Always test on the target hardware to confirm visual fidelity and performance impact.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.