这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
6.1 微表面理论
微表面理论把镜面反射拆成 D、G、F 三个因子:GGX 分布 D 决定高光形状,Smith 几何项 G 补偿微面互相遮挡,Schlick 菲涅尔 F 决定反射比例。本节在 Q16.16 里把它们写成纯整数,用投影圆盘方向集验证 ∫D(n·h)dω≈1,并量出 alpha=0.25 时镜面波瓣的半高全宽是 40.74 度。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
微表面理论把镜面反射拆成 D、G、F 三个因子:GGX 分布 D 决定高光形状,Smith 几何项 G 补偿微面互相遮挡,Schlick 菲涅尔 F 决定反射比例。本节在 Q16.16 里把它们写成纯整数,用投影圆盘方向集验证 ∫D(n·h)dω≈1,并量出 alpha=0.25 时镜面波瓣的半高全宽是 40.74 度。
环境贴图把整个天空的入射光存下来,球谐用 9 个系数(L0/L1/L2)把它压成可解析重建的方向函数。本节用整数 Fibonacci 格点做投影,得到 9 个 Q16.16 系数,重建辐照度与直接积分只差 2‰;但太阳圆盘这类高频特征装不进 9 个系数,L2 带被它拉偏,反而在低频背景上引入振铃。
SSAO 从深度缓冲重建视空间位置与法线,再在半球上撒一组固定方向做遮挡估计。方向集固定会让误差呈低频带纹,把它按一张确定性的 4×4 旋转表逐像素转开,误差被打散成高频噪声,再用 3×3 盒式模糊压回去。本节量出 4/8/16/32/64 采样的收敛曲线、旋转前后的粗糙度,以及模糊对内部像素误差的削减,并在 TypeScript 侧逐字节复现。
环境光遮蔽衡量一个点周围半球有多大比例能看见天空,是环境光里最便宜的一阶近似。本节先构造一个能解析求解的最小场景——平面上方悬一个球,正下方那点的遮蔽恰为 1 减去 r/h 的平方——再用一组固定的 64 条整数方向做逐像素数值估计,量出它与解析值的误差、整幅 AO 图的 MAE,最后把 AO 作为乘性项接进着色,并在 TypeScript 侧复现同一张 AO 图。
把 32x32 的 8 位法线贴图还原成 128x128 世界法线:解压 n=(v<<17)/255-ONE 后未归一化模长落在 [0.9905,1.0024],归一化后偏差最大 2,与连续法线夹角最大 0.7753°。W 层用 WebGL2 片元着色器做同一件事,与 S 层按声明容差比对 MAE 0.5068 / PSNR 51.08 dB / 覆盖差异 0‰,负例被同一判据拒绝。
切线空间从三角形的三个顶点位置与三个 UV 解出:标准 UV 下 denom=65536、T=(2,0,0)、B=(0,0,2),手性 w=-1;把 u 反向令 denom=-65536 后 T 翻成 (-1,0,0)、w 变 +1。相邻面平均出的切线 T_avg=(1,0.5,0) 模长 1.1180、n·T_avg=0.5,Gram-Schmidt 后回到正交单位基。
法线不能直接用模型矩阵 M 变换:非均匀缩放 diag(2,1,1) 下 (M·t)·(M·n) 实测 +3.0000 而非 0,改用逆转置 (M⁻¹)ᵀ 后恒为 +0.0000;纯旋转时两者相等,错误只在非均匀缩放下暴露。4096 个单位法线扫描给出归一化后的模长不变量 |n| ∈ [ONE−5, ONE]。
材质的线性混合只要满足不变量 w_A+w_B==ONE 就仍能量守恒(五组权重实测 65533/65479/65426/65373/65321 全 ≤ ONE,权重和 =2·ONE 时涨到 130854);清漆层朴素相加 78640 越界、按 (1-F) 缩放后 60946 守恒。并用混合材质渲染一帧,跨宿主 sha256 逐字节相同。
把 Lambert、Phong、Blinn-Phong 三种 BRDF 写成全整数:1/π=20861,Phong 归一化 9/π=187749,Blinn 归一化 3/π=62583,指数 (R·V)^16 用 4 次平方。实跑能量:Lambert 65533、归一化 Phong 65321、归一化 Blinn 69886(超过 ONE),未归一化 Phong 只 22800。
把 BRDF 的半球反射率写成固定的 512 方向整数估计:cosθ_k=(2k+1)/128 是精确有理数、方位只用 {0,±1,±√2/2},全程无浮点三角函数。∫cosθ dω 的估计逐位等于 π(残差 0),Lambert 能量 65533(999.9542‰),残差 3。
给定一张「错了的图」,按二分定位法逐级比对覆盖率、法线、albedo 与最终颜色四张缓冲,把偏差锁到管线的某一段。差异数字本身就是线索:等于覆盖像素数说明逐像素操作出问题,只有几十个说明舍入或常数出问题。五个负例各定位到对应阶段,并对「插值后漏归一化」做一次完整演练,从法线图差异 43731 一路定位到漏掉的那一行。
把逐像素着色器的四张中间缓冲各自单独导出并断言:法线模长落在 [65533, 65537]、albedo 通道在 [70,220]、深度沿单三角形单调、覆盖率与解析解逐像素相同(43731 像素、差异 0)。法线图与 albedo 图编码后的 sha256 由 TypeScript 侧逐字节复现。
同一份场景规格在 Python 与 TypeScript 两条 CPU 路径下产出逐字节相同的像素缓冲。把着色器写成可参数化形式,五个负例各只动一处口径——降精度改用除法、插值后不归一化、逐项钳制、1/π 取 20860、albedo 四舍五入——每个都让 sha256 分叉,七个摘要两两不同,退化档复现卷二基线。
把光照拆成环境 / 漫反射 / 镜面 / 衰减四张灰度图,再加一张合成图。故意把衰减写成 r²/(r²+d)(分母忘了平方),衰减通道 43731/43731 个像素全变、最大差 47042,而环境 / 漫反射 / 镜面三通道逐字节相同——缺陷被精确定位到衰减那一行。
点光源衰减改成有限支撑的 (r²−d²)/(r²+d²),才让「哪些光源影响哪个像素」成为一条整数谓词。两级剔除:逐像素距离阈值 + 16×16 网格分桶。N=64 时循环 3031360→380842、完整求值恒为 72576、两版逐字节相同;代价模型 25702400→4498256 ops(比例 0.1750)。
三种光源共用一套光照方向构造与衰减接口:方向光无衰减、点光源 r²/(r²+d²)、聚光再乘锥软化。同一场景下受光像素 959.8‰ / 953.3‰ / 70.1‰;衰减的两种整数写法在 178/1280 个距离上分叉、最大差 1 个 Q16.16,d=range 处恰好 0.5。
一份材质表 JSON 驱动三种宿主:Python 建材质对象、TypeScript 建同构对象、HTML 内联同一条材质。三处解析的六个字段逐字段相同(对账表 S/W/P 三列一致),规范 digest c456c8c1… 两侧相同;改一个字段后帧 digest 与内联值同步变化,四次扰动全部同步。
材质的每个开关都让着色器分叉:4 开关理论 16 组合、实际只用 9;8 开关理论 256、实际 23,爆炸比 11.13×。变体键用整数位编码去重,与 frozenset、关键词串去重同数;键漏掉 shadow 位则去重数从 9 塌缩到 7。逐个渲染实际变体,不同键必给不同帧。
材质是六个定点参数而非浮点结构体。继承用不动点松弛解析,与输入顺序无关:叶子 gold_trim_dark 逐字段来源确定,解析 digest 074b01f1… 在打乱输入后不变,自指父指针报错;叶子材质驱动一帧,改被继承字段帧变、改旁系帧不变。
把「哪个抗锯齿方案更好」写成一条可复算的判据:先用参考解的梯度阈值圈出边缘像素,只在边缘上算 MAE,再配上 PSNR。64×64 圆盘的 296 个边缘像素上,无抗锯齿 → 4 spp SSAA → 8 帧 TAA → 16 spp SSAA 三级判据全部成立,而把覆盖率乘 0.5 的错实现被同一条判据拒绝。