这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
2.2 点与向量的区别
齐次坐标里 w=1 是点、w=0 是向量,这一位之差决定了平移矩阵对谁生效。本节用可跑的例子演示点减点得向量、点加向量得点、向量加向量仍是向量,并逐条给出断言;最后把点当向量按 w=0 变换,展示平移项如何被乘 0 抹掉,以及模型为何飘不动。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
齐次坐标里 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≤0)、屏幕映射、三角形装配、背面剔除、边函数覆盖、重心插值、深度测试、像素输出。逐级给出输入输出契约与定点不变量,断言 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 三种过滤的差异,并用「选错一级」的负例验证判据确实有区分力。
顶点上的 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 落在安全区间内,并用一份被污染的规格证明这套判据会拒绝它。