code wiki / _hdl_build / nx_cube3d_lit_gate.nx

nx_cube3d_lit_gate.nx source

↩ module page · 174 lines · 8301 B

1// nx_cube3d_lit_gate.nx -- LIT 3D: per-face Lambert shading. Rotated face NORMALS (cross product of rotated 2// edges, forced OUTWARD via the centroid) dotted with a light direction L, modulating each face's base color. 3// This is the step from "flat-colored cube" to "a real lit object" -- the lighting axis toward photoreal. 4// Light L=(3,4,12) (|L|=13, a Pythagorean quad -> no sqrt). NLMAX=4*S^2*|L| is DERIVED from cube geometry 5// (every cube face-normal has magnitude 4*S^2), not a magic constant. Composes nx_depth_tri. license_tier: ORIGINAL expect_exit: 0 6import "nx_syscalls.nx" 7import "nx_image.nx" 8import "nx_depth_tri.nx" 9import "nx_png_write.nx" 10 11const AMB: i64 = 105 // ambient floor (~41%): lifts shadowed faces to a pleasant level, directional term still shows 12 13func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 14func pn(v: i64) -> i64 { let t: *u8=sys_mmap(24); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} let b: *u8=sys_mmap(24); var j: i64=0; while j<k{b[j]=t[k-1-j];j=j+1} sys_write(1,b,k); return 0 } 15 16func isin(deg: i64) -> i64 { 17 var d: i64 = deg % 360 18 if d < 0 { d = d + 360 } 19 var sign: i64 = 1 20 if d >= 180 { sign = 0 - 1; d = d - 180 } 21 let hh: i64 = d * (180 - d) 22 return sign * (4 * hh * 1024 / (40500 - hh)) 23} 24func icos(deg: i64) -> i64 { return isin(deg + 90) } 25 26// rotate a vert (Y by ay, X by ax) -> rotated 3D coords in out[0..2] 27func rot3(vx: i64, vy: i64, vz: i64, ay: i64, ax: i64, out: *i64) -> i64 { 28 let SA: i64 = isin(ay) 29 let CA: i64 = icos(ay) 30 let SB: i64 = isin(ax) 31 let CB: i64 = icos(ax) 32 let x1: i64 = (vx * CA + vz * SA) / 1024 33 let z1: i64 = (0 - (vx * SA) + vz * CA) / 1024 34 out[0] = x1 35 out[1] = (vy * CB - z1 * SB) / 1024 36 out[2] = (vy * SB + z1 * CB) / 1024 37 return 0 38} 39 40// n.L for a face, with the normal forced OUTWARD (points same side as the face centroid). L=(3,4,12). 41func ndotl_face(rx: *i64, ry: *i64, rz: *i64, ia: i64, ib: i64, ic: i64, id: i64) -> i64 { 42 let ux: i64 = rx[ib]-rx[ia]; let uy: i64 = ry[ib]-ry[ia]; let uz: i64 = rz[ib]-rz[ia] 43 let vx: i64 = rx[id]-rx[ia]; let vy: i64 = ry[id]-ry[ia]; let vz: i64 = rz[id]-rz[ia] 44 var nx: i64 = uy*vz - uz*vy 45 var ny: i64 = uz*vx - ux*vz 46 var nz: i64 = ux*vy - uy*vx 47 // centroid direction (sum of the 4 verts ~ 4*centroid); flip normal if it points inward 48 let sx: i64 = rx[ia]+rx[ib]+rx[ic]+rx[id] 49 let sy: i64 = ry[ia]+ry[ib]+ry[ic]+ry[id] 50 let sz: i64 = rz[ia]+rz[ib]+rz[ic]+rz[id] 51 if nx*sx + ny*sy + nz*sz < 0 { nx = 0-nx; ny = 0-ny; nz = 0-nz } 52 return nx*3 + ny*4 + nz*12 53} 54func shade_of(ndl: i64, nlmax: i64) -> i64 { 55 var d: i64 = ndl 56 if d < 0 { d = 0 } 57 if d > nlmax { d = nlmax } 58 return AMB + ((255 - AMB) * d) / nlmax 59} 60 61func face_r(fb: *Image, zb: *i64, rx: *i64, ry: *i64, rz: *i64, px: *i64, py: *i64, pz: *i64, ia: i64, ib: i64, ic: i64, id: i64, r: i64, g: i64, b: i64, nlmax: i64, lit: i64) -> i64 { 62 var sh: i64 = 255 63 if lit == 1 { sh = shade_of(ndotl_face(rx, ry, rz, ia, ib, ic, id), nlmax) } 64 let rr: i64 = r * sh / 255 65 let gg: i64 = g * sh / 255 66 let bb: i64 = b * sh / 255 67 depth_tri_render(fb, zb, px[ia], py[ia], pz[ia], px[ib], py[ib], pz[ib], px[ic], py[ic], pz[ic], rr, gg, bb, 1) 68 depth_tri_render(fb, zb, px[ia], py[ia], pz[ia], px[ic], py[ic], pz[ic], px[id], py[id], pz[id], rr, gg, bb, 1) 69 return 0 70} 71 72func cube_lit(fb: *Image, zb: *i64, rx: *i64, ry: *i64, rz: *i64, px: *i64, py: *i64, pz: *i64, npix: i64, nlmax: i64, lit: i64) -> i64 { 73 let pb: *u8 = fb.pixels 74 var i: i64 = 0 75 while i < npix * 3 { pb[i] = 10 as u8; i = i + 1 } 76 depth_clear(zb, npix, 1000000000) 77 face_r(fb, zb, rx, ry, rz, px, py, pz, 4, 5, 6, 7, 80, 120, 255, nlmax, lit) // +Z blue 78 face_r(fb, zb, rx, ry, rz, px, py, pz, 0, 1, 2, 3, 235, 220, 90, nlmax, lit) // -Z amber 79 face_r(fb, zb, rx, ry, rz, px, py, pz, 1, 5, 6, 2, 255, 90, 90, nlmax, lit) // +X red 80 face_r(fb, zb, rx, ry, rz, px, py, pz, 0, 4, 7, 3, 90, 220, 220, nlmax, lit) // -X cyan 81 face_r(fb, zb, rx, ry, rz, px, py, pz, 3, 2, 6, 7, 110, 230, 130, nlmax, lit) // +Y green 82 face_r(fb, zb, rx, ry, rz, px, py, pz, 0, 1, 5, 4, 230, 120, 230, nlmax, lit) // -Y magenta 83 return 0 84} 85 86func main() -> i64 { 87 hw("=== nx_cube3d_lit_gate -- LIT 3D: per-face Lambert shading (rotated normals . light) ===\n" as *u8) 88 var fails: i64 = 0 89 let W: i64 = 220 90 let H: i64 = 220 91 let npix: i64 = W * H 92 let S: i64 = 52 93 let nlmax: i64 = 4 * S * S * 13 // DERIVED: |face normal|=4*S^2, |L|=13 94 95 let cvx: *i64 = sys_mmap(64) as *i64 96 let cvy: *i64 = sys_mmap(64) as *i64 97 let cvz: *i64 = sys_mmap(64) as *i64 98 cvx[0]=0-S; cvy[0]=0-S; cvz[0]=0-S 99 cvx[1]=S; cvy[1]=0-S; cvz[1]=0-S 100 cvx[2]=S; cvy[2]=S; cvz[2]=0-S 101 cvx[3]=0-S; cvy[3]=S; cvz[3]=0-S 102 cvx[4]=0-S; cvy[4]=0-S; cvz[4]=S 103 cvx[5]=S; cvy[5]=0-S; cvz[5]=S 104 cvx[6]=S; cvy[6]=S; cvz[6]=S 105 cvx[7]=0-S; cvy[7]=S; cvz[7]=S 106 107 // rotate (ay=35, ax=-24 -> see top) then project 108 let rx: *i64 = sys_mmap(64) as *i64 109 let ry: *i64 = sys_mmap(64) as *i64 110 let rz: *i64 = sys_mmap(64) as *i64 111 let px: *i64 = sys_mmap(64) as *i64 112 let py: *i64 = sys_mmap(64) as *i64 113 let pz: *i64 = sys_mmap(64) as *i64 114 let o: *i64 = sys_mmap(32) as *i64 115 var i: i64 = 0 116 while i < 8 { 117 rot3(cvx[i], cvy[i], cvz[i], 35, 0 - 24, o) 118 rx[i]=o[0]; ry[i]=o[1]; rz[i]=o[2] 119 let zc: i64 = o[2] + 560 120 px[i] = 110 + (o[0] * 340) / zc 121 py[i] = 110 - (o[1] * 340) / zc 122 pz[i] = zc 123 i = i + 1 124 } 125 126 let zb: *i64 = sys_mmap(npix * 8) as *i64 127 let fbL: *Image = nx_image_alloc(W, H, 3) 128 let fbF: *Image = nx_image_alloc(W, H, 3) 129 cube_lit(fbL, zb, rx, ry, rz, px, py, pz, npix, nlmax, 1) // LIT 130 cube_lit(fbF, zb, rx, ry, rz, px, py, pz, npix, nlmax, 0) // FLAT (neg-control) 131 132 // T1: lit frame != flat frame -> shading is applied 133 let pL: *u8 = fbL.pixels 134 let pF: *u8 = fbF.pixels 135 var d1: i64 = 0 136 var k: i64 = 0 137 while k < npix * 3 { if pL[k] != pF[k] { d1 = d1 + 1 } k = k + 1 } 138 hw(" lit-vs-flat diff bytes=" as *u8); pn(d1); hw("\n" as *u8) 139 var t1: i64 = 0 140 if d1 > 500 { t1 = 1 } 141 if t1 == 1 { hw("T1 PASS shading applied (lit frame differs from flat) -> per-face brightness is real\n" as *u8) } else { fails=fails+1; hw("T1 FAIL no shading\n" as *u8) } 142 143 // T2: directional lighting -> the lit cube has a brightness RANGE across its visible faces 144 var maxb: i64 = 0 145 var minb: i64 = 100000 146 var kk: i64 = 0 147 while kk < npix { 148 let r: i64 = pL[kk*3] as i64 149 let g: i64 = pL[kk*3+1] as i64 150 let b: i64 = pL[kk*3+2] as i64 151 let s: i64 = r + g + b 152 if s > 60 { // non-background 153 if s > maxb { maxb = s } 154 if s < minb { minb = s } 155 } 156 kk = kk + 1 157 } 158 hw(" visible brightness range: min=" as *u8); pn(minb); hw(" max=" as *u8); pn(maxb); hw(" spread=" as *u8); pn(maxb-minb); hw("\n" as *u8) 159 var t2: i64 = 0 160 if maxb - minb > 120 { t2 = 1 } 161 if t2 == 1 { hw("T2 PASS directional lighting: visible faces span a real brightness range (bright side vs shadowed side)\n" as *u8) } else { fails=fails+1; hw("T2 FAIL flat brightness (no direction)\n" as *u8) } 162 163 // T3: ambient floor -> even the dimmest visible face is >= ambient (nothing crushed to black) 164 var t3: i64 = 0 165 if minb >= AMB { t3 = 1 } 166 if t3 == 1 { hw("T3 PASS ambient floor respected (dimmest visible face >= ambient; no crushed blacks)\n" as *u8) } else { fails=fails+1; hw("T3 FAIL below ambient\n" as *u8) } 167 168 nx_png_write_rgb("knowledge/nx_cube3d_lit.png\x00" as *u8, fbL.pixels, W, H) 169 hw("artifact -> knowledge/nx_cube3d_lit.png (lit cube: light from upper-right-front)\n" as *u8) 170 171 if fails == 0 { hw("NX-CUBE3D-LIT GREEN -- lit 3D: per-face Lambert shading with outward rotated normals (the lighting axis toward photoreal)\n" as *u8); sys_exit(0); return 0 } 172 hw("NX-CUBE3D-LIT RED fails=" as *u8); pn(fails); hw("\n" as *u8) 173 sys_exit(1); return 1 174}