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

Material

Color by position + a sharp specular highlight.

So far everything was one flat color. A material decides the color at each hit point. Here we pull it from position with a cos, which cycles through colors as the height changes.

And we add the touch that sells realism: the specular, that bright dot where light bounces straight into the eye. It is the normal against the half-vector between light and camera, raised to a power: pow(..., 32.0). More power, smaller and harder highlight.

SpecularThe direct reflection of the light. When normal, light and view nearly align, the spot ignites. The power (32.0) sets the size: high = metallic pinpoint, low = broad matte sheen.
PruebaRaise the 32.0 in pow(..., 32.0) and the highlight tightens to a point. The second slider changes the color rhythm (pos.y*2.0).

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)));
}
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 diff=max(dot(n,lightDir),0.0);
  vec3 mat=0.5+0.5*cos(pos.y*2.0+vec3(0.0,2.0,4.0));
  vec3 hv=normalize(lightDir+normalize(ro-pos));
  float spec=pow(max(dot(n,hv),0.0), 32.0);
  vec3 lit=mat*(diff*0.85+0.15)+vec3(1.0)*spec*0.7;
  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