光线追踪
需要预先学习: 光栅化 光线追踪 光栅化 处理的是"每个三角形覆盖哪些像素",从几何走向图像;着色与渲染管线 里的着色是局部的——每个点只依赖光源与自身材质,不知道其他物体的存在。于是阴影要靠 ShadowMapping 单独补救,镜面反射要靠额外的 pass 画,多次反弹的间接光只能用一个常数环境光糊弄过去。 光线追踪(ray tracing)换一个方向思考:既然光从光源出发,经过物体的反射、折射,最终进入人眼,那么从摄像机出发,沿像素方向把这条路径倒着走一遍,同样能算出像素的颜色。它模拟的是光的传播本身,全局光照的效果(阴影、反射、折射、多次反弹)自然地包含在流程里,代价是计算量大得多。本篇以经典的 Whitted 风格光线追踪(Whitted-style ray tracing)为主线,介绍光线与物体的求交、递归追踪与加速结构。 光线的定义 光线的数学表示 一条光线(ray)由一个起点和一个方向确定,写成参数形式: P(t)=O+tD,t≥0\mathbf{P}(t) = \mathbf{O} + t\mathbf{D}...
全局光照与局部光照
需要预先学习: 着色与渲染管线 光线追踪 渲染方程与重要性采样 局部光照(Local Illumination) 光照模型(illumination model)描述表面颜色如何由光源与材质决定。着色与渲染管线 里的着色是局部的:每个着色点独立计算颜色,只考虑光源与表面本身的属性(法线、材质系数),完全不考虑场景中其他物体的存在。 Blinn-Phong 模型是典型的局部光照模型(local illumination model),它把表面颜色拆成环境光、漫反射、高光三项之和。局部光照的"局部"体现在两点: 逐点独立:每个点的着色只依赖该点的法线、位置与光源,与其他点互不相干; 无视遮挡:着色点与光源之间有什么东西、有没有东西挡着,一概不知。 这两点决定了局部光照的固有局限。 局部光照的局限 因为每个点只知道自己和光源,局部光照算不出一系列依赖"场景其他部分"的效果: 效果 为什么局部光照做不到 补救手段 阴影 不知道光源与点之间是否有物体遮挡 ShadowMapping([[Sha...
渲染方程与重要性采样
需要预先学习: 全局光照与局部光照 光线追踪 渲染方程 从局部到全局的数学表达 全局光照与局部光照 把光照分为局部与全局两个层次。渲染方程(rendering equation)把"全局"写成了数学形式:一个表面点向观察方向反射的总亮度,等于它自身发出的光,加上从法线半球所有方向射来的光经 BRDF 反射后的总和: Lo(p,ωo)=Le(p,ωo)+∫Ωfr(p,ωi,ωo) Li(p,ωi) (n⋅ωi) dωiL_o(p, \omega_o) = L_e(p, \omega_o) + \int_{\Omega} f_r(p, \omega_i, \omega_o)\, L_i(p, \omega_i)\, (\mathbf{n} \cdot \omega_i)\, d\omega_i Lo(p,ωo)=Le(p,ωo)+∫Ωfr(p,ωi,ωo)Li(p,ωi)(n⋅ωi)dωi 方程中各项: 符号 含义 Lo(p,ωo)L_o(p, \omega_o)Lo(p,ωo) 点 ppp...
坐标空间总览
需要预先学习: 变换 视图(摄像机)与投影 光栅化 一个顶点的旅程 前面几篇笔记分别讲了变换(变换)、视图与投影(视图(摄像机)与投影)、光栅化(光栅化),每一篇都工作在某一个特定的坐标空间里。这篇笔记把它们串成一条完整的链:一个顶点从模型文件出发,到成为屏幕上的像素,中间经过哪些空间、每一步由哪个矩阵驱动、www 分量经历了什么。 空间 又称 变换矩阵 关键操作 模型空间 局部空间(local space) —(起点) 顶点属性定义于此 世界空间 world space 模型矩阵 MMM 摆放物体、光源与摄像机 视图空间 摄像机空间 / 眼睛空间(eye space) 视图矩阵 VVV 摄像机移到原点、看向 −z-z−z 裁剪空间 clip space 投影矩阵 PPP 可视区域规范化,www 携带深度 NDC 标准化设备坐标 透视除法(除以 www) 坐标落入 [−1,1]3[-1, 1]^3[−1,1]3 屏幕空间 screen space 视口变换 映射到像素网格,进入光栅化 整...
透视校正插值
需要预先学习: 光栅化 纹理映射 坐标空间总览 屏幕空间插值的问题 纹理映射 介绍过属性插值:光栅化阶段用重心坐标(barycentric coordinates)把顶点属性(颜色、法线、UV)推算到三角形内部每个片元。纹理映射 的注里留了一个悬念:屏幕空间的重心坐标不能直接用于属性插值,透视投影下会产生扭曲。 先看症状。一块斜着放的地板(或墙),用均匀的棋盘格纹理铺满: 正确(透视校正) 错误(屏幕空间直接插值) ┌────┬──┬──┬────┐ ┌──┬──┬──┬──┬──┐ │ │ │ │ │ │ │ │ │ │ │ ← 远处:格子应越来越密 │ │ │ │ │ │ │ │ │ │ │ 却被均匀压缩 ├────┼──┼──┼────┤ ├──┼──┼──┼──┼──┤ │ │ │ │ │ │ │ │ │ │ │ │ │...
剔除总结
需要预先学习: 着色与渲染管线 深度缓冲 视图(摄像机)与投影 剔除解决什么问题 真实场景的三角形动辄百万千万,但屏幕只有几百万像素,一帧里真正可见的几何只占极小一部分。剔除(culling)就是在管线的不同阶段尽早把"反正看不见"的几何丢掉,让 GPU 不做无用功。剔除发生得越早,省下的越多:一个物体在 CPU 侧被剔除,顶点变换、光栅化、片元着色全省;一个片元在着色前被剔除,至少省下最贵的片元着色。 按发生的位置,剔除技术可以分层: 层次 执行者 粒度 代表技术 应用层 CPU 物体 / 实例 视锥剔除、遮挡剔除、小物体剔除 图形管线固定阶段 GPU 硬件 三角形 / 片元 裁剪、背面剔除、Early-Z GPU 计算阶段(现代) compute shader 物体 / 实例 / 三角形 Hi-Z 遮挡剔除、GPU 驱动管线 每一层的输入都是上一层筛剩下的,层层过滤。这篇笔记按"从物体到片元"的顺序把这些技术串起来。 视锥剔除(Frustum Culling) 视锥剔除(fru...
抗锯齿算法
需要预先学习: 光栅化 抗锯齿的分类 光栅化 的结尾简单列过几种反走样技术的名字,这篇笔记把它们逐个展开,讲清每种算法在管线的哪个位置工作、付出什么代价、换来什么样的边缘质量。 先回到问题的根源。几何边缘之所以产生锯齿,是因为"三角形边界"在数学上是一个无限陡的阶跃信号(step signal,从一个值瞬间跳到另一个值)——非黑即白,频率无穷高。任何有限的采样率都追不上它(奈奎斯特定理,见 光栅化),所以抗锯齿算法没有谁能"完全消除"锯齿,它们都在做同一件事:近似边缘在像素内的覆盖率。一个像素 70% 被三角形覆盖,理想的输出就是 70% 的三角形颜色混合 30% 的背景色,各种算法的差别只在"怎么知道是 70%"。 按"额外的采样从哪里来",主流算法分三类: 类别 思路 代表算法 超采样(supersampling) 增加每个像素的采样点数 SSAA、MSAA 后处理(post-process) 从渲染好的图像里检测并修复边缘 FXAA、MLAA、SM...
层次包围结构
需要预先学习: 光线追踪 剔除总结 层次包围结构 两个看似无关的问题,其实是同一件事: 剔除总结 里的视锥剔除,每帧要拿每个物体的包围体测六个平面——物体一多,CPU 逐个测试本身就成为开销; 光线追踪 里每条光线要与每个三角形求交——三角形数乘以光线数,直接相乘完全不可接受。 两者的共同点:绝大多数测试的结果都是"没有命中",但每一次"没有"都要花一次测试的代价换回来。层次包围结构(hierarchical bounding structure)的思路是让"没有"变得成片:把物体按空间位置组织成一棵树,每个节点用包围体框住它负责的所有内容;光线或视锥与某个节点的包围体不相交,整棵子树一次排除。单次查询的复杂度从 O(n)O(n)O(n) 降到 O(logn)O(\log n)O(logn)。 按"切的东西"不同,层次结构分两大族: 族类 切的是什么 代表结构 空间划分(spatial subdivision) 空间本身 均匀网格、八叉树、BS...


