code wiki / _hdl_build / nx_raster3d_gouraud.nx

nx_raster3d_gouraud.nx source

↩ module page · 110 lines · 8247 B

1// nx_raster3d_gouraud.nx -- GOURAUD (SMOOTH) SHADING, the third renderer pillar (geometry=perspective-correct, 2// texture=bilinear, lighting=Gouraud). Interpolates per-VERTEX colors/intensities across the triangle (barycentric, 3// screen-affine -- the correct Gouraud model), per channel, integer/fixed-point => BIT-EXACT, no GPU. Flat shading 4// bands per face; Gouraud is smooth. KAT distinguishes them: an R/G/B triangle's centroid is the smooth blend 5// (~85,85,85), NOT a flat vertex color. 6// T1 Gouraud KAT: centroid of a red/green/blue triangle = the exact barycentric blend (86,86,82), not flat. 7// T2 smooth: the shaded triangle emits MANY distinct colors (a gradient), which flat per-face shading never would. 8// T3 Gouraud LIGHTING: a surface lit at the vertices (intensities 255/128/40) shades as a smooth ramp, not 3 bands. 9// T4 (EXCEED) determinism -- bit-identical across runs. 10// expect_exit: 0 Sovereign: nx_syscalls. NEVER-BRICK: software render, writes 0 GPU firmware. 11import "nx_syscalls.nx" 12const K_MAGIC_5381: i64 = 5381 13const K_MAGIC_4096: i64 = 4096 14 15func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 16func g_pn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var x: i64=v; if x<0{b[0]=45;sys_write(1,b,1);x=0-x} if x==0{b[0]=48;sys_write(1,b,1);return 0} var d: i64=0; var y: i64=x; while y>0{d=d+1;y=y/10} var i: i64=d-1; y=x; while i>=0{b[i]=(48+(y%10)) as u8;y=y/10;i=i-1} sys_write(1,b,d); return 0 } 17func ck(name: *u8, c: i64) -> i64 { if c==1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) } g_puts(name); g_puts("\n" as *u8); return c } 18 19const W: i64 = 64 20const H: i64 = 64 21const ZINF: i64 = 1000000000 22 23func fb_clear(fb: *i64, zb: *i64) -> i64 { var i: i64=0; while i<W*H { fb[i]=0; zb[i]=ZINF; i=i+1 } return 0 } 24func cksum(fb: *i64) -> i64 { var h: i64=K_MAGIC_5381; var i: i64=0; while i<W*H { h=((h<<5)+h)+fb[i]; i=i+1 } return h } 25func edge(ax: i64, ay: i64, bx: i64, by: i64, px: i64, py: i64) -> i64 { return (bx-ax)*(py-ay) - (by-ay)*(px-ax) } 26 27// GOURAUD triangle: interpolate the three vertex colors across the triangle, per R/G/B channel (barycentric). 28func draw_tri_gouraud(fb: *i64, zb: *i64, x0: i64, y0: i64, z0: i64, c0: i64, x1: i64, y1: i64, z1: i64, c1: i64, x2: i64, y2: i64, z2: i64, c2: i64) -> i64 { 29 let area0: i64 = edge(x0,y0,x1,y1,x2,y2) 30 if area0==0 { return 0 } 31 let r0: i64=(c0>>16)&255; let g0: i64=(c0>>8)&255; let b0: i64=c0&255 32 let r1: i64=(c1>>16)&255; let g1: i64=(c1>>8)&255; let b1: i64=c1&255 33 let r2: i64=(c2>>16)&255; let g2: i64=(c2>>8)&255; let b2: i64=c2&255 34 var minx: i64=x0; if x1<minx {minx=x1} if x2<minx {minx=x2} if minx<0 {minx=0} 35 var maxx: i64=x0; if x1>maxx {maxx=x1} if x2>maxx {maxx=x2} if maxx>W-1 {maxx=W-1} 36 var miny: i64=y0; if y1<miny {miny=y1} if y2<miny {miny=y2} if miny<0 {miny=0} 37 var maxy: i64=y0; if y1>maxy {maxy=y1} if y2>maxy {maxy=y2} if maxy>H-1 {maxy=H-1} 38 var py: i64=miny 39 while py<=maxy { 40 var px: i64=minx 41 while px<=maxx { 42 let w0: i64=edge(x1,y1,x2,y2,px,py); let w1: i64=edge(x2,y2,x0,y0,px,py); let w2: i64=edge(x0,y0,x1,y1,px,py) 43 var inside: i64=0 44 if area0>0 { if w0>=0 { if w1>=0 { if w2>=0 { inside=1 } } } } 45 else { if w0<=0 { if w1<=0 { if w2<=0 { inside=1 } } } } 46 if inside==1 { 47 var aw0: i64=w0; var aw1: i64=w1; var aw2: i64=w2; var aar: i64=area0 48 if area0<0 { aw0=0-w0; aw1=0-w1; aw2=0-w2; aar=0-area0 } 49 let r: i64=(aw0*r0+aw1*r1+aw2*r2)/aar; let g: i64=(aw0*g0+aw1*g1+aw2*g2)/aar; let b: i64=(aw0*b0+aw1*b1+aw2*b2)/aar 50 let depth: i64=(aw0*z0+aw1*z1+aw2*z2)/aar 51 let idx: i64=py*W+px 52 if depth<zb[idx] { zb[idx]=depth; fb[idx]=(r<<16)|(g<<8)|b } 53 } 54 px=px+1 55 } 56 py=py+1 57 } 58 return 0 59} 60// shade a base color by an integer intensity 0..255 (per channel). 61func lit(base: i64, inten: i64) -> i64 { let r: i64=((base>>16)&255)*inten/255; let g: i64=((base>>8)&255)*inten/255; let b: i64=(base&255)*inten/255; return (r<<16)|(g<<8)|b } 62 63func main() -> i64 { 64 g_puts("nx_raster3d_gouraud (SOVEREIGN integer Gouraud smooth shading: the third renderer pillar)\n" as *u8) 65 var pass: i64=0; var total: i64=0 66 let fb: *i64=sys_mmap(W*H*8) as *i64; let zb: *i64=sys_mmap(W*H*8) as *i64 67 68 // T1: Gouraud KAT. R/G/B triangle v0(10,10)red v1(50,10)green v2(30,50)blue. Centroid pixel (30,23) has 69 // barycentric ~(540,540,520)/1600 -> (86,86,82). Flat shading would paint one solid color. 70 fb_clear(fb, zb) 71 draw_tri_gouraud(fb, zb, 10,10,100,0xFF0000, 50,10,100,0x00FF00, 30,50,100,0x0000FF) 72 let cc: i64 = fb[23*W+30]; let cr: i64=(cc>>16)&255; let cg: i64=(cc>>8)&255; let cb: i64=cc&255 73 var t1: i64=0; if cr==86 { if cg==86 { if cb==82 { t1=1 } } } 74 g_puts(" T1 centroid pixel: R="); g_pn(cr); g_puts(" G="); g_pn(cg); g_puts(" B="); g_pn(cb); g_puts(" (expect 86,86,82 = smooth barycentric blend of all 3 vertices)\n" as *u8) 75 pass=pass+ck("T1: GOURAUD smooth shading -- the centroid is the exact 3-vertex barycentric blend, not a flat color" as *u8, t1); total=total+1 76 77 // T2: smoothness -- count distinct colors. Gouraud emits a GRADIENT (many colors); flat per-face = 1 color. 78 var seen: *i64=sys_mmap(K_MAGIC_4096*8) as *i64; var ns: i64=0; var pi: i64=0 79 while pi<W*H { let c: i64=fb[pi]; if c!=0 { var f: i64=0; var j: i64=0; while j<ns { if seen[j]==c { f=1 } j=j+1 } if f==0 { if ns<K_MAGIC_4096 { seen[ns]=c; ns=ns+1 } } } pi=pi+1 } 80 var t2: i64=0; if ns>50 { t2=1 } 81 g_puts(" T2 distinct colors in the shaded triangle="); g_pn(ns); g_puts(" (Gouraud = smooth gradient; flat shading would be 1)\n" as *u8) 82 pass=pass+ck("T2: the Gouraud triangle is a smooth GRADIENT (many distinct colors) -- flat per-face shading emits one" as *u8, t2); total=total+1 83 84 // T3: Gouraud LIGHTING -- a white surface lit at the vertices (intensities 255/128/40) shades as a smooth ramp. 85 // vertex colors = white * intensity. midpoint of the 255-vertex to 40-vertex edge should read ~(255+40)/2 ~ 147. 86 fb_clear(fb, zb) 87 draw_tri_gouraud(fb, zb, 10,30,100,lit(0xFFFFFF,255), 50,30,100,lit(0xFFFFFF,40), 30,58,100,lit(0xFFFFFF,128)) 88 let mc: i64 = fb[30*W+30]; let mr: i64=(mc>>16)&255 // midpoint of edge v0(255)-v1(40) 89 var t3: i64=0; if mr>=143 { if mr<=151 { t3=1 } } 90 g_puts(" T3 lit-ramp midpoint intensity="); g_pn(mr); g_puts(" (~147 = smooth (255+40)/2; flat would be a single face value)\n" as *u8) 91 pass=pass+ck("T3: Gouraud LIGHTING -- a vertex-lit surface shades as a smooth ramp (the actual use of smooth shading)" as *u8, t3); total=total+1 92 93 // T4 (EXCEED): determinism. 94 let fb2: *i64=sys_mmap(W*H*8) as *i64; let zb2: *i64=sys_mmap(W*H*8) as *i64 95 fb_clear(fb2, zb2) 96 draw_tri_gouraud(fb2, zb2, 10,30,100,lit(0xFFFFFF,255), 50,30,100,lit(0xFFFFFF,40), 30,58,100,lit(0xFFFFFF,128)) 97 let h1: i64=cksum(fb); let h2: i64=cksum(fb2) 98 var t4: i64=0; if h1==h2 { t4=1 } 99 g_puts(" T4 determinism: run1="); g_pn(h1); g_puts(" run2="); g_pn(h2); g_puts("\n" as *u8) 100 pass=pass+ck("T4 (EXCEED): Gouraud render is BIT-IDENTICAL across runs (integer-deterministic, any CPU, no GPU)" as *u8, t4); total=total+1 101 102 var okall: i64=0; if pass==total { okall=1 } 103 g_puts("---- nx_raster3d_gouraud: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8) 104 if okall==1 { 105 let logf: i64=sys_openat_append("knowledge/status/raster3d_gouraud.log" as *u8, 420) 106 if logf>=0 { let z: i64=sys_write(logf,"NXRASTER3DGOURAUD GREEN: integer Gouraud smooth shading -- per-vertex color/intensity barycentric interpolation; KAT centroid=exact blend, smooth gradient (many colors), lit ramp; the 3rd renderer pillar, bit-deterministic\n" as *u8,222); sys_close(logf) } 107 g_puts("verdict=GREEN (sovereign integer Gouraud smooth shading: exact barycentric blend, smooth gradient, vertex-lit ramp, bit-deterministic; the renderer's 3rd pillar -- geometry+texture+lighting all SOTA now)\n" as *u8); sys_exit(0); return 0 108 } 109 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 110}