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 10 of 10 · Ray marching in GLSL

Full scene

Everything together + the light orbits with time. FINALE.

The breakdown comes together. Two spheres fused with smin over a floor, normals, diffuse light, soft shadow, material by position and specular. And now it moves: the light orbits the scene using T (time) inside a cos and a sin, and the shadow swings with it.

This is a full render engine in one function. The same recipe behind fluids, planets, creatures and fractals. You can now read the whole thing.

T = timeT is seconds ticking. Fed into cos(T*ls) and sin(T*ls) it traces a circle: the light goes around. ls is the speed. Any number in the shader can drink from T and come alive.
PruebaPush ls=0.6 to 1.5 and the light orbits faster. The second slider sharpens the specular (40.0).

The code

precision highp float;
float smin(float a, float b, float k){
  float h=clamp(0.5+0.5*(b-a)/k, 0.0, 1.0);
  return mix(b,a,h)-k*h*(1.0-h);
}
float map(vec3 p){
  float s1=length(p-vec3(-0.6,0.0,0.0))-0.8;
  float s2=length(p-vec3(0.6,0.0,0.0))-0.8;
  float blob=smin(s1, s2, 0.6);
  float flo=p.y+1.1;
  return min(blob, 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, 10.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.2,3.2);
  vec3 rd=normalize(vec3(uv,-1.6));
  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);
  float ls=0.6;
  vec3 lightDir=normalize(vec3(cos(T*ls)*1.4, 1.0, sin(T*ls)*1.4));
  float sh=shad(pos+n*0.02, lightDir);
  float diff=max(dot(n,lightDir),0.0)*sh;
  vec3 mat=0.5+0.5*cos(pos.x*1.5+vec3(0.0,2.0,4.0)+T*0.2);
  vec3 hv=normalize(lightDir+normalize(ro-pos));
  float spec=pow(max(dot(n,hv),0.0), 40.0)*sh;
  vec3 lit=mat*(diff*0.9+0.15)+vec3(1.0)*spec*0.8;
  vec3 sky=mix(vec3(0.04,0.05,0.10), vec3(0.14,0.20,0.40), uv.y*0.5+0.5);
  float fog=1.0-exp(-t*0.06);
  vec3 col=mix(lit, sky, fog);
  col=mix(sky, col, hit);
  col=pow(col, vec3(0.85));
  gl_FragColor=vec4(col,1.0);
}
close