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.Sube 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);
}