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

Normals

Which way the surface faces. The orientation map.

To light something you first need to know which way each point of its surface faces. That is the normal: an arrow perpendicular to the shell.

We do not store it, but the SDF hands it over: measure the distance at six tiny points around the hit (a bit left, right, up, down, front, back) and the difference points outward. That is the gradient. We paint n*0.5+0.5 to see it: each axis a color.

The gradientThe normal is the direction in which the SDF grows fastest. Subtracting map(p+e)-map(p-e) on each axis measures that slope. normalize makes it unit-length. A sphere shows the full RGB rainbow because it faces every direction.
PruebaMove the 0.0 in hueShift(col, 0.0) to spin the whole normal palette. Change the radius 1.0 too.

The code

precision highp float;
float map(vec3 p){
  return length(p)-1.0;
}
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)));
}
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>12.0) break;
  }
  vec3 pos=ro+rd*t;
  vec3 n=nrm(pos);
  vec3 col=mix(vec3(0.04,0.05,0.09), n*0.5+0.5, hit);
  col=hueShift(col, 0.0);
  gl_FragColor=vec4(col,1.0);
}
close