5.1 针孔相机模型
从小孔成像推出针孔相机模型,把 scene_spec.json 的 eyeQ16/targetQ16/upQ16/tanHalfFovYQ16/nearQ16/farQ16 换算成位置、视场角与等效焦距,用定点投影验证「同一物体距离翻倍、屏幕尺寸减半」这条不变量,并给出 Python 与 TypeScript 两侧逐位相同的屏幕高度。
category
从小孔成像推出针孔相机模型,把 scene_spec.json 的 eyeQ16/targetQ16/upQ16/tanHalfFovYQ16/nearQ16/farQ16 换算成位置、视场角与等效焦距,用定点投影验证「同一物体距离翻倍、屏幕尺寸减半」这条不变量,并给出 Python 与 TypeScript 两侧逐位相同的屏幕高度。
把 nlerp 与 slerp 的差别量化:nlerp 走弦、速度不均(170° 行程逐帧步长 max/min=1.784),slerp 走大圆弧、恒定角速度(max/min=1.004);演示四元数双覆盖——q 与 -q 同朝向,不取最短路径会绕 350° 远路,并给出全整数插值实现与端点、单调性断言。
把万向节死锁从口号变成三个可断言的数字:世界角速度 Jacobian 行列式 -cosβ 在 β=90° 归零、α 与 γ 的旋转轴从垂直变成反向共线、参数走一样远世界朝向却不动或转 180° 的死区;再给出整数四元数的表示、Hamilton 乘法与旋转,说明它为何不退化。
在 config 禁浮点的前提下用纯整数造定点正弦表:嵌套泰勒构造四分之一波表、查表加线性插值、二分查表求反三角,再用它搭出绕 x/y/z 三轴的旋转矩阵,断言 sin²+cos²=ONE 与正交性在声明容差内成立,并量化欧拉角 ZYX 与 XYZ 的顺序差异。
逐项推导透视投影矩阵的来历,含 (far+near)/(near-far) 与 2·far·near/(near-far) 的定点化;说明 MVP = P·V·M 的合成顺序与深度归一化到 [0, ONE] 的映射,并给出完整一帧渲染,断言 Python 与 TypeScript 的像素缓冲 sha256 完全相同。
推导 lookAt 的三个正交基 f/s/u 与平移项的来历,说明视图矩阵为何是相机世界变换的逆,用可跑的定点脚本验证世界原点在视图空间的位置等于相机位置取反后再旋转,并给出视线与 up 平行时叉积退化为零向量、矩阵奇异时的应对与拦截办法。
从平移、旋转、缩放的 4x4 齐次形式出发,解释复合变换为何不可交换,给出「先缩放、再旋转、再平移」的 Q16.16 定点实现并断言它与逐点手算逐项一致,再用实测数据说明矩阵乘的中间量为何超过 32 位、JavaScript 侧必须改用 BigInt。
透视投影的裁剪空间坐标是齐次的,w 来自投影矩阵第 4 行(即相机前方的距离),透视除法就是拿它去除 x、y、z。本节讲清 w 的来源与除法的意义,给出 NDC 到屏幕 Q6 的定点映射,并用一个小场景验证相机后方 w<=0 的点必须被拒绝。
齐次坐标里 w=1 是点、w=0 是向量,这一位之差决定了平移矩阵对谁生效。本节用可跑的例子演示点减点得向量、点加向量得点、向量加向量仍是向量,并逐条给出断言;最后把点当向量按 w=0 变换,展示平移项如何被乘 0 抹掉,以及模型为何飘不动。
本节把向量的加、减、点积、叉积搬进 Q16.16 定点整数:加减是同分母整数运算故逐位精确,乘积必须右移 16 降精度。我们用同一套确定性向量在 Python 与 TypeScript 下算出逐位相同的点积与叉积并以 sha256 锁定,最后给出点积误差恒落在 [0,3) ulp 的实测上界。
把规格里的分辨率、FOV、子像素位数与三角形数量四条轴分别拧出基线区间:给出走样阈值、相变阈值与每一档的代价曲线,并说明子像素位数是规格契约而非精度旋钮;四条轴的每一档摘要都由 TypeScript 侧逐字节复现,B 档越界只告警不中断,判据稳定可复跑。
同一份 config/scene_spec.json 分别喂给 S 层 Python 定点软光栅化、P 层 TypeScript 定点内核与 W 层浏览器 WebGL2:S 与 P 逐字节相同,S 与 W 按 MAE、PSNR、覆盖归属三条声明容差比对,并让片元颜色 ×0.5 的负例被同一判据拒绝。
把前 15 章的环节串成一条完整管线:顶点变换、近平面剔除、屏幕映射、三角形装配、背面剔除、边函数覆盖、重心插值、深度测试、像素输出。逐级给出输入输出契约与定点不变量,断言 w 恒等式与重心权重和,并让串联后的成片 sha256 与卷一基线逐字节一致。
场景图的层级变换合成与遍历序语义:world = 父 * 子 的顺序如何决定叶子落点(同一原点在 T·S 与 S·T 下分别落到 x = 1.5 与 x = 2.0),以及遍历序在不交叠场景逐字节相同、部分交叠场景差 72 个像素、完全重合时差异像素数等于覆盖像素数。
索引顶点缓冲与独立三角形表示的字节账:subdiv=2 的球索引存 66*12+128*12=2328 字节、独立三角形存 384*12=4608 字节,差 2280;再用 (x,y,z) 定点三元组整数去重把 384 个顶点槽压回 66 个,并用小端 int32 缓冲回读验证逐字节相同。
从《烛影山谷》的两类几何出发,推导八面体细分球的顶点数解析式 V = 4^(d+1) + 2 与地形格点数 (gridN+1)^2,用整数哈希噪声加双线性与 smoothstep 造起伏、顶点色按高度插值,最后把整场景几何序列化成小端 int32 缓冲取 sha256 与 TypeScript 逐字节对照。
把定点管线写成可参数化形式:baseline 复现 S 层金标准 sha256,再把 `>>` 换成向零截断、把截断除法换成 floor 除法、把子像素位数 6 改成 5,三类扰动各给出一个不同的 sha256(覆盖 43730 / 43734 / 43731,分歧像素 153 / 173 / 646),并由 TypeScript 侧逐字节复现全部四个摘要。
把 GPU 的 top-left rule 写成纯整数判据:有向边 A→B 拥有其边界样本当且仅当 dy<0 或 dy==0 且 dx>0。用顶点落在像素角上的正方形隔离共享边变量,实测 3x3 网格下 top-left 让共享边像素恰好被一个三角形认领(认领合计=并集=144、重复 0、最大认领 1),而 naive `>= 0` 重复 36 个像素。
把同一份《烛影山谷》规格分别喂给 Q16.16 定点管线与 float64 镜像管线:矩阵链最大元素差 5.007e-04,132/487 个顶点的屏幕坐标或深度不同;给定同一条整数坐标时两种光栅化器的像素缓冲逐字节相同,端到端则分歧 153 个像素并给出位置分布与通道差直方图。
纹理被缩小时,一个像素覆盖多个 texel,采其中一个必然走样。解法是预先造好一整串减半的 mip 链,再按屏幕足迹选级。本节用整数盒式平均构造 32→1 的六级链,用 uv 的有限差分算出每像素 LOD,量出最近邻、双线性与 mip 三种过滤的差异,并用「选错一级」的负例验证判据确实有区分力。