learn GLSL from zero
ToolsCrea con shadersShader MakerCrea shaders gratis en líneaToolkitObras de shaders listas para tu webSketcher LabPinta el hero de tu landing con shaders3D StudioEl editor Three.js en tu navegador
GamesJuega con shadersOnomatoShaderEscribe una palabra y explótalaThe WatcherUn ojo que te sigueAetherVidrio líquido con un ojo adentroBloomcoreUna palabra hace crecer un jardín de luzPyrokineAvienta fuego a la paredVoltaicLa pirámide lanza rayosSurfSurfea un océano de shader

Lección 10 de 10 · Ray marching en GLSL

Escena completa

Todo junto + la luz orbita con el tiempo. FINALE.

El destripado se arma. Dos esferas fundidas con smin sobre un piso, normales, luz difusa, sombra suave, material por posición y especular. Y ahora se mueve: la luz orbita la escena usando T (el tiempo) dentro de un cos y un sin, y la sombra gira con ella.

Esto es un motor de render completo en una función. La misma receta que hace fluidos, planetas, criaturas y fractales. Ya sabes leerla entera.

T = tiempoT son los segundos corriendo. Metido en cos(T*ls) y sin(T*ls) dibuja un círculo: la luz da vueltas. ls es la velocidad. Cualquier número del shader puede beber de T y cobrar vida.
PruebaSube ls=0.6 a 1.5 y la luz orbita más rápido. El segundo slider afila el especular (40.0).

El código

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