Lección 8 de 10 · El Sistema Solar en GLSL
Urano
Rueda de lado. El metano se queda con el rojo.
Hasta aquí el color era el de la superficie. En Urano, el color es lo que el aire le quitó a la luz en el camino. El truco: exp(-k*path), la ley de Beer–Lambert. Cada canal pierde luz según su k y según cuánto aire cruzó.
El metano absorbe el rojo: k es 0.55 en rojo, 0.12 en verde y 0.09 en azul. path suma la entrada y la salida, y cada una es 1/cos del ángulo. En la orilla la luz cruza más aire, así que la orilla sale más cian y más oscura.
El eje de Urano está a 97.8°: axisTilt en 1.71. Cada polo pasa 42 años de día y 42 de noche. Sus anillos son angostos y oscuros; el ε está a 2 radios. Abajo: Metano, Eje y Neblina.
Una canica de agua de alberca. Rodando de lado.
exp(-k*path) = lo que queda de la luz. k rojo grande = metano. path largo en la orilla = más cian.methane en 0.0: bola de nieve. Súbelo a 3.0: azul de fondo de alberca. Lleva axisTilt a 0.0: un planeta cualquiera.El código
precision highp float;
// The exact sphere: how far along the ray it is hit, or -1 if the ray misses
float sphere(vec3 ro, vec3 rd, float r){
float b = dot(ro, rd), c = dot(ro, ro) - r*r, h = b*b - c;
return h < 0.0 ? -1.0 : -b - sqrt(h);
}
// WoGL close: knee at 0.70, sRGB, chroma 1.3
vec3 wogl(vec3 c){
float l = dot(c, vec3(0.2126, 0.7152, 0.0722)), k = max(l - 0.7, 0.0);
c *= l > 0.0 ? (l - k + k/(1.0 + k))/l : 0.0;
c = pow(clamp(c, 0.0, 1.0), vec3(0.4545));
l = dot(c, vec3(0.2126, 0.7152, 0.0722));
return clamp(mix(vec3(l), c, 1.3), 0.0, 1.0);
}
// The background: black, with a star now and then
vec3 sky(vec2 f){
float s = h2(floor(f));
return vec3(0.0002, 0.0003, 0.0006) + vec3(0.7, 0.7, 0.8)*step(0.9982, s)*h2(floor(f) + 3.1);
}
float methane = 1.0; // how much methane the light crosses
float axisTilt = 1.71; // 97.8° = 1.71 radians: it rolls on its side
float haze = 0.25;
void main(){
vec2 p = (gl_FragCoord.xy*2.0 - R)/min(R.x, R.y);
vec3 ro = vec3(0.0, 0.0, 5.0), rd = normalize(vec3(p, -2.3)); // the camera looks at the center
vec3 sun = normalize(vec3(M.x*4.0 - 3.8, M.y*3.0 - 1.0, 1.0)); // you move the sun: it is the pointer
vec3 axis = normalize(vec3(sin(axisTilt), cos(axisTilt), 0.55)); // tipped a little toward you, to see the rings
vec3 col = sky(gl_FragCoord.xy);
float tp = sphere(ro, rd, 1.0);
if (tp > 0.0){
vec3 n = normalize(ro + rd*tp);
float toSun = max(dot(n, sun), 0.0), toEye = max(dot(n, -rd), 0.0);
// The trick: light goes in and out at a slant; at the edge it crosses more air
float path = 0.5/max(toSun, 0.05) + 0.5/max(toEye, 0.05);
vec3 color = exp(-methane*vec3(0.55, 0.12, 0.09)*path); // Beer–Lambert: the red stays behind on the way
float lat = asin(dot(n, axis));
float bands = 0.95 + 0.05*sin(lat*12.0) + haze*smoothstep(0.9, 1.4, lat); // the cap facing the sun
col = vec3(0.95)*color*bands*toSun*2.8;
}
float tr = -dot(ro, axis)/dot(rd, axis);
if (tr > 0.0 && (tp < 0.0 || tr < tp)){
float r = length(ro + rd*tr);
float ring = smoothstep(0.012, 0.0, abs(r - 2.0)) + 0.5*smoothstep(0.006, 0.0, abs(r - 1.84)) + 0.4*smoothstep(0.005, 0.0, abs(r - 1.66)); // ε and two more: narrow and dark
col = mix(col, vec3(0.18, 0.2, 0.22), clamp(ring, 0.0, 1.0)*0.8);
}
gl_FragColor = vec4(wogl(col), 1.0);
}