code wiki / _hdl_build / nx_gx8_ssao_gate.nx

nx_gx8_ssao_gate.nx source

↩ module page · 208 lines · 8924 B

1// nx_gx8_ssao_gate.nx -- Gx-8: AMBIENT OCCLUSION, the contact cue that stops everything floating. 2// Direct lighting alone carries no proximity information, so a shape resting ON a surface and one hovering 3// above it shade identically -- which is why our figures and beacons read as pasted onto the scene rather 4// than sitting in it. sg_ssao adds cavity darkening from the finished depth buffer. 5// THE TEST THAT MATTERS is the neg-control: a naive depth-only SSAO greys out every receding floor, because 6// downhill neighbours are always nearer. This gate renders a big SLOPED PLANE and requires AO to leave it 7// essentially untouched, while requiring the crevice-rich humanoid to darken measurably. Passing ONE of 8// those is easy; passing BOTH is the whole point of the opposite-pair sampling. 9// license_tier: ORIGINAL expect_exit: 0 10import "nx_swgpu.nx" 11import "nx_png_write.nx" 12 13func ao_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 14func ao_pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 } 15 16func ao_fb_rgb(base: i64, dst: *u8) -> i64 { 17 let fb: *i64 = sg_fb(base) 18 var p: i64 = 0 19 while p < ww()*hh() { 20 let v: i64 = fb[p] 21 dst[p*3] = (v % 256) as u8 22 dst[p*3+1] = ((v/256) % 256) as u8 23 dst[p*3+2] = ((v/65536) % 256) as u8 24 p = p + 1 25 } 26 return 0 27} 28 29// a large plane receding from the camera -- the surface a depth-only SSAO would wrongly grey out 30func ao_build_plane(base: i64) -> i64 { 31 let px: *i64 = (base + O_PX) as *i64 32 let py: *i64 = (base + O_PY) as *i64 33 let pz: *i64 = (base + O_PZ) as *i64 34 let nx: *i64 = (base + O_NX) as *i64 35 let ny: *i64 = (base + O_NY) as *i64 36 let nz: *i64 = (base + O_NZ) as *i64 37 let ta: *i64 = (base + O_TA) as *i64 38 let tn: *i64 = (base + O_TN) as *i64 39 let vc: *i64 = (base + O_VC) as *i64 40 sg_reset(base) 41 // 12x12 grid so it is finely tessellated (many depth steps) yet perfectly planar 42 let SIDE: i64 = 13 43 var j: i64 = 0 44 while j < SIDE { 45 var i: i64 = 0 46 while i < SIDE { 47 let vi: i64 = j*SIDE + i 48 px[vi] = ((0-24) + i*4) * 4096 49 py[vi] = 0 - 9000 50 pz[vi] = (6 + j*4) * 4096 51 nx[vi] = 0; ny[vi] = 4096; nz[vi] = 0 52 i = i + 1 53 } 54 j = j + 1 55 } 56 vc[0] = SIDE*SIDE 57 var t: i64 = 0 58 j = 0 59 while j < SIDE-1 { 60 var i2: i64 = 0 61 while i2 < SIDE-1 { 62 let v00: i64 = j*SIDE + i2 63 let v10: i64 = j*SIDE + i2 + 1 64 let v01: i64 = (j+1)*SIDE + i2 65 let v11: i64 = (j+1)*SIDE + i2 + 1 66 ta[t*3]=v00; ta[t*3+1]=v10; ta[t*3+2]=v11; t=t+1 67 ta[t*3]=v00; ta[t*3+1]=v11; ta[t*3+2]=v10; t=t+1 68 ta[t*3]=v00; ta[t*3+1]=v11; ta[t*3+2]=v01; t=t+1 69 ta[t*3]=v00; ta[t*3+1]=v01; ta[t*3+2]=v11; t=t+1 70 i2 = i2 + 1 71 } 72 j = j + 1 73 } 74 tn[0] = t 75 return t 76} 77 78// mean luminance over pixels that belong to geometry (z-buffer says something was drawn there) 79func ao_mean_lit(base: i64, rgb: *u8, cnt: *i64) -> i64 { 80 let zb: *i64 = sg_zb(base) 81 var sum: i64 = 0 82 var n: i64 = 0 83 var p: i64 = 0 84 while p < ww()*hh() { 85 if zb[p] < ZFAR { 86 sum = sum + (rgb[p*3] as i64) + (rgb[p*3+1] as i64) + (rgb[p*3+2] as i64) 87 n = n + 1 88 } 89 p = p + 1 90 } 91 cnt[0] = n 92 if n == 0 { return 0 } 93 return sum / (n*3) 94} 95 96func main() -> i64 { 97 ao_puts("=== nx_gx8_ssao_gate -- ambient occlusion: contact darkening without greying out the floor ===\n" as *u8) 98 var fails: i64 = 0 99 let base: i64 = sys_mmap(swgpu_bytes()) as i64 100 let noao: *u8 = sys_mmap(ww()*hh()*3+16) 101 let withao: *u8 = sys_mmap(ww()*hh()*3+16) 102 let cnt: *i64 = sys_mmap(8*4) as *i64 103 104 // ================= SUBJECT: the humanoid -- overlapping ellipsoids, i.e. many real crevices ========== 105 sg_humanoid(base, 20) 106 sg_project(base, 500, 0-5) 107 sg_render(base, 240, 182, 158) 108 ao_fb_rgb(base, noao) 109 let meanBefore: i64 = ao_mean_lit(base, noao, cnt) 110 let bodyPx: i64 = cnt[0] 111 sg_ssao(base, 190) 112 ao_fb_rgb(base, withao) 113 let meanAfter: i64 = ao_mean_lit(base, withao, cnt) 114 115 var darkened: i64 = 0 116 var brightened: i64 = 0 117 var p: i64 = 0 118 while p < ww()*hh() { 119 let a: i64 = noao[p*3] as i64 120 let b: i64 = withao[p*3] as i64 121 if b < a - 3 { darkened = darkened + 1 } 122 if b > a + 3 { brightened = brightened + 1 } 123 p = p + 1 124 } 125 ao_puts(" FIGURE: body px="); ao_pn(bodyPx); ao_puts(" mean lum "); ao_pn(meanBefore) 126 ao_puts(" -> "); ao_pn(meanAfter); ao_puts(" darkened px="); ao_pn(darkened) 127 ao_puts(" brightened px="); ao_pn(brightened); ao_puts("\n" as *u8) 128 129 // ---- T1 AO actually darkens the crevice-rich subject ---- 130 var t1: i64 = 0 131 if darkened > 500 { if meanAfter < meanBefore { t1 = 1 } } 132 if t1==1 { ao_puts("T1 PASS occlusion darkens the figure's cavities ("); ao_pn(darkened); ao_puts(" px)\n" as *u8) } 133 else { fails=fails+1; ao_puts("T1 FAIL no darkening\n" as *u8) } 134 135 // ---- T2 it ONLY darkens: ambient occlusion must never ADD light ---- 136 var t2: i64 = 0 137 if brightened == 0 { t2 = 1 } 138 if t2==1 { ao_puts("T2 PASS occlusion is subtractive only -- zero pixels brightened\n" as *u8) } 139 else { fails=fails+1; ao_puts("T2 FAIL brightened "); ao_pn(brightened); ao_puts(" px\n" as *u8) } 140 141 // ---- T3 it is SELECTIVE, not a global dimmer: most of the figure is untouched ---- 142 var t3: i64 = 0 143 if darkened * 100 / bodyPx < 60 { t3 = 1 } 144 ao_puts(" darkened share of the body = "); ao_pn(darkened*100/bodyPx); ao_puts(" percent\n" as *u8) 145 if t3==1 { ao_puts("T3 PASS selective: cavities darken, open surfaces do not (not a global dimmer)\n" as *u8) } 146 else { fails=fails+1; ao_puts("T3 FAIL blanket darkening\n" as *u8) } 147 148 // ================= NEG-CONTROL: a flat receding plane must come out essentially UNTOUCHED ========== 149 ao_build_plane(base) 150 sg_project(base, 0, 0) 151 sg_render(base, 150, 150, 155) 152 ao_fb_rgb(base, noao) 153 let planePx: i64 = 0 154 ao_mean_lit(base, noao, cnt) 155 let planeCount: i64 = cnt[0] 156 sg_ssao(base, 190) 157 ao_fb_rgb(base, withao) 158 var planeDark: i64 = 0 159 p = 0 160 while p < ww()*hh() { 161 let a: i64 = noao[p*3] as i64 162 let b: i64 = withao[p*3] as i64 163 if b < a - 3 { planeDark = planeDark + 1 } 164 p = p + 1 165 } 166 ao_puts(" PLANE (neg-control): surface px="); ao_pn(planeCount); ao_puts(" darkened px="); ao_pn(planeDark) 167 if planeCount > 0 { ao_puts(" = "); ao_pn(planeDark*100/planeCount); ao_puts(" percent\n" as *u8) } else { ao_puts("\n" as *u8) } 168 169 // ---- T4 THE FALSE-POSITIVE TEST: a receding plane is not a cavity and must not be shaded ---- 170 var t4: i64 = 0 171 if planeCount > 10000 { if planeDark * 100 / planeCount < 5 { t4 = 1 } } 172 if t4==1 { ao_puts("T4 PASS receding plane left alone -- the opposite-pair test rejects slopes (a depth-only SSAO fails here)\n" as *u8) } 173 else { fails=fails+1; ao_puts("T4 FAIL plane wrongly occluded\n" as *u8) } 174 175 // ---- artifact: figure without AO | figure with AO ---- 176 sg_humanoid(base, 20) 177 sg_project(base, 500, 0-5) 178 sg_render(base, 240, 182, 158) 179 ao_fb_rgb(base, noao) 180 sg_ssao(base, 190) 181 ao_fb_rgb(base, withao) 182 let CW: i64 = 1028 183 let comp: *u8 = sys_mmap(CW*hh()*3+16) 184 var y2: i64 = 0 185 while y2 < hh() { 186 var x2: i64 = 0 187 while x2 < CW { 188 let o: i64 = (y2*CW+x2)*3 189 if x2 < ww() { let s: i64=(y2*ww()+x2)*3; comp[o]=noao[s]; comp[o+1]=noao[s+1]; comp[o+2]=noao[s+2] } 190 else { if x2 >= ww()+4 { let s2: i64=(y2*ww()+(x2-ww()-4))*3; comp[o]=withao[s2]; comp[o+1]=withao[s2+1]; comp[o+2]=withao[s2+2] } 191 else { comp[o]=200 as u8; comp[o+1]=90 as u8; comp[o+2]=110 as u8 } } 192 x2 = x2 + 1 193 } 194 y2 = y2 + 1 195 } 196 nx_png_write_rgb("knowledge/nx_gx8_ssao.png\x00" as *u8, comp, CW, hh()) 197 let szp: *i64 = sys_mmap(16) as *i64 198 let rb: *u8 = sys_read_file("knowledge/nx_gx8_ssao.png\x00" as *u8, szp) 199 var t5: i64 = 0 200 if (rb as i64)!=0 { if szp[0]>1000 { t5=1 } } 201 if t5==1 { ao_puts("T5 PASS artifact written (no-AO | AO), "); ao_pn(szp[0]); ao_puts(" bytes\n" as *u8) } 202 else { fails=fails+1; ao_puts("T5 FAIL png\n" as *u8) } 203 204 if fails==0 { ao_puts("GX8-SSAO GREEN -- cavities darken, open surfaces and receding planes do not, occlusion never adds light\n" as *u8); sys_exit(0); return 0 } 205 ao_puts("GX8-SSAO RED fails="); ao_pn(fails); ao_puts("\n" as *u8) 206 sys_exit(1) 207 return 1 208}