每日编程实践: Atmosphere Scattering Renderer — 基于物理的大气散射天空渲染
一、背景与动机为什么需要大气散射?在任何真实感渲染系统中,天空颜色都是最重要的环境光照来源之一。如果使用固定颜色或简单渐变来近似天空,会出现以下明显问题: 日落无法自然呈现:简单渐变无法模拟太阳低仰角时光线穿越大量大气层产生的橙红色 天顶到地平线的色调变化缺失:天顶处散射路程短、蓝光多;地平线处散射路程长、偏橙黄 无法与场景光照自洽:天空颜色决定环境光颜色,必须与物理散射一致才能保证整体协调 风格化场景也依赖可控参数:游戏中外星环境、奇异星球的天空必须能通过调参实现 工业界实际使用场景实时渲染(游戏引擎):Unreal Engine 4/5 的 SkyAtmosphere 组件、Unity 的 Physical Based Sky,都基于 Bruneton 等人提出的预计算大气散射(Precomputed Atmospheric Scattering)。在 GPU 上通过预计算透射率 LUT 和多重散射 LUT,实现实时的天空颜色查询。 离线渲染(VFX/影视):RenderMan、Arnold、Mantra 等渲染器均内置大气散射模型,用于电影级天空渲染...
每日编程实践: Disney Principled BRDF Renderer
背景与动机在现代实时渲染和离线渲染领域,材质系统是最核心的模块之一。早期的渲染系统采用 Phong 或 Blinn-Phong 模型,这些模型参数对美术不直观——改变”高光指数”时,你无法直觉判断结果。而且这些模型能量不守恒:高光加上漫反射经常超过入射光能量,导致不真实感。 2012 年,Burley 在 SIGGRAPH 发表了《Physically-Based Shading at Disney》,提出了 Principled BRDF(原则性双向反射分布函数)。这个模型的核心哲学不是追求”物理精确”,而是追求对艺术家直觉友好 + 足够真实: 对艺术家友好:所有参数都在 [0, 1] 范围内,语义直观(metallic=0 是绝缘体,=1 是金属) 能量守恒:漫反射和镜面反射之和不超过入射能量 适用广泛:皮肤、金属、玻璃、布料,用同一套参数系统搞定 工业界使用场景Disney BRDF 或其变体已经被以下系统采用: Unreal Engine 4/5:Epic 改良了 Disney BRDF,称为”UE4 Shading Model”,是 ...
每日编程实践: BVH Accelerated Triangle Mesh Renderer
每日编程实践: BVH 加速三角形网格光线追踪渲染器 Cornell Box 场景,三个物体:镜面 icosphere、漫反射球、蓝色圆环,右侧高盒。BVH 加速,3354 三角形,64 SPP,800×600,16 秒完成。 ① 背景与动机:为什么需要 BVH?朴素光线追踪的性能瓶颈最早学路径追踪时,场景里只有隐式球体。球体求交极快——一个方程解出来就是结果。但真实世界的模型都是三角网格:一个低精度的人物模型有 5000-50000 个三角形,高精度角色可能达到 100 万三角形,游戏场景的所有物体加起来轻松超过 1000 万三角形。 朴素做法:遍历场景里每一个三角形,对每条光线求交。设场景有 N 个三角形,图像有 P 个像素,每像素 S 个样本,每样本光线弹射 D 次。总求交次数 = P × S × D × N。 以今天的场景为例: P = 800 × 600 = 480,000 像素 S = 64 samples per pixel D = 6 弹射深度 N = 3,354 三角形(已经很少了) 朴素求交次数...
每日编程实践: SDF Ray Marching Renderer
背景与动机在渲染领域,我们通常用三角形网格来表示几何体。一个球体需要几百个三角面才能看起来圆润,一个复杂的有机形状(角色的脸部、液体、云朵)则需要数万甚至数百万个三角形,且对它们做 CSG 布尔运算(切割、合并、交集)在实时场景中代价极高。 有向距离场(Signed Distance Field, SDF) 提供了一种完全不同的几何表示方式:不存储顶点,而是用一个数学函数 f(p) → d 来描述空间中任意点 p 到曲面的最近距离 d。当 d < 0 时点在物体内部,d > 0 时在外部,d = 0 恰好在表面。这个简洁的定义带来了一系列天然的好处: 精确的 CSG 操作:两个 SDF 做并集只需 min(d1, d2),交集用 max,差集用 max(-d1, d2),数学上完全精确,没有多边形法的数值误差 平滑过渡:引入平滑最小值函数可以让两个物体无缝熔合,一行代码就能做到有机体般的流动感 无限分辨率:SDF 是解析函数,不管你离多近,表面永远是精确的数学曲线,不会出现网格锯齿 简化渲染管线:不需要顶点缓冲、光栅化、深度测试——Ray Marching 算法直接...
每日编程实践: Caustics Renderer — 光子映射焦散渲染
每日编程实践:Caustics Renderer — 光子映射焦散渲染今天的项目是用**光子映射(Photon Mapping)算法渲染焦散(Caustics)**效果——玻璃球折射光线在漫反射地板上形成的亮斑图案。这是计算机图形学中最经典且最难正确实现的效果之一,纯路径追踪需要极长时间才能收敛,而光子映射可以在有限时间内产生可辨识的焦散图案。 一、背景与动机什么是焦散?焦散(Caustics)是光线通过折射或反射介质后聚焦在漫反射表面形成的亮斑。游泳池底部的波纹光斑、玻璃杯旁的彩色光圈、钻石的内部闪烁——都是焦散现象。 从物理角度看,焦散是辐照度场的高频信息:光子从光源出发,通过玻璃的斯涅尔折射改变方向,最终在某块漫反射地面上”聚堆”,该区域接收到的光通量远高于周围,形成明亮的光斑。 为什么纯路径追踪很难渲染焦散?想用路径追踪渲染焦散,渲染器需要从相机出发,击中地板,然后经历以下路径才能采样到焦散贡献: 1相机 → 地板 → 玻璃球(进入)→ 玻璃球(离开)→ 光源 这条路径称为 LS²DE 路径(Light-Specular-Specular-Diffuse-Eye)。问题...
每日编程实践: Procedural Texture Synthesis — 程序化纹理合成
今日主题:程序化纹理合成——用数学公式而非美术资产生成无限变化的有机纹理。核心技术:Worley Noise(细胞/Voronoi 噪声)+ Fractal Brownian Motion(分形布朗运动),生成大理石、木纹、熔岩、有机细胞六种纹理。 ① 背景与动机为什么需要程序化纹理?传统美术流程中,纹理贴图是美术师手工绘制的图像文件——一张 2048×2048 的漫反射贴图占用 16MB(未压缩)。大型开放世界游戏中,仅地形纹理就可能高达几十 GB。这带来三个核心痛点: 1. 存储瓶颈《巫师 3》的纹理资产超过 30GB。玩家在下载时要忍受漫长等待,主机平台更是受限于蓝光盘容量。而程序化纹理只需存储几十行数学参数,运行时实时生成任意分辨率的纹理。 2. 分辨率无关性手绘纹理存在物理分辨率上限——放大 4 倍就会看到像素化。程序化纹理本质是连续函数,理论上可以无限放大,细节永远清晰。《No Man’s Sky》的整个星球纹理系统就建立在这一特性上。 3. 参数化变化手绘大理石纹理要变色,需要美术师重新画一张。程序化纹理只需修改一个颜色参数,立刻生成新变体。这使得实...
每日编程实践: HDR Tone Mapping & Color Grading
背景与动机每一帧游戏画面的最后一道工序,往往最不起眼,却决定了一切的观感——色调映射(Tone Mapping)。 问题的根源:显示器不是眼睛现实世界的亮度范围极为宽广。晴天户外的直射阳光亮度约为 100,000 cd/m²,室内阴影处可能只有 0.01 cd/m²,动态范围高达 10 个数量级(100dB)。人眼通过瞳孔收缩和视杆/视锥细胞的自适应,能感知约 20 个 EV(Exposure Value)的动态范围。 然而,家用显示器的 SDR(Standard Dynamic Range)面板通常只能显示 0.1 到 300 cd/m² 的亮度,不足 3 个数量级。HDR 显示器能达到 1000–4000 nits,也仅覆盖 4 个数量级。 一个物理正确的渲染器(Path Tracer、光栅化 + 全局光照),输出的是线性光照空间的辐射值,可以包含从 0.001(深阴影)到 20+(太阳直射)甚至更高的值域。如果我们直接把这些值限制在 [0, 1] 范围内显示,高光区域全部爆掉变成一片白,暗部细节也因非线性感知而全部消失。 色调映射就是...
每日编程实践: Depth of Field Renderer — 薄透镜景深渲染器
每日编程实践: Depth of Field Renderer — 薄透镜景深渲染器今天实现了一个基于薄透镜模型(Thin Lens Model)的物理正确景深渲染器,配合路径追踪产生自然的散景(Bokeh)效果。 ① 背景与动机为什么景深很重要?如果你玩过《赛博朋克 2077》《荒野大镖客2》或《对马岛之魂》,一定注意过游戏里的镜头语言——当主角站在前景,背景的城市灯光化成一圈圈朦胧的光晕,这就是景深(Depth of Field, DoF)效果。这个效果来自真实相机和人眼的光学特性:光圈有物理尺寸,只有焦平面附近的物体才能在感光元件上聚焦成点,其他距离的物体会形成弥散圆(Circle of Confusion),俗称散景(Bokeh)。 没有景深的渲染器——包括我们之前做的大多数渲染器——使用的都是**针孔相机(Pinhole Camera)**模型。针孔相机假设光圈无穷小,所有距离的物体都完全清晰。这对学习渲染基础够用,但就视觉真实性而言有明显缺陷: 画面缺乏层次感:前景、中景、背景同样清晰,空间深度感弱 缺少导演感:真实摄影中,摄影师用景深引导观众视线——清晰的地方...
每日编程实践: Motion Blur Renderer — 速度缓冲与后处理运动模糊
一、背景与动机运动模糊:人眼的物理现实我们的眼睛(以及相机的感光元件)在曝光期间并不是瞬间”拍一张照”,而是在一段时间窗口内持续积累光信号。当物体在这段时间内发生运动时,不同时刻的光信号叠加在同一感光区域,就产生了运动模糊(Motion Blur)。 如果没有运动模糊,画面会呈现出一种不自然的”抖动感”。这在早期电子游戏和动画中尤为明显——每一帧都是完全锐利的静止图像,但一旦物体快速运动,视觉反而会感到不舒适。这个现象被称为频闪效应(Stroboscopic Effect)。 运动模糊的本质是对快门开放时间段内的图像进行时间积分: $$I_{blurred}(x, y) = \frac{1}{T} \int_0^T I(x, y, t), dt$$ 其中 $T$ 是快门时间,$I(x, y, t)$ 是 $t$ 时刻像素 $(x, y)$ 处的颜色值。 工业界实际应用运动模糊在以下场景中必不可少: 游戏引擎:虚幻引擎(Unreal Engine)、Unity 的 HDRP 都内置了后处理运动模糊。典型实现是速度缓冲(Velocity Buffer)+ 后处理 Pass,...
每日编程实践: Temporal Anti-Aliasing (TAA) — 从0实现时序抗锯齿
每日编程实践: Temporal Anti-Aliasing (TAA)① 背景与动机锯齿是什么,为什么它顽固存在当你在游戏里靠近一堵砖墙,看着瓷砖边缘闪烁跳动;或者远处树木的枝条变成一片乱码噪点——这就是走样 (Aliasing) 的典型场景。 走样的根本原因是采样定理:以有限分辨率的像素网格对连续的几何场景采样,必然会丢失高频细节。一条对角线在像素网格上只能用”阶梯状”近似,这就是几何走样。 传统的解决方案: MSAA (Multisample AA):每像素采集多个子样本,但只对几何边缘有效,对着色走样(Shader Aliasing)无效。而且随着延迟渲染的普及,MSAA 的 G-Buffer 内存开销变得难以承受。4x MSAA = G-Buffer 内存×4。 SSAA (Supersample AA):以更高分辨率渲染再降采样。质量最好但开销也最大,渲染代价与超采样倍数成正比。 FXAA (Fast Approximate AA):后处理边缘模糊,便宜但会过度模糊纹理细节,丢失锐利感。 TAA(Temporal Anti-Aliasing,时序抗锯齿)...










