learn GLSL from zero
ToolsCreate with shadersShader MakerFree online shader builderToolkitShader works ready for your siteSketcher LabPaint your landing’s hero with shaders3D StudioThe Three.js editor in your browser
GamesPlay with shadersOnomatoShaderType a word, blow it upThe WatcherAn eye that follows youAetherLiquid glass with an eye insideBloomcoreA word grows a garden of lightPyrokineThrow fire at the wallVoltaicThe pyramid throws lightningSurfSurf a shader ocean

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.
PruebaLower 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);
}
close