12.2 透视校正插值
顶点上的 UV 直接线性插值到像素是错的:透视投影下屏幕坐标是 x/w,属性在屏幕空间不再线性。正确做法是插值 u/w 与 1/w 再相除。本节在一块近远端 w 相差 2.57 倍的后仰四边形上把两条路径都跑一遍,量出 1923 个像素不同、最大通道偏差 180,再让 WebGL2 用同一份场景复现。
category
顶点上的 UV 直接线性插值到像素是错的:透视投影下屏幕坐标是 x/w,属性在屏幕空间不再线性。正确做法是插值 u/w 与 1/w 再相除。本节在一块近远端 w 相差 2.57 倍的后仰四边形上把两条路径都跑一遍,量出 1923 个像素不同、最大通道偏差 180,再让 WebGL2 用同一份场景复现。
纹理在显存里是一张二维表,UV 就是把这张表贴到三角形上的那套坐标。本节把 uv 也做成 Q16.16 定点数,用整数移位把连续坐标落到具体 texel,并实现 repeat / clamp / mirror 三种寻址——它们分别是位与、钳位与向下取整的模。纹理不引入任何第三方素材,由整数棋盘与渐变程序化生成。
MSAA 只在覆盖上多采样、着色一次。本节用 8 片带深度的叶片,把每像素 4 个子样本各自做覆盖与深度测试再按覆盖平均的 CPU 实现写出来,并让浏览器 WebGL2 用 4 遍平移渲染做同一件事,按 MAE/PSNR/覆盖归属三条容差判据对上,且负例必被拒绝。
把像素切成 N×N 个小格,覆盖率就是落在三角形内的子样本数——0 到 N² 的整数计数。本节用一个斜边三角形,把 2×2 与 4×4 超采样的覆盖率、整数颜色混合与误差随 N 的下降全部写成可断言整数,并与 TypeScript 逐字节对齐。
渲染的本质是把连续场景采样成离散像素。本节用 256×256 的整数波带片与周期 3 斜条纹,把「点采样把高频折回低频」的走样复现出来,再用整数方差把摩尔纹量成可断言的数字;Nyquist 边界上周期 2 的条纹会塌成一个常数,判据在 Python 与 TypeScript 间逐字节复现。
深度缓冲内部只有 65537 个刻度,一个刻度值多少世界距离却随距离按 D² 增长。本节从 5.3 的相机量程视角切回缓冲内部,量化 Q16.16 的最小可分辨深度差、塌陷距离与误差传播,再看逐像素定点插值的截断误差如何沿距离放大,并让 Python 与 TypeScript 两份实现逐字节对齐。
1.1 的脚本能用,但四步糊在一个 main 里。本节把它重构成规格、几何、屏幕坐标、像素四段,每段一个有名字、可单独调用的函数,再串起来渲染《烛影山谷》的第一帧并逐段计时;同时说明 S 层与 P 层如何共用同一份规格,做到像素缓冲逐字节相同。
config/scene_spec.json 有 31 个必填字段,分布在渲染参数、相机、两盏光源、地形与球体五组里。本节逐组讲清每个字段的量纲与取值范围,给出纯 Python 的 schema 校验脚本,断言所有数值字段都是整数、Q16.16 落在安全区间内,并用一份被污染的规格证明这套判据会拒绝它。
渲染一帧到底发生了什么?本节把整条链路压成顶点变换、三角形组装、光栅化、逐像素着色四步,用一份 config/scene_spec.json 跑出 487 顶点、896 三角形、43731 个覆盖像素并落盘一张 PNG;每一步都只用整数与定点,为后面三种宿主消费同一份规格打下地基。
《计算机图形学》卷一《几何、变换与光栅化》目录:16 章 48 节,把渲染从 API 调用还原成可算的整数——Q16.16 定点顶点变换、整数边函数覆盖判定、定点重心插值、深度缓冲、裁剪、抗锯齿与纹理映射全部定点实现,让 Python 与 TypeScript 产出逐字节相同的 PNG。本页给出与站内既有内容的分工、六档可运行性口径、《烛影山谷》场景总览与全书 16 章 48 节完整清单。