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 8 de 10 · Ray marching en GLSL

Dos formas

smin(): dos esferas que se derriten en una.

El min() normal une dos formas, pero deja una arruga dura donde se tocan. smin() (smooth minimum) las funde como gotas de mercurio: en la zona de contacto interpola en vez de cortar, y aparece un cuello suave.

Dos esferas, una a cada lado, fusionadas. Esta es la magia que hace que el raymarching se sienta orgánico, no a bloques de Lego.

smin(a, b, k)Como min, pero k controla cuánto se derriten. k chico = casi separadas; k grande = una sola masa. El cuello se calcula con una mezcla cuadrática, no hay geometría extra: dos esferas y un número.
PruebaSube el 0.6 de smin(s1, s2, 0.6) y las gotas se fusionan más. El segundo slider separa la esfera derecha (p-vec3(0.6).

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.7,0.0,0.0))-0.85;
  float s2=length(p-vec3(0.6,0.0,0.0))-0.85;
  float blob=smin(s1, s2, 0.6);
  float flo=p.y+1.2;
  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)));
}
void main(){
  vec2 uv=(gl_FragCoord.xy*2.0-R)/R.y;
  vec3 ro=vec3(0.0,0.0,3.2);
  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 lit=vec3(0.55,0.65,0.95)*(diff*0.9+0.14);
  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