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}