improve sw renderer

This commit is contained in:
asrael 2026-01-21 23:19:50 -06:00
parent 415d424057
commit 39ee0fefb7
89 changed files with 9380 additions and 2307 deletions

View file

@ -1,23 +1,32 @@
(local ffi (require :ffi))
(local pxl8 (require :pxl8))
(local effects (require :pxl8.effects))
(local SKY_GRADIENT_START 144)
(local SKY_GRADIENT_COUNT 16)
(local sky-radius 900)
(local sky-segments 16)
(local sky-rings 16)
(local max-theta (* math.pi 0.55))
(local STAR_COUNT 200)
(local TINY_STAR_COUNT 5000)
(local STAR_SILVER_START 160)
(local STAR_BLUE_START 168)
(local STAR_RED_START 176)
(local NUM_RANDOM_STARS 300)
(local NUM_TINY_STARS 7000)
(local STAR_SEED 0xDEADBEEF)
(local STAR_CYCLE_PERIOD 7200)
;; Use existing bright palette colors
;; Silver/white: indices 14-15 (brightest grays)
(local IDX_SILVER 14)
;; Warm/torch: indices 216-218 (bright creams/yellows)
(local IDX_TORCH 216)
;; Blue/magic: indices 176-178 (purples - brightest of the range)
(local IDX_MAGIC 176)
(var sky-mesh nil)
(var last-gradient-key nil)
(var stars [])
(var random-stars [])
(var sky-mesh nil)
(var star-count 0)
(var star-directions nil)
(var star-glows nil)
(var star-projected nil)
(var star-time 0)
(var tiny-stars [])
(fn generate-sky-gradient [zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b]
@ -33,8 +42,8 @@
indices []]
(for [i 0 (- sky-rings 1)]
(let [theta0 (* (/ i sky-rings) max-theta)
theta1 (* (/ (+ i 1) sky-rings) max-theta)
(let [theta0 (* (/ i sky-rings) math.pi 0.5)
theta1 (* (/ (+ i 1) sky-rings) math.pi 0.5)
sin-t0 (math.sin theta0)
cos-t0 (math.cos theta0)
sin-t1 (math.sin theta1)
@ -67,22 +76,17 @@
(if (= i 0)
(do
;; First ring is degenerate - just a triangle from pole
;; Vertices: v00 (pole, c0), v11 (bottom-right, c1), v10 (bottom-left, c1)
(table.insert verts {:x x00 :y y0 :z z00 :nx nx00 :ny ny00 :nz nz00 :color c0 :light 255})
(table.insert verts {:x x11 :y y1 :z z11 :nx nx11 :ny ny11 :nz nz11 :color c1 :light 255})
(table.insert verts {:x x10 :y y1 :z z10 :nx nx10 :ny ny10 :nz nz10 :color c1 :light 255})
;; Triangle: base, base+2, base+1
(table.insert indices base-idx)
(table.insert indices (+ base-idx 2))
(table.insert indices (+ base-idx 1)))
(do
;; Regular quad: v00 (top-left), v01 (top-right), v11 (bottom-right), v10 (bottom-left)
(table.insert verts {:x x00 :y y0 :z z00 :nx nx00 :ny ny00 :nz nz00 :color c0 :light 255})
(table.insert verts {:x x01 :y y0 :z z01 :nx nx01 :ny ny01 :nz nz01 :color c0 :light 255})
(table.insert verts {:x x11 :y y1 :z z11 :nx nx11 :ny ny11 :nz nz11 :color c1 :light 255})
(table.insert verts {:x x10 :y y1 :z z10 :nx nx10 :ny ny10 :nz nz10 :color c1 :light 255})
;; push_quad(base, base+3, base+2, base+1) = triangles (base,base+3,base+2) and (base,base+2,base+1)
(table.insert indices base-idx)
(table.insert indices (+ base-idx 3))
(table.insert indices (+ base-idx 2))
@ -98,149 +102,142 @@
(generate-sky-gradient zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b)
(set last-gradient-key key))))
(fn palette-ramp [start c0 c1]
(let [r0 (bit.rshift (bit.band c0 0xFF0000) 16)
g0 (bit.rshift (bit.band c0 0x00FF00) 8)
b0 (bit.band c0 0x0000FF)
r1 (bit.rshift (bit.band c1 0xFF0000) 16)
g1 (bit.rshift (bit.band c1 0x00FF00) 8)
b1 (bit.band c1 0x0000FF)]
(for [i 0 7]
(let [t (/ i 7)
r (math.floor (+ r0 (* t (- r1 r0))))
g (math.floor (+ g0 (* t (- g1 g0))))
b (math.floor (+ b0 (* t (- b1 b0))))]
(pxl8.set_palette_rgb (+ start i) r g b)))))
(fn galactic-band-factor [dx dy dz]
(let [band-len (math.sqrt (+ (* 0.6 0.6) (* 0.3 0.3) (* 0.742 0.742)))
bx (/ 0.6 band-len)
by (/ 0.3 band-len)
bz (/ 0.742 band-len)
dist-from-band (math.abs (+ (* dx bx) (* dy by) (* dz bz)))
in-band (- 1 (math.min (* dist-from-band 3) 1))]
(* in-band in-band)))
(fn init-star-palette []
(palette-ramp STAR_SILVER_START 0x707888 0xFFFFFF) ;; silver
(palette-ramp STAR_BLUE_START 0x5070B0 0xD0E8FF) ;; blue
(palette-ramp STAR_RED_START 0x802020 0xFF9090)) ;; red
(fn generate-stars [seed]
(set stars [])
(fn generate-stars []
(set random-stars [])
(set tiny-stars [])
(init-star-palette)
(pxl8.rng_seed seed)
(for [i 1 STAR_COUNT]
(let [theta (math.acos (- 1 (* (pxl8.rng_f32) 0.85)))
phi (* (pxl8.rng_f32) math.pi 2)
brightness (pxl8.rng_range 1 4)
color-type (pxl8.rng_range 0 100)
shade (pxl8.rng_range 0 5)
color (if (< color-type 3) (+ STAR_RED_START shade)
(< color-type 15) (+ STAR_BLUE_START shade)
(+ STAR_SILVER_START shade))
sin-t (math.sin theta)
cos-t (math.cos theta)]
(table.insert stars {:dx (* sin-t (math.cos phi))
:dy cos-t
:dz (* sin-t (math.sin phi))
:brightness brightness
:color color})))
;; Generate random stars - use full upper hemisphere (dy > -0.1)
(for [i 0 (- NUM_RANDOM_STARS 1)]
(let [h1 (pxl8.hash32 (+ STAR_SEED (* i 5)))
h2 (pxl8.hash32 (+ STAR_SEED (* i 5) 1))
h3 (pxl8.hash32 (+ STAR_SEED (* i 5) 2))
h4 (pxl8.hash32 (+ STAR_SEED (* i 5) 3))
theta (* (/ h1 0xFFFFFFFF) math.pi 2)
phi (math.acos (- 1 (* (/ h2 0xFFFFFFFF) 1.0)))
sin-phi (math.sin phi)
cos-phi (math.cos phi)
dx (* sin-phi (math.cos theta))
dy cos-phi
dz (* sin-phi (math.sin theta))
brightness-raw (/ (% h3 256) 255)
brightness (math.floor (+ 60 (* brightness-raw brightness-raw 195)))
color-type (% h4 100)
color (if (< color-type 8) (+ IDX_TORCH (% (bit.rshift h4 8) 2))
(< color-type 16) (+ IDX_MAGIC (% (bit.rshift h4 8) 2))
(+ IDX_SILVER (% (bit.rshift h4 8) 2)))]
(pxl8.rng_seed (+ seed 0xCAFEBABE))
(for [i 1 TINY_STAR_COUNT]
(let [theta (math.acos (- 1 (* (pxl8.rng_f32) 0.95)))
phi (* (pxl8.rng_f32) math.pi 2)
brightness (+ 25 (pxl8.rng_range 0 40))
shade (pxl8.rng_range 0 3)
color-type (pxl8.rng_range 0 100)
color (if (< color-type 15) (+ STAR_BLUE_START shade)
(+ STAR_SILVER_START shade))
sin-t (math.sin theta)
cos-t (math.cos theta)]
(table.insert tiny-stars {:dx (* sin-t (math.cos phi))
:dy cos-t
:dz (* sin-t (math.sin phi))
:brightness brightness
:color color}))))
(when (> dy -0.1)
(table.insert random-stars {:dx dx :dy dy :dz dz
:brightness brightness
:color color}))))
(fn project-direction [dir-x dir-y dir-z yaw pitch width height]
(let [cos-yaw (math.cos yaw)
sin-yaw (math.sin yaw)
cos-pitch (math.cos pitch)
sin-pitch (math.sin pitch)
rotated-x (+ (* dir-x cos-yaw) (* dir-z sin-yaw))
rotated-z (+ (* (- dir-x) sin-yaw) (* dir-z cos-yaw))
rotated-y (- (* dir-y cos-pitch) (* rotated-z sin-pitch))
final-z (+ (* dir-y sin-pitch) (* rotated-z cos-pitch))]
(when (> final-z 0.01)
(let [fov 1.047
aspect (/ width height)
half-fov-tan (math.tan (* fov 0.5))
ndc-x (/ rotated-x (* final-z half-fov-tan aspect))
ndc-y (/ rotated-y (* final-z half-fov-tan))]
(when (and (>= ndc-x -1) (<= ndc-x 1) (>= ndc-y -1) (<= ndc-y 1))
{:x (math.floor (* (+ 1 ndc-x) 0.5 width))
:y (math.floor (* (- 1 ndc-y) 0.5 height))})))))
(let [tiny-seed (+ STAR_SEED 0xCAFEBABE)]
(for [i 0 (- NUM_TINY_STARS 1)]
(let [h1 (pxl8.hash32 (+ tiny-seed (* i 4)))
h2 (pxl8.hash32 (+ tiny-seed (* i 4) 1))
h3 (pxl8.hash32 (+ tiny-seed (* i 4) 2))
h4 (pxl8.hash32 (+ tiny-seed (* i 4) 3))
theta (* (/ h1 0xFFFFFFFF) math.pi 2)
phi (math.acos (- 1 (* (/ h2 0xFFFFFFFF) 1.0)))
sin-phi (math.sin phi)
cos-phi (math.cos phi)
dx (* sin-phi (math.cos theta))
dy cos-phi
dz (* sin-phi (math.sin theta))
band-boost (galactic-band-factor dx dy dz)
base-bright (+ 40 (% h3 50))
brightness (+ base-bright (math.floor (* band-boost 40)))
color-shift (% h4 100)
color (if (< color-shift 3) (+ IDX_TORCH (% (bit.rshift h4 8) 2))
(< color-shift 12) (+ IDX_MAGIC (% (bit.rshift h4 8) 2))
(+ IDX_SILVER (% (bit.rshift h4 8) 2)))]
(when (> dy -0.1)
(table.insert tiny-stars {:dx dx :dy dy :dz dz
:brightness brightness
:color color})))))
(fn render-stars [yaw pitch intensity]
(when (> intensity 0)
(let [width (pxl8.get_width)
height (pxl8.get_height)
glows []
fade-sq (* intensity intensity)]
(set star-count (+ (length tiny-stars) (length random-stars)))
(set star-directions (pxl8.create_vec3_array star-count))
(set star-glows (pxl8.create_glows 10000))
(set star-projected (pxl8.create_vec3_array star-count))
(each [_ star (ipairs tiny-stars)]
(let [screen (project-direction star.dx star.dy star.dz yaw pitch width height)]
(when screen
(let [int (math.floor (* star.brightness fade-sq))]
(var idx 0)
(each [_ star (ipairs tiny-stars)]
(let [dir (. star-directions idx)]
(set dir.x star.dx)
(set dir.y star.dy)
(set dir.z star.dz))
(set idx (+ idx 1)))
(each [_ star (ipairs random-stars)]
(let [dir (. star-directions idx)]
(set dir.x star.dx)
(set dir.y star.dy)
(set dir.z star.dz))
(set idx (+ idx 1))))
(fn render-stars [cam-x cam-y cam-z intensity dt]
(set star-time (+ star-time (or dt 0)))
(when (and (> intensity 0) (> star-count 0) star-glows)
(let [fade-in (* intensity intensity)
time-factor (/ star-time 60)
star-rotation (/ (* star-time math.pi 2) STAR_CYCLE_PERIOD)
t (pxl8.mat4_translate cam-x cam-y cam-z)
r (pxl8.mat4_rotate_y star-rotation)
s (pxl8.mat4_scale sky-radius sky-radius sky-radius)
transform (pxl8.mat4_multiply t (pxl8.mat4_multiply r s))
tiny-count (length tiny-stars)]
(star-glows:clear)
(pxl8.project_points star-directions star-projected star-count transform)
(for [i 0 (- tiny-count 1)]
(let [screen (. star-projected i)]
(when (> screen.z 0)
(let [star (. tiny-stars (+ i 1))
int (math.floor (* star.brightness fade-in))]
(when (> int 8)
(table.insert glows {:x screen.x :y screen.y
:radius 1
:intensity int
:color star.color
:shape effects.GLOW_CIRCLE}))))))
(star-glows:add (math.floor screen.x) (math.floor screen.y)
1 int star.color pxl8.GLOW_CIRCLE))))))
(each [_ star (ipairs stars)]
(let [screen (project-direction star.dx star.dy star.dz yaw pitch width height)]
(when screen
(let [base-int (math.floor (* star.brightness 50 fade-sq 1.5))]
(if (>= star.brightness 4)
(for [i 0 (- (length random-stars) 1)]
(let [screen (. star-projected (+ tiny-count i))]
(when (> screen.z 0)
(let [star (. random-stars (+ i 1))
phase (+ (* (+ i 1) 2.137) (* time-factor 3.0))
twinkle (+ 0.75 (* 0.25 (math.sin (* phase 6.28))))
int (math.floor (* star.brightness fade-in twinkle))
sx (math.floor screen.x)
sy (math.floor screen.y)]
(if (> star.brightness 220)
(do
(table.insert glows {:x screen.x :y screen.y
:radius 4
:intensity (math.floor (/ base-int 4))
:color star.color
:shape effects.GLOW_CIRCLE})
(table.insert glows {:x screen.x :y screen.y
:radius 2
:intensity base-int
:color star.color
:shape effects.GLOW_DIAMOND}))
(>= star.brightness 3)
(star-glows:add sx sy 3 (math.floor (* int 1.5)) star.color pxl8.GLOW_DIAMOND)
(star-glows:add sx sy 5 (math.floor (/ int 2)) star.color pxl8.GLOW_CIRCLE))
(> star.brightness 180)
(do
(table.insert glows {:x screen.x :y screen.y
:radius 3
:intensity (math.floor (/ base-int 4))
:color star.color
:shape effects.GLOW_CIRCLE})
(table.insert glows {:x screen.x :y screen.y
:radius 2
:intensity base-int
:color star.color
:shape effects.GLOW_DIAMOND}))
(>= star.brightness 2)
(table.insert glows {:x screen.x :y screen.y
:radius 2
:intensity base-int
:color star.color
:shape effects.GLOW_DIAMOND})
(table.insert glows {:x screen.x :y screen.y
:radius 1
:intensity (math.floor (* base-int 0.7))
:color star.color
:shape effects.GLOW_CIRCLE}))))))
(star-glows:add sx sy 2 int star.color pxl8.GLOW_DIAMOND)
(star-glows:add sx sy 4 (math.floor (/ int 3)) star.color pxl8.GLOW_CIRCLE))
(> star.brightness 120)
(do
(star-glows:add sx sy 2 (math.floor (* int 0.67)) star.color pxl8.GLOW_DIAMOND)
(star-glows:add sx sy 3 (math.floor (/ int 4)) star.color pxl8.GLOW_CIRCLE))
(star-glows:add sx sy 2 (math.floor (* int 0.5)) star.color pxl8.GLOW_CIRCLE))))))
(when (> (length glows) 0)
(effects.glows glows)))))
(when (> (star-glows:count) 0)
(star-glows:render)))))
(fn render [cam-x cam-y cam-z]
(fn render [cam-x cam-y cam-z wireframe]
(when (not sky-mesh) (create-sky-dome))
(when sky-mesh
(pxl8.draw_mesh sky-mesh {:x cam-x :y cam-y :z cam-z :passthrough true})))
(pxl8.draw_mesh sky-mesh {:x cam-x :y cam-y :z cam-z :passthrough true :wireframe wireframe})))
{:render render
:render-stars render-stars