16.3 交付卷三
卷二的收尾交付:接口契约写清卷一封了装配/深度/覆盖、卷二封了着色链(片元颜色的来源),卷三只替换「直接光照求值」为渲染方程的积分估计;另给出三条轴的参数扰动扫描——强度偏移 1 个 Q16.16 单位即产生首次可见差异(128 像素),超采样 N=4 才收敛,10 条软告警不硬失败、exit 0。
tag
卷二的收尾交付:接口契约写清卷一封了装配/深度/覆盖、卷二封了着色链(片元颜色的来源),卷三只替换「直接光照求值」为渲染方程的积分估计;另给出三条轴的参数扰动扫描——强度偏移 1 个 Q16.16 单位即产生首次可见差异(128 像素),超采样 N=4 才收敛,10 条软告警不硬失败、exit 0。
同一份 scene_spec.json 在 Python 定点、TypeScript 定点、浏览器 WebGL2 三处消费:S 与 P 成片 sha256 逐字节相同(0d997343…,43731 覆盖像素),S 与 W 按声明容差比对为 MAE 0.5067、PSNR 47.03 dB、覆盖归属差 0.0781‰;负例(片元颜色 ×0.5)MAE 21.3528 被同一判据拒绝。
把卷二前面十五节的环节首尾相接成一条从规格到成像的链:S 层在 320x240 场景上逐级核对十二个缓冲契约,w 不变量 487/487 成立、重心权重和违反 0 次、覆盖 43731 像素,成片 sha256 与卷二基线逐字节一致;P 层用 BigInt 重算同一链路复现同一摘要,负例(顶点顺序交换)必须让摘要分叉。
把三卷基线的 sha256、计数与容差判据并成一张表:卷一顶点光栅化 bac65977…、卷二逐像素 0d997343…、卷三主光线 213b4130…,三者命中或覆盖都是 43731 像素;再引第 13.3 与第 14.3 节的实测锚给出反直觉结论——列口径真 bug 下两个语言实现仍逐字节相同,只有掩码对称差(4 对 22212)抓得出来。
把三类判据摆在同一帧上:跨语言逐字节一致(sha256 相等,0 容忍)、跨路径像素掩码一致(光追命中掩码对光栅化覆盖掩码的对称差等于 4 个像素)、跨宿主声明容差(MAE ≤ 1.0 / PSNR ≥ 30 dB / 覆盖差 ≤ 20‰)。再给出反直觉的结论:判据一抓不到「两侧同错」。
在深度图上取 N x N 邻域逐个做整数遮蔽比较,可见度=(N^2-occ)/N^2,把硬边界摊成半影。8 顶点 / 4 三角形的极简场景里,N=1/3/5/7 的半影像素为 0/1103/2146/3059,与 N=15 参考的可见度 MAE 为 0.12926/0.11639/0.10724/0.09882,整帧 114.1 到 403.1 ms,收益按 1/N 递减。
把 487 顶点 / 896 三角形的烛影山谷用光源正交矩阵投到 256x256 深度图,再在主相机逐像素插值出光源空间坐标 (lu,lv,ld),整数比对 ld-map>bias 判定遮蔽:深度图覆盖 11409 纹素、光源趟剔除 192 三角形、主视图 43731 像素、硬阴影 1360 像素(31.1‰)。P 层与 S 层像素缓冲逐字节相同,W 层按声明容差收敛。
6.1 的 BRDF 和 6.2 的参数映射都默认反射能量不超过入射能量,但 BRDF 本身给不出这个保证。本节把 ∫f·cosθ·dω≤1 变成一个可跑的整数实验:GGX 各粗糙度的残差表、alpha=1 处的解析锚点 1-ln2、单次弹射丢掉 69.3% 能量、Kulla-Conty 补偿把它补回 1,以及 kD=1-F 为什么守不住掠射角。
金属度-粗糙度工作流只用两个标量描述一个表面:基础色在绝缘体时是漫反射 albedo、在金属时是镜面 F0,粗糙度平方后映射成 GGX 的 alpha。本节扫过粗糙度 0/0.25/0.5/0.75/1,量出 NDF 峰值从 809 掉到 0.32、半高角从 0.76 度张到 90 度,并说明为什么高光宽度要用 NDF 半高角而不是 BRDF 的半高全宽。
微表面理论把镜面反射拆成 D、G、F 三个因子:GGX 分布 D 决定高光形状,Smith 几何项 G 补偿微面互相遮挡,Schlick 菲涅尔 F 决定反射比例。本节在 Q16.16 里把它们写成纯整数,用投影圆盘方向集验证 ∫D(n·h)dω≈1,并量出 alpha=0.25 时镜面波瓣的半高全宽是 40.74 度。
给定一张「错了的图」,按二分定位法逐级比对覆盖率、法线、albedo 与最终颜色四张缓冲,把偏差锁到管线的某一段。差异数字本身就是线索:等于覆盖像素数说明逐像素操作出问题,只有几十个说明舍入或常数出问题。五个负例各定位到对应阶段,并对「插值后漏归一化」做一次完整演练,从法线图差异 43731 一路定位到漏掉的那一行。
点光源衰减改成有限支撑的 (r²−d²)/(r²+d²),才让「哪些光源影响哪个像素」成为一条整数谓词。两级剔除:逐像素距离阈值 + 16×16 网格分桶。N=64 时循环 3031360→380842、完整求值恒为 72576、两版逐字节相同;代价模型 25702400→4498256 ops(比例 0.1750)。
同一份场景、同一套顶点变换、同一个 Lambert 公式,只把「插值颜色」换成「插值法线再逐像素求值」:覆盖掩码与深度缓冲逐像素相同(几何封版),颜色却有 41261 个像素不同(537.2526‰),MAE 3.5764、PSNR 37.06 dB、最大通道差 48。
只裁近平面时投影坐标会飞出视口 64 倍,整数包围盒加剪刀矩形把扫描窗口压回视口且逐字节不改像素;保护带 G=0 就能让 80.7% 的三角形免裁,G=256 提到 93.4%。本节实测原始包围盒 4922869 单元、夹紧后 244708、扫描 223753 单元覆盖 43730 像素。
近平面裁剪会把三角形裁成四边形甚至五边形,重建它靠的是扇形三角化的整数恒等式:三个扇形三角形的面积和精确等于多边形面积,逆序则精确变号。本节实测英雄三角形 3→4→5 的逐步计数、面积比恰为 t^2 = 1/4 的解析解,以及「丢弃 vs 裁剪」两帧覆盖异或为 0 而颜色差 171。
视锥六个面在视图空间解析写成四条整数系数,与投影矩阵同源;Sutherland–Hodgman 的交点用 fdiv 定标、算术右移降精度,于是面积守恒成为精确整数恒等式。本节实测六平面系数、487 个顶点的分类、英雄三角形 3 裁成 5 的逐步计数,以及裁剪前后两帧覆盖异或为 0。
线宽不能用开方算距离:把 d <= r 两边平方,得到一条纯整数的覆盖判据 cross^2 <= r^2*|AB|^2,端帽用最近端点的距离平方兜底。本节实测五个线宽的覆盖数、水平与竖直的对称性,并用 N x N 子样本把覆盖率收敛到解析面积 194514 千分像素^2。
半像素误差界可以写成一条不含除法的整数判据:2*|k*major - i*minor| <= major。本节在 89439 个采样点上穷举验证:Bresenham 与半像素 DDA 的实测 max|e| 恰好是 0.5 像素,floor 取整的 DDA 有 1922 组越界;界在 651 组上被恰好达到,说明它已不能再收紧。
开曲面没有厚度:从背面看,一块 4x4 的布帘 32 个三角形全是背面,单面渲染得到一张空图。双面材质的做法不是不剔除,而是把背面三角形的几何法线取反再着色;本节实测单趟与两遍渲染逐字节相同,并给出「忘了取反」的指纹——背面图像恰好是正面图像的左右镜像。
背面剔除的判定只是一次整数符号比较,可它省下的到底是什么?本节把代价拆成像素测试数与覆盖像素数两笔,实测 896 个三角形下测试数从 161448 降到 140493(省 129‰),覆盖集合分毫不变,而地形这类开曲面在掠射处仍有 2477 个像素会因剔除而换色。