Lesson 7 of 10 · Ray marching in GLSL
Shadow
A second ray toward the light. If it hits, shadow.
A shadow is just another march. From the lit point, you shoot a ray toward the light. If it bumps into something along the way, the light is blocked: that point is in shadow.
The pretty trick (soft shadow) tracks the thinnest the ray got near an obstacle: a near-graze gives soft penumbra, a clean pass gives full light. One extra march and the sphere casts its shadow on the floor.
Soft shadowWhile the shadow ray advances we keep
min(res, k*d/t): how close it passed an object, scaled by distance. k is hardness: high = crisp edges, low = diffuse penumbra.Lower the
8.0 in min(res, 8.0 and the shadow softens. The second slider raises the light (vec3(0.7, 0.9) and shortens the shadow.The code
precision highp float;
float map(vec3 p){
float sph=length(p)-1.0;
float flo=p.y+1.0;
return min(sph, flo);
}
vec3 nrm(vec3 p){
vec2 e=vec2(0.001,0.0);
return normalize(vec3(
map(p+e.xyy)-map(p-e.xyy),
map(p+e.yxy)-map(p-e.yxy),
map(p+e.yyx)-map(p-e.yyx)));
}
float shad(vec3 ro, vec3 rd){
float res=1.0;
float t=0.05;
for(int i=0;i<32;i++){
float d=map(ro+rd*t);
if(d<0.001) return 0.0;
res=min(res, 8.0*d/t);
t+=d;
if(t>10.0) break;
}
return clamp(res, 0.0, 1.0);
}
void main(){
vec2 uv=(gl_FragCoord.xy*2.0-R)/R.y;
vec3 ro=vec3(0.0,0.0,3.0);
vec3 rd=normalize(vec3(uv,-1.5));
float t=0.0;
float hit=0.0;
for(int i=0;i<64;i++){
vec3 pos=ro+rd*t;
float d=map(pos);
if(d<0.001){ hit=1.0; break; }
t+=d;
if(t>20.0) break;
}
vec3 pos=ro+rd*t;
vec3 n=nrm(pos);
vec3 lightDir=normalize(vec3(0.7, 0.9, 0.5));
float sh=shad(pos+n*0.02, lightDir);
float diff=max(dot(n,lightDir),0.0)*sh;
vec3 lit=vec3(0.85,0.55,0.35)*(diff*0.95+0.12);
vec3 sky=mix(vec3(0.05,0.06,0.12), vec3(0.16,0.22,0.42), uv.y*0.5+0.5);
float fog=1.0-exp(-t*0.08);
vec3 col=mix(lit, sky, fog);
col=mix(sky, col, hit);
gl_FragColor=vec4(col,1.0);
}