E.04 · Código abiertoOpen sourcePrisma de NewtonNewton’s prism
400–700 nm · 44 λ
Un haz de luz blanca entra a un prisma de vidrio y sale abierto en un abanico de 44 longitudes de onda.A beam of white light enters a glass prism and comes out spread into a fan of 44 wavelengths.
Escena del teaserScene from the teaser
// 01La cienciaThe science
La luz blanca es una mezcla de todos los colores. Al entrar al vidrio, cada longitud de onda se frena de manera distinta, porque el índice de refracción depende de ella, y por eso cada color se desvía en un ángulo distinto: es la dispersión.
Isaac Newton usó prismas para mostrar que los colores ya venían en la luz y no los creaba el vidrio. El arcoíris es el mismo fenómeno, con gotas de agua en lugar de un prisma.
White light is a mixture of every color. Entering glass, each wavelength slows down by a different amount, because the refractive index depends on it, so each color bends at a different angle: that is dispersion.
Isaac Newton used prisms to show that the colors were already in the light and not made by the glass. A rainbow is the same effect, with water droplets instead of a prism.
// 02Cómo se calculaHow it is computed
- Todo se calcula con raymarching en un shader WebGL2: el prisma es una función de distancia con signo de un prisma triangular.
- El haz blanco es luz volumétrica: el brillo de cada píxel depende de la distancia entre su rayo de cámara y el segmento del haz.
- El abanico son 44 haces, uno por longitud de onda entre 400 y 700 nm. Cada uno se desvía en proporción a
λ − 580 nm, una aproximación lineal de la dispersión, y toma su color de un ajuste analítico del espectro visible. - El vidrio refleja con la aproximación de Schlick de Fresnel,
F = 0,04 + 0,96·(1 − cos θ)⁵; sus aristas brillan y una película delgada le agrega iridiscencia. - Cuatro muestras por píxel, tonemapping ACES y corrección gamma.
- Everything is raymarched in a WebGL2 shader: the prism is the signed distance function of a triangular prism.
- The white beam is volumetric light: each pixel’s brightness depends on the distance between its camera ray and the beam segment.
- The fan is 44 beams, one per wavelength between 400 and 700 nm. Each one bends in proportion to
λ − 580 nm, a linear approximation of dispersion, and takes its color from an analytic fit of the visible spectrum. - The glass reflects with Schlick’s Fresnel approximation,
F = 0.04 + 0.96·(1 − cos θ)⁵; its edges glow and a thin film adds iridescence. - Four samples per pixel, ACES tonemapping and gamma.
// 03El códigoThe code
Este es el código que corre en el video, tal como está en el repositorio. Todo es función del tiempo: cualquier cuadro se puede calcular solo y sale igual.
This is the code that runs in the video, exactly as it is in the repository. Everything is a function of time: any frame can be computed on its own and comes out the same.
El abanico: un haz por longitud de onda, con su desviación y su color.The fan: one beam per wavelength, with its deviation and color.
for (int i = 0; i < 44; i++) { float lam = 400.0 + 300.0 * (float(i) + 0.5) / 44.0; float dev = (lam - 580.0) / 200.0; vec3 dir = normalize(vec3(1.0, -0.12 - dev * 0.55 * uFan, dev * 0.08)); float d = distSeg(ro, rd, Q, Q + dir * 9.0 * sat(uFan * 1.5), tr); float w = 0.0012 + 0.0009 * max(tr - 4.0, 0.0); col += espectro(lam) * (exp(-d * d / w) * 0.2 + 0.001 / (d * d * 30.0 + 0.01)) * uHaz * sat(uFan * 3.0); }
El vidrio: raymarching del prisma, Fresnel, aristas y película delgada.The glass: raymarching the prism, Fresnel, edges and thin film.
float t = 0.0; bool hit = false; for (int i = 0; i < 90; i++) { vec3 p = ro + rd * t; float d = mapPr(p); if (d < 0.0008) { hit = true; break; } t += d; if (t > 30.0) break; } if (hit) { vec3 p = ro + rd * t; vec3 n = nrm(p); float cosI = sat(dot(n, -rd)); float fres = 0.04 + 0.96 * pow(1.0 - cosI, 5.0); vec3 rf = reflect(rd, n); float studio = smoothstep(0.85, 1.0, rf.y) * 1.5 + smoothstep(0.9, 1.0, rf.x) * 0.8 + smoothstep(0.92, 1.0, -rf.z) * 0.6; vec3 e = vec3(0.012, 0, 0); float lap = mapPr(p + e.xyy) + mapPr(p - e.xyy) + mapPr(p + e.yxy) + mapPr(p - e.yxy) + mapPr(p + e.yyx) + mapPr(p - e.yyx); float edge = smoothstep(0.004, 0.03, lap); vec3 disp = pelicula(500.0 + 300.0 * rf.y, 1.5, cosI); vec3 vidrio = col * 0.7 + (vec3(studio) * fres * 1.2 + edge * vec3(0.9, 0.95, 1.0) * 1.2) * mix(vec3(1.0), disp * 1.4, 0.5); col = mix(col, vidrio, uVidrio); }
// 04RecrearloRecreate it
Con Node 20 y Chrome alcanza para sacar los cuadros de esta escena. El video completo, con la canción y todas las simulaciones, se arma con los pasos del README.
Node 20 and Chrome are enough to render the frames of this scene. The full video, with the song and every simulation, is built with the steps in the README.
# 1 · clonar e instalar · clone and install $ git clone https://github.com/4mser/bn-lectures-teaser.git $ cd bn-lectures-teaser $ npm ci # 2 · cuadros sueltos de esta escena (16:9) · frames of this scene $ node herramientas/cuadros.mjs out/prisma 2140,2190 ClaseYT # 3 · o recorrerla en Remotion Studio · or scrub it in Remotion Studio $ npm run dev