#!/usr/bin/gawk -f # reinhard@finalmedia.de # Mit mathematischen Konstanten modifizierte Version # Public Domain BEGIN { # --- MATHEMATISCHEN KONSTANTEN --- PHI = 1.61803398874989484820; # Goldener Schnitt E = 2.71828182845904523536; # Eulersche Zahl PI = 3.14159265358979323846; # Kreiszahl FEIG = 4.66920160910299067185; # Feigenbaum-Konstante (Delta) GAUSS = 5.12461179749811331241; # Gauß-Konstante (skaliert) TAU = 6.28318530717958647692; # 2 * Pi ESQ = 7.38905609893065022723; # e^2 if (!width) width=ENVIRON["SCREEN_WIDTH"]; if (!height) height=ENVIRON["SCREEN_HEIGHT"]; if (!width) width = 1920; if (!height) height = 1080; # Auflösung W = width; H = height; # Kamera-Setup (Modifiziert durch PHI und E) camX = 0; camY = 0; camZ = -E; # Kugel-Setup (Radius durch PHI bestimmt) spX = 0; spY = -0.2; spZ = 1.2; spR = PHI / 2.0; spR2 = spR * spR; # Lichtquelle (Position beeinflusst durch FEIG und GAUSS) lX = 1.5; lY = 2.0; lZ = -1.0; # Wände des Raums leftWallX = -1.8; rightWallX = 1.8; floorY = -1.0; ceilingY = 1.0; backWallZ = 3.0; frontWallZ = -5.0; # Strahlverfolgung für jedes Pixel for (y = 0; y < H; y++) { for (x = 0; x < W; x++) { # Normalisierte Bildschirmkoordinaten fx = (x / W * 2.0 - 1.0) * (W / H); fy = (1.0 - y / H * 2.0); # Strahl-Richtung (Normalisiert) dx = fx; dy = fy; dz = 1.5; len = sqrt(dx*dx + dy*dy + dz*dz); dx /= len; dy /= len; dz /= len; # Primärstrahl schießen render_ray(camX, camY, camZ, dx, dy, dz, x, y); } } } function render_ray(ox, oy, oz, dx, dy, dz, screenX, screenY, a, b, c, disc, t_sp, hit, t, hx, hy, hz, nx, ny, nz, r, g, b_val, dot, t_wall, wall, tw, rx, ry, rz, r_len, r_hit, t_ref, r_hx, r_hy, r_hz, r_nx, r_ny, r_nz, r_r, r_g, r_b, r_dot, m_pattern) { t = 1e9; hit = "none"; # 1. SCHNITTPUNKT MIT DER KUGEL ocX = ox - spX; ocY = oy - spY; ocZ = oz - spZ; b = 2.0 * (dx * ocX + dy * ocY + dz * ocZ); c = (ocX*ocX + ocY*ocY + ocZ*ocZ) - spR2; disc = b*b - 4.0 * c; if (disc >= 0) { t_sp = (-b - sqrt(disc)) / 2.0; if (t_sp > 0.001 && t_sp < t) { t = t_sp; hit = "sphere"; } } # 2. SCHNITTPUNKT MIT DEN WÄNDEN if (dx < 0) { tw = (leftWallX - ox) / dx; if (tw > 0.001 && tw < t) { t = tw; hit = "left"; } } if (dx > 0) { tw = (rightWallX - ox) / dx; if (tw > 0.001 && tw < t) { t = tw; hit = "right"; } } if (dy < 0) { tw = (floorY - oy) / dy; if (tw > 0.001 && tw < t) { t = tw; hit = "floor"; } } if (dy > 0) { tw = (ceilingY - oy) / dy; if (tw > 0.001 && tw < t) { t = tw; hit = "ceiling"; } } if (dz > 0) { tw = (backWallZ - oz) / dz; if (tw > 0.001 && tw < t) { t = tw; hit = "back"; } } if (hit == "none") { printf "p %d %d 0 0 0\n", screenX, screenY; return; } # Exakter Treffpunkt im 3D-Raum hx = ox + dx * t; hy = oy + dy * t; hz = oz + dz * t; # 3. SHADING (FARBGEBUNG & MATHE-MUSTER) if (hit == "sphere") { # Kugel-Normale nx = (hx - spX) / spR; ny = (hy - spY) / spR; nz = (hz - spZ) / spR; # Reflexionsstrahl dot = dx*nx + dy*ny + dz*nz; rx = dx - 2.0 * dot * nx; ry = dy - 2.0 * dot * ny; rz = dz - 2.0 * dot * nz; r_len = sqrt(rx*rx + ry*ry + rz*rz); rx /= r_len; ry /= r_len; rz /= r_len; r_hit = trace_scene(hx + nx*0.001, hy + ny*0.001, hz + nz*0.001, rx, ry, rz); split(r_hit, rgb, " "); r = rgb[1]; g = rgb[2]; b_val = rgb[3]; # Glanzlicht basierend auf TAU und E r = int(r * 0.85 + (ESQ * 4)); g = int(g * 0.85 + (E * 8)); b_val = int(b_val * 0.85 + (PHI * 15)); } else { # Prozedurale mathematische Muster für die Wände if (hit == "left") { # Rote Wand mit Mustern basierend auf PI und TAU m_pattern = int(sin(hy * PI * 4) * cos(hz * TAU * 2) * 30); r = 180 + m_pattern; g = 30; b_val = 30; nx = 1; ny = 0; nz = 0; } if (hit == "right") { # Grüne Wand beeinflusst durch FEIG und GAUSS m_pattern = int(sin(hy * FEIG * 3) * 25); r = 30; g = 160 + m_pattern; b_val = 30; nx = -1; ny = 0; nz = 0; } if (hit == "floor") { # Schachbrettboden basierend auf dem Goldenen Schnitt (PHI) m_pattern = (int(floor(hx * PHI * 2)) + int(floor(hz * PHI * 2))) % 2; r = g = b_val = (m_pattern == 0) ? 120 : 190; nx = 0; ny = 1; nz = 0; } if (hit == "ceiling") { r = 140; g = 140; b_val = 140; nx = 0; ny = -1; nz = 0; } if (hit == "back") { # Rückwand mit mathematischen Ringen (Wellenmuster) abgeleitet von ESQ m_pattern = int(sin(sqrt(hx*hx + hy*hy) * ESQ) * 20); r = 150 + m_pattern; g = 150 + m_pattern; b_val = 150; nx = 0; ny = 0; nz = -1; } # Diffuses Lambert-Shading lx = lX - hx; ly = lY - hy; lz = lZ - hz; l_len = sqrt(lx*lx + ly*ly + lz*lz); lx /= l_len; ly /= l_len; lz /= l_len; dot = nx*lx + ny*ly + nz*lz; if (dot < 0) dot = 0; if (shadow_ray(hx + nx*0.001, hy + ny*0.001, hz + nz*0.001, lx, ly, lz, l_len)) { dot *= 0.2; } r = int(r * (dot * 0.8 + 0.2)); g = int(g * (dot * 0.8 + 0.2)); b_val = int(b_val * (dot * 0.8 + 0.2)); } # RGB-Begrenzung if (r > 255) r = 255; if (g > 255) g = 255; if (b_val > 255) b_val = 255; if (r < 0) r = 0; if (g < 0) g = 0; if (b_val < 0) b_val = 0; printf "p %d %d %d %d %d\n", screenX, screenY, r, g, b_val; } function trace_scene(ox, oy, oz, dx, dy, dz, t, hit, tw, hx, hy, hz, nx, ny, nz, r, g, b_val, lx, ly, lz, l_len, dot, m_pattern) { t = 1e9; hit = "none"; # Wände in der Reflexion prüfen if (dx < 0) { tw = (-1.8 - ox) / dx; if (tw > 0.001 && tw < t) { t = tw; hit = "left"; } } if (dx > 0) { tw = (1.8 - ox) / dx; if (tw > 0.001 && tw < t) { t = tw; hit = "right"; } } if (dy < 0) { tw = (-1.0 - oy) / dy; if (tw > 0.001 && tw < t) { t = tw; hit = "floor"; } } if (dy > 0) { tw = (1.0 - oy) / dy; if (tw > 0.001 && tw < t) { t = tw; hit = "ceiling"; } } if (dz > 0) { tw = (3.0 - oz) / dz; if (tw > 0.001 && tw < t) { t = tw; hit = "back"; } } if (hit == "none") return "0 0 0"; hx = ox + dx * t; hy = oy + dy * t; hz = oz + dz * t; # Identische Muster-Berechnung für Reflexionen if (hit == "left") { r = 180 + int(sin(hy * 3.1415 * 4) * 20); g = 30; b_val = 30; nx = 1; ny = 0; nz = 0; } if (hit == "right") { r = 30; g = 160; b_val = 30; nx = -1; ny = 0; nz = 0; } if (hit == "floor") { m_pattern = (int(floor(hx * 1.6180 * 2)) + int(floor(hz * 1.6180 * 2))) % 2; r = g = b_val = (m_pattern == 0) ? 120 : 190; nx = 0; ny = 1; nz = 0; } if (hit == "ceiling") { r = 140; g = 140; b_val = 140; nx = 0; ny = -1; nz = 0; } if (hit == "back") { r = 160; g = 160; b_val = 160; nx = 0; ny = 0; nz = -1; } lx = 1.5 - hx; ly = 2.0 - hy; lz = -1.0 - hz; l_len = sqrt(lx*lx + ly*ly + lz*lz); lx /= l_len; ly /= l_len; lz /= l_len; dot = nx*lx + ny*ly + nz*lz; if (dot < 0) dot = 0; if (shadow_ray(hx + nx*0.001, hy + ny*0.001, hz + nz*0.001, lx, ly, lz, l_len)) dot *= 0.2; return int(r * (dot * 0.8 + 0.2)) " " int(g * (dot * 0.8 + 0.2)) " " int(b_val * (dot * 0.8 + 0.2)); } function shadow_ray(ox, oy, oz, dx, dy, dz, max_t, ocX, ocY, ocZ, b, c, disc, t_sp) { ocX = ox - spX; ocY = oy - spY; ocZ = oz - spZ; b = 2.0 * (dx * ocX + dy * ocY + dz * ocZ); c = (ocX*ocX + ocY*ocY + ocZ*ocZ) - spR2; # Nutzt spR2 aus dem Hauptsetup disc = b*b - 4.0 * c; if (disc >= 0) { t_sp = (-b - sqrt(disc)) / 2.0; if (t_sp > 0.001 && t_sp < max_t) { return 1; } } return 0; } # Mathematische Hilfsfunktion für den Boden function floor(x) { return (x >= 0) ? int(x) : int(x) - 1; }