这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
7.3 线宽与直线抗锯齿
线宽不能用开方算距离:把 d <= r 两边平方,得到一条纯整数的覆盖判据 cross^2 <= r^2*|AB|^2,端帽用最近端点的距离平方兜底。本节实测五个线宽的覆盖数、水平与竖直的对称性,并用 N x N 子样本把覆盖率收敛到解析面积 194514 千分像素^2。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
线宽不能用开方算距离:把 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 组上被恰好达到,说明它已不能再收紧。
《计算机图形学》卷一附录 A:scene_spec.json 的 44 个键、展平后 71 个标量叶子(相机、方向光、点光源、地形、球体、渲染参数;70 个整数 + name 字符串)、Q16.16 与 Q6 子像素的定点约定、四条舍入铁律、定点不变量清单、两条一致性纪律的判据表,以及全书数据集索引与一个可跑的定点约定自检脚本。
开曲面没有厚度:从背面相机看(屏幕绕序判为背面),一块 4x4 的布帘 32 个三角形全是背面,单面渲染得到一张空图。双面材质的做法不是不剔除,而是把背面三角形的几何法线取反再着色;本节实测单趟与两遍渲染逐字节相同,并给出「忘了取反」的指纹——背面图像恰好是正面图像的左右镜像。
背面剔除的判定只是一次整数符号比较,可它省下的到底是什么?本节把代价拆成像素测试数与覆盖像素数两笔,实测 896 个三角形下测试数从 161448 降到 140493(省 129‰),覆盖集合分毫不变,而地形这类开曲面在掠射处仍有 2477 个像素会因剔除而换色。
屏幕空间边函数 E(A,B,C) 的符号就是三角形的朝向。本节在《烛影山谷》的 896 个三角形上实测 606 正面、290 背面、0 退化,与光栅化器剔除的 290 完全一致;再证明世界空间几何法线与视线方向的点积是同一条判据,并用 det<0 的镜像矩阵说明手性反转时法线必须跟着翻。
直线的光栅化先要把方向折进一个八分圆,再决定每一步的取整语义:floor 会系统性偏下并漏掉终点,半像素偏置才落到最近像素。本节用定点 DDA 与整数 Bresenham 扫过 2079 组斜率,量出两者的分歧只出现在平局且步长无截断的 129 组,并用覆盖掩码的 sha256 把 Python 与 TypeScript 逐字节对齐。
多个三角形覆盖同一个像素时,谁的颜色留下由深度测试决定。本节把 6.2 插出的深度送进 z-buffer,说清严格 `<` 与 `<=` 的差别:前者让共享边像素的归属由绘制顺序决定——这不是「与顺序无关」,只是把赢家换成先画的那个;再用一份 64×48 场景让 WebGL2 与 CPU 定点内核对照,并给出必被拒绝的负例。
把 6.1 的三条边函数归一化就得到重心坐标:权重 w0/w1/w2 是像素对三个顶点的贡献比例。本节证明不变量 w0+w1+w2 恒等于面积(定点残差严格为 0),用权重线性插值颜色与深度,说清分子非负时 Python 的 `//` 与截断除同义,最后让 TypeScript 复现出逐字节相同的像素缓冲。
从整数边函数 E(A,B,P) 的「有向面积 2 倍」含义出发,推出三个边函数同号即在三角形内;实测屏幕空间 y 向下时 area > 0 对应的环绕方向、Q6 子像素精度与像素中心取法,并用子像素扰动实验区分恒等实现,最后看 `>= 0` 如何让共享边像素被重复认领。
视锥六平面的解析构造与近远平面:从 MVP 直接抽出六平面并用 45 个采样点核对判定,解释近平面不能取 0(深度值会塌到 1),用纯 xml.etree 生成 depth(D)=far/(far-near)·(1-near/D) 的精度分布 SVG,量化给出首个倍程独占约 503‰ 深度量程的结论。
逐项推导透视与正交两个投影矩阵:透视最后一行 (0,0,-1,0) 让 w=-z_view 随深度变化、片元要除以 w,正交最后一行 (0,0,0,1) 让 w 恒为 1、不做透视除法。用同一场景分别渲染两帧并量化覆盖率与深度分布差别,说明正交为何在工程制图里仍然有用。
从小孔成像推出针孔相机模型,把 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 的点必须被拒绝。