| """Hero video: THE DRAGON'S HALL - one scene, every gradient, live. |
| |
| A single continuous shot in the style of the old 3D benchmarks: a dark |
| marble hall, a pedestal with gold trim and a glass slab, glowing runes on |
| the back wall, warm braziers, atmospheric fog, and a faintly luminous |
| crystal dragon. From a flat gray start, Adam through render() recovers |
| simultaneously: |
| - the 128x128 per-texel floor albedo (49,152 unknowns), |
| - the gold trim's conductor tint and the pedestal's plastic albedo, |
| - the dragon's emission glow and the braziers' warm emission, |
| - a 16x64 per-texel emissive rune band (3,072 unknowns), |
| - the participating medium (sigma_a, sigma_s) filling the hall, |
| and, once the appearance has settled, geometry_grad() alone slides the |
| displaced dragon (5,205 vertices) onto its pedestal by its shadow and |
| silhouette. The optimization runs at spp 8; every displayed frame is |
| re-rendered clean from the current parameters. Layout: one widescreen |
| live view with a small fixed TARGET inset and a progress bar. |
| |
| Dragon mesh: the Stanford dragon, courtesy of the Stanford Computer |
| Graphics Laboratory (dragon_vrip_res4). |
| |
| Output: media/inverse.mp4 (1024x620, 30 fps) + media/inverse.gif. |
| """ |
| import math |
| import os |
| import sys |
|
|
| ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| sys.path.insert(0, ROOT) |
|
|
| import imageio |
| import numpy as np |
| import torch |
| from PIL import Image, ImageDraw, ImageFont |
|
|
| import load_local |
|
|
| ptd = load_local.load() |
|
|
| DEV = "cuda" |
| RW, RH = 1024, 576 |
| HEADER = 44 |
| CW, CH = RW, RH + HEADER |
| FPS = 30 |
| INSET_W, INSET_H = 256, 144 |
|
|
| _FONT_CANDIDATES = ( |
| "DejaVuSansMono-Bold.ttf", |
| "/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf", |
| "consolab.ttf", |
| ) |
|
|
|
|
| def _font(size): |
| """First available bold monospace face, else PIL's default.""" |
| for path in _FONT_CANDIDATES: |
| try: |
| return ImageFont.truetype(path, size) |
| except OSError: |
| continue |
| return ImageFont.load_default() |
|
|
|
|
| FONT, FONT_BIG, FONT_MID, FONT_SM = (_font(20), _font(44), _font(25), |
| _font(16)) |
|
|
| MP4 = os.path.join(ROOT, "media", "inverse.mp4") |
| GIF = os.path.join(ROOT, "media", "inverse.gif") |
| vw = imageio.get_writer(MP4, fps=FPS, codec="libx264", quality=8, |
| pixelformat="yuv420p", macro_block_size=1) |
| gif_entries = [] |
|
|
|
|
| def emit(fr, seconds, static=False): |
| arr = np.asarray(fr, dtype=np.uint8) |
| for _ in range(max(1, round(seconds * FPS))): |
| vw.append_data(arr) |
| gif_entries.append((fr, seconds, static)) |
|
|
|
|
| def quad(a, b, c, d): |
| return [[a, b, c], [a, c, d]] |
|
|
|
|
| def add_box(verts, faces, mat, uv, lo, hi, mid): |
| x0, y0, z0 = lo |
| x1, y1, z1 = hi |
| b = len(verts) |
| verts.extend([(x0, y0, z0), (x1, y0, z0), (x1, y0, z1), (x0, y0, z1), |
| (x0, y1, z0), (x1, y1, z0), (x1, y1, z1), (x0, y1, z1)]) |
| for q in [quad(b, b + 1, b + 2, b + 3), quad(b + 4, b + 7, b + 6, b + 5), |
| quad(b, b + 4, b + 5, b + 1), quad(b + 3, b + 2, b + 6, b + 7), |
| quad(b, b + 3, b + 7, b + 4), quad(b + 1, b + 5, b + 6, b + 2)]: |
| faces.extend(q) |
| mat.extend([mid, mid]) |
| uv.extend([[(0, 0)] * 3] * 2) |
|
|
|
|
| def load_dragon(path, height=1.5, yaw_deg=205.0): |
| """Parse the ASCII PLY, center on xz, base at y=0, scale to `height`, |
| yaw around y.""" |
| verts, faces = [], [] |
| with open(path, "r", encoding="ascii") as f: |
| n_v = n_f = 0 |
| props = 0 |
| for line in f: |
| line = line.strip() |
| if line.startswith("element vertex"): |
| n_v = int(line.split()[-1]) |
| elif line.startswith("element face"): |
| n_f = int(line.split()[-1]) |
| elif line.startswith("property float") or \ |
| line.startswith("property double"): |
| props += 1 |
| elif line == "end_header": |
| break |
| for _ in range(n_v): |
| xs = f.readline().split() |
| verts.append([float(xs[0]), float(xs[1]), float(xs[2])]) |
| for _ in range(n_f): |
| xs = f.readline().split() |
| assert xs[0] == "3" |
| faces.append([int(xs[1]), int(xs[2]), int(xs[3])]) |
| v = torch.tensor(verts, dtype=torch.float32) |
| fc = torch.tensor(faces, dtype=torch.int64) |
| lo, hi = v.amin(0), v.amax(0) |
| v = v - torch.tensor([(lo[0] + hi[0]) / 2, lo[1], (lo[2] + hi[2]) / 2]) |
| v = v * (height / float(hi[1] - lo[1])) |
| a = math.radians(yaw_deg) |
| ca, sa = math.cos(a), math.sin(a) |
| x, y, z = v[:, 0].clone(), v[:, 1], v[:, 2].clone() |
| v[:, 0] = ca * x + sa * z |
| v[:, 2] = -sa * x + ca * z |
| return v, fc |
|
|
|
|
| DRAGON_V, DRAGON_F = load_dragon(os.path.join(ROOT, "media", |
| "dragon_res4.ply")) |
| print(f"dragon: {DRAGON_V.shape[0]} verts, {DRAGON_F.shape[0]} faces") |
|
|
| DRAGON_BASE_Y = 1.78 |
| DRAGON_HOME = (0.0, -1.5) |
|
|
| |
| |
| |
|
|
|
|
| def build_scene(floor_tex, gold, plast, glow, brazier, runes, sa, ss, |
| occ_t): |
| verts, faces, mat, uv = [], [], [], [] |
| b = len(verts) |
| verts.extend([(-6, 0, -6), (6, 0, -6), (6, 0, 6), (-6, 0, 6)]) |
| faces.extend(quad(b, b + 1, b + 2, b + 3)) |
| mat.extend([0, 0]) |
| uv.extend([[(0, 0), (1, 0), (1, 1)], [(0, 0), (1, 1), (0, 1)]]) |
| b = len(verts) |
| verts.extend([(-6, 0, -6), (6, 0, -6), (6, 7, -6), (-6, 7, -6)]) |
| faces.extend(quad(b, b + 1, b + 2, b + 3)) |
| mat.extend([1, 1]) |
| uv.extend([[(0, 0)] * 3] * 2) |
| b = len(verts) |
| verts.extend([(-6, 0, 6), (-6, 0, -6), (-6, 7, -6), (-6, 7, 6)]) |
| faces.extend(quad(b, b + 1, b + 2, b + 3)) |
| mat.extend([1, 1]) |
| uv.extend([[(0, 0)] * 3] * 2) |
| b = len(verts) |
| verts.extend([(6, 0, -6), (6, 0, 6), (6, 7, 6), (6, 7, -6)]) |
| faces.extend(quad(b, b + 1, b + 2, b + 3)) |
| mat.extend([1, 1]) |
| uv.extend([[(0, 0)] * 3] * 2) |
| b = len(verts) |
| verts.extend([(-6, 7, -6), (6, 7, -6), (6, 7, 6), (-6, 7, 6)]) |
| faces.extend(quad(b, b + 1, b + 2, b + 3)) |
| mat.extend([1, 1]) |
| uv.extend([[(0, 0)] * 3] * 2) |
|
|
| add_box(verts, faces, mat, uv, (-1.3, 0.0, -2.8), (1.3, 1.0, -0.2), 2) |
| add_box(verts, faces, mat, uv, (-1.45, 1.0, -2.95), (1.45, 1.25, -0.05), |
| 3) |
| add_box(verts, faces, mat, uv, (-1.05, 1.25, -2.55), (1.05, 1.55, -0.45), |
| 4) |
|
|
| b = len(verts) |
| verts.extend([(-5.98, 1.4, -2.9), (-5.98, 1.4, -2.0), |
| (-5.98, 3.2, -2.0), (-5.98, 3.2, -2.9)]) |
| faces.extend(quad(b, b + 1, b + 2, b + 3)) |
| mat.extend([6, 6]) |
| uv.extend([[(0, 0)] * 3] * 2) |
| b = len(verts) |
| verts.extend([(5.98, 1.4, -2.0), (5.98, 1.4, -2.9), |
| (5.98, 3.2, -2.9), (5.98, 3.2, -2.0)]) |
| faces.extend(quad(b, b + 1, b + 2, b + 3)) |
| mat.extend([6, 6]) |
| uv.extend([[(0, 0)] * 3] * 2) |
|
|
| b = len(verts) |
| verts.extend([(-4.5, 3.2, -5.98), (4.5, 3.2, -5.98), |
| (4.5, 4.2, -5.98), (-4.5, 4.2, -5.98)]) |
| faces.extend(quad(b, b + 1, b + 2, b + 3)) |
| mat.extend([7, 7]) |
| uv.extend([[(0, 0), (1, 0), (1, 1)], [(0, 0), (1, 1), (0, 1)]]) |
|
|
| b = len(verts) |
| verts.extend([(-1.6, 6.98, -2.9), (1.6, 6.98, -2.9), |
| (1.6, 6.98, -0.1), (-1.6, 6.98, -0.1)]) |
| faces.extend(quad(b, b + 1, b + 2, b + 3)) |
| mat.extend([8, 8]) |
| uv.extend([[(0, 0)] * 3] * 2) |
|
|
| occ0 = len(verts) |
| dx = DRAGON_HOME[0] + float(occ_t[0]) |
| dz = DRAGON_HOME[1] + float(occ_t[1]) |
| dv = DRAGON_V + torch.tensor([dx, DRAGON_BASE_Y, dz]) |
| verts = torch.cat([torch.tensor(verts, dtype=torch.float32), dv]) |
| faces = torch.cat([torch.tensor(faces, dtype=torch.int64), |
| DRAGON_F + occ0]) |
| mat.extend([5] * DRAGON_F.shape[0]) |
| uv.extend([[(0, 0)] * 3] * DRAGON_F.shape[0]) |
|
|
| scene = ptd.Scene( |
| verts, faces, mat, |
| albedo=[floor_tex, |
| torch.tensor([0.24, 0.22, 0.21], device=DEV), |
| plast, gold, |
| torch.tensor([1.0, 1.0, 1.0], device=DEV), |
| torch.tensor([1.0, 1.0, 1.0], device=DEV), |
| torch.tensor([0.55, 0.35, 0.20], device=DEV), |
| torch.tensor([0.05, 0.06, 0.09], device=DEV), |
| torch.tensor([0.8, 0.8, 0.8], device=DEV)], |
| emission=[torch.zeros(3), torch.zeros(3), torch.zeros(3), |
| torch.zeros(3), torch.zeros(3), glow, brazier, runes, |
| torch.tensor([20.0, 17.5, 14.0])], |
| material_types=[ptd.DIFFUSE, ptd.DIFFUSE, ptd.PLASTIC, |
| ptd.CONDUCTOR, ptd.DIELECTRIC, ptd.ROUGH_DIELECTRIC, |
| ptd.DIFFUSE, ptd.DIFFUSE, ptd.DIFFUSE], |
| roughness=[0.3, 0.3, 0.25, 0.12, 0.0, 0.08, 0.3, 0.3, 0.3], |
| ior=[1.5] * 9, uvs=uv, medium=(sa, ss)) |
| scene.med_sbar = 0.05 |
| return scene, occ0 |
|
|
|
|
| def marble(n=128): |
| y, x = torch.meshgrid(torch.linspace(0, 1, n), torch.linspace(0, 1, n), |
| indexing="ij") |
| v1 = torch.sin(9.0 * x + 3.5 * torch.sin(4.0 * y + 1.0)) ** 6 |
| v2 = torch.sin(7.0 * y + 3.0 * torch.sin(5.0 * x + 2.2) + 1.3) ** 8 |
| base = torch.tensor([0.055, 0.055, 0.075]) |
| teal = torch.tensor([0.10, 0.45, 0.50]) |
| goldv = torch.tensor([0.55, 0.38, 0.12]) |
| t = base + v1.unsqueeze(-1) * teal + v2.unsqueeze(-1) * goldv |
| return t.clamp(0.02, 0.98).to(DEV) |
|
|
|
|
| def rune_band(h=16, w=64): |
| torch.manual_seed(4) |
| e = torch.full((h, w, 3), 0.06) |
| e[..., 2] = 0.16 |
| for k in range(10): |
| cx = 4 + k * 6 |
| g = torch.rand(5) |
| if g[0] > 0.3: |
| e[3:13, cx:cx + 2] = 0 |
| e[3:13, cx:cx + 2, 1] = 3.2 |
| e[3:13, cx:cx + 2, 2] = 4.0 |
| if g[1] > 0.4: |
| e[3:5, cx:cx + 4, 1] = 3.2 |
| e[3:5, cx:cx + 4, 2] = 4.0 |
| if g[2] > 0.4: |
| e[11:13, cx - 2:cx + 3, 1] = 3.2 |
| e[11:13, cx - 2:cx + 3, 2] = 4.0 |
| if g[3] > 0.5: |
| e[7:9, cx:cx + 4, 1] = 3.2 |
| e[7:9, cx:cx + 4, 2] = 4.0 |
| return e.to(DEV) |
|
|
|
|
| cam = ptd.Camera(position=(0.0, 3.5, 9.6), look_at=(0.0, 1.9, -1.6), |
| vfov_deg=33.0) |
|
|
|
|
| def tonemap(img): |
| x = (img * 1.35).clamp(0, 1) ** (1 / 2.2) |
| return (x * 255).byte().cpu().numpy() |
|
|
|
|
| target_np_inset = None |
|
|
|
|
| def frame_of(live_img, label, progress): |
| fr = Image.new("RGB", (CW, CH), (10, 10, 12)) |
| fr.paste(Image.fromarray(tonemap(live_img)), (0, HEADER)) |
| d = ImageDraw.Draw(fr) |
| d.text((12, 10), label, font=FONT, fill=(120, 235, 160)) |
| |
| x0 = CW - INSET_W - 14 |
| y0 = HEADER + 12 |
| d.rectangle([x0 - 2, y0 - 2, x0 + INSET_W + 1, y0 + INSET_H + 1], |
| outline=(230, 230, 235)) |
| fr.paste(target_np_inset, (x0, y0)) |
| d.text((x0 + 6, y0 + INSET_H - 22), "TARGET", font=FONT_SM, |
| fill=(235, 235, 240)) |
| |
| d.rectangle([0, CH - 5, int(CW * progress), CH - 1], |
| fill=(90, 220, 140)) |
| return fr |
|
|
|
|
| def title_card(title, lines, seconds=2.2): |
| fr = Image.new("RGB", (CW, CH), (10, 10, 12)) |
| d = ImageDraw.Draw(fr) |
| y = CH // 2 - 40 - 20 * len(lines) |
| d.text((CW // 2, y), title, font=FONT_BIG, fill=(235, 235, 240), |
| anchor="mm") |
| for i, ln in enumerate(lines): |
| d.text((CW // 2, y + 64 + 34 * i), ln, font=FONT_MID, |
| fill=(150, 200, 165), anchor="mm") |
| emit(fr, seconds, static=True) |
|
|
|
|
| |
| tex_t = marble() |
| gold_t = torch.tensor([1.0, 0.71, 0.29], device=DEV) |
| plast_t = torch.tensor([0.55, 0.06, 0.08], device=DEV) |
| glow_t = torch.tensor([0.30, 0.55, 0.90], device=DEV) |
| brazier_t = torch.tensor([14.0, 8.0, 3.0], device=DEV) |
| runes_t = rune_band() |
| sa_t = torch.tensor([0.010, 0.014, 0.022], device=DEV) |
| ss_t = torch.tensor([0.020, 0.017, 0.012], device=DEV) |
| t_home = torch.zeros(2) |
|
|
| target_scene, _ = build_scene(tex_t, gold_t, plast_t, glow_t, brazier_t, |
| runes_t, sa_t, ss_t, t_home) |
| target_hi = ptd.render(target_scene, cam, RH, RW, spp=1024, max_bounces=6, |
| seed=9).detach() |
| target_lo = ptd.render(target_scene, cam, RH, RW, spp=8, max_bounces=6, |
| seed=3).detach() |
| target_np_inset = Image.fromarray(tonemap(target_hi)).resize( |
| (INSET_W, INSET_H), Image.LANCZOS) |
|
|
| title_card("THE DRAGON'S HALL", |
| ["pathtracer-diff: one scene, every gradient, live", |
| "floor texels - gold - plastic - glass - crystal - emission", |
| "runes - fog - and the dragon's position", |
| "all optimized through one CUDA kernel"], 3.0) |
|
|
| |
| |
| |
| glow = glow_t |
| tex = torch.full((128, 128, 3), 0.18, device=DEV, requires_grad=True) |
| gold = torch.full((3,), 0.35, device=DEV, requires_grad=True) |
| plast = torch.full((3,), 0.30, device=DEV, requires_grad=True) |
| brazier = torch.full((3,), 2.0, device=DEV, requires_grad=True) |
| runes = torch.full((16, 64, 3), 0.30, device=DEV, requires_grad=True) |
| sa = torch.full((3,), 0.002, device=DEV, requires_grad=True) |
| ss = torch.full((3,), 0.002, device=DEV, requires_grad=True) |
| occ_t = torch.tensor([-3.1, 3.4], requires_grad=True) |
|
|
| opt = torch.optim.Adam([ |
| {"params": [tex], "lr": 0.08}, |
| {"params": [gold], "lr": 0.08}, |
| {"params": [plast], "lr": 0.05}, |
| {"params": [brazier], "lr": 0.15}, |
| {"params": [runes], "lr": 0.09}, |
| ]) |
| opt_f = torch.optim.Adam([sa, ss], lr=0.0025) |
| opt_g = torch.optim.Adam([occ_t], lr=0.09) |
|
|
| |
| |
| |
| |
| |
| ITERS = 1400 |
| FOG_START = 700 |
| GEO_START = 350 |
| APP_FREEZE = 1400 |
| losses = [] |
| for it in range(ITERS): |
| scene, occ0 = build_scene(tex, gold, plast, glow, brazier, runes, sa, |
| ss, occ_t.detach()) |
| opt.zero_grad() |
| opt_f.zero_grad() |
| img = ptd.render(scene, cam, RH, RW, spp=12, max_bounces=6, seed=3) |
| loss = (img - target_lo).square().mean() |
| loss.backward() |
| if it < APP_FREEZE: |
| opt.step() |
| if it >= FOG_START: |
| opt_f.step() |
| if it >= GEO_START: |
| dldi = (2.0 / img.numel()) * (img.detach() - target_lo) |
| gv = ptd.geometry_grad(scene, cam, dldi, spp=12, |
| edge_samples=1 << 18, seed=17 + it) |
| opt_g.zero_grad() |
| g = gv[occ0:occ0 + DRAGON_V.shape[0]][:, [0, 2]].sum(dim=0).cpu() |
| gn = g.norm() |
| if gn > 0.5: |
| g = g * (0.5 / gn) |
| occ_t.grad = g |
| if float(occ_t.detach().norm()) < 0.8: |
| for grp in opt_g.param_groups: |
| grp["lr"] = 0.04 |
| opt_g.step() |
| with torch.no_grad(): |
| occ_t.clamp_(-4.0, 4.0) |
| with torch.no_grad(): |
| tex.clamp_(0.02, 0.98) |
| gold.clamp_(0.02, 1.0) |
| plast.clamp_(0.02, 0.98) |
| brazier.clamp_(0.0, 40.0) |
| runes.clamp_(0.0, 10.0) |
| sa.clamp_(1e-4, 0.5) |
| ss.clamp_(1e-4, 0.5) |
| losses.append(loss.item()) |
| if it % 8 == 0: |
| |
| |
| import time as _time |
| torch.cuda.synchronize() |
| _time.sleep(0.25) |
| if it % 4 == 0: |
| st_err = ((sa + ss).detach() - (sa_t + ss_t)).abs().max().item() |
| disp = ptd.render(scene, cam, RH, RW, spp=48, max_bounces=6, |
| seed=9).detach() |
| lab = (f"LIVE iter {it:04d} loss {loss.item():8.2e} " |
| f"fog err {st_err:6.4f} dragon off {occ_t.norm():5.2f}") |
| emit(frame_of(disp, lab, (it + 1) / ITERS), 1.0 / FPS) |
|
|
| scene, _ = build_scene(tex, gold, plast, glow, brazier, runes, |
| sa.detach(), ss.detach(), occ_t.detach()) |
| final_hi = ptd.render(scene, cam, RH, RW, spp=1024, max_bounces=6, |
| seed=9).detach() |
| st_err = ((sa + ss).detach() - (sa_t + ss_t)).abs().max().item() |
| emit(frame_of(final_hi, |
| f"LIVE iter {ITERS} loss {losses[-1]:8.2e} " |
| f"fog err {st_err:6.4f} dragon off {occ_t.norm():5.2f}" |
| f" (spp 1024)", 1.0), 3.2) |
|
|
| title_card("pathtracer-diff", |
| [f"loss {losses[0]:.1e} -> {losses[-1]:.1e} " |
| f"dragon offset 4.60 -> {occ_t.norm():.3f}", |
| "49,152 floor texels + 3,072 rune texels + materials + fog", |
| "Stanford dragon: Stanford Computer Graphics Laboratory", |
| "hf.co/kernels/phanerozoic/pathtracer-diff"], 3.0) |
|
|
| vw.close() |
|
|
| |
| GW, GH = CW * 3 // 4, CH * 3 // 4 |
| frames, durs = [], [] |
| carry = 0.0 |
| idx = 0 |
| for fr, sec, static in gif_entries: |
| if static: |
| if carry > 0 and frames: |
| durs[-1] += int(carry * 1000) |
| carry = 0.0 |
| frames.append(fr.resize((GW, GH), Image.LANCZOS)) |
| durs.append(int(sec * 1000)) |
| idx = 0 |
| else: |
| carry += sec |
| if idx % 4 == 0: |
| frames.append(fr.resize((GW, GH), Image.LANCZOS)) |
| durs.append(max(60, int(carry * 1000))) |
| carry = 0.0 |
| idx += 1 |
| frames[0].save(GIF, save_all=True, append_images=frames[1:], duration=durs, |
| loop=0, optimize=True) |
|
|
| mp4_mb = os.path.getsize(MP4) / 1e6 |
| gif_mb = os.path.getsize(GIF) / 1e6 |
| print(f"loss {losses[0]:.2e} -> {losses[-1]:.2e} in {ITERS} steps") |
| print(f"floor tex mean err {(tex.detach() - tex_t).abs().mean():.3f}; " |
| f"gold max err {(gold.detach() - gold_t).abs().max():.3f}; " |
| f"plastic max err {(plast.detach() - plast_t).abs().max():.3f}") |
| print(f"brazier max err {(brazier.detach() - brazier_t).abs().max():.2f}; " |
| f"runes mean err {(runes.detach() - runes_t).abs().mean():.3f}") |
| print(f"fog sigma_t err {st_err:.4f}; dragon offset residual " |
| f"{occ_t.detach().norm():.3f} (start 4.60)") |
| print(f"wrote {MP4} ({mp4_mb:.1f} MB) and {GIF} ({gif_mb:.1f} MB, " |
| f"{len(frames)} frames)") |
|
|