NOTE前五部分把离线渲染、几何、动画、物理四根主轴讲完了。这部分走现代前沿——实时渲染里真正在跑的东西(PBR 管线、硬件光线追踪、GPGPU),以及还在研究前线的东西(神经渲染、XPBD、SPH)。主题很多,但它们之间都是前五部分积木的组合:PBR = Part 4 的渲染方程 + GGX;RTX = Part 4 求交 + BVH 的硬件化;NeRF/3DGS = 把 Part 4 的体积渲染微分化。每节都会指出它靠的是前面哪一块。
目录
- 实时渲染优化 — LOD / 视锥剔除 / 遮挡查询 / 实例化批处理
- 基于物理的渲染 (PBR) — Cook-Torrance 完整实现 / 能量守恒 / IBL
- 硬件光线追踪(RTX / DXR) — 加速结构 / 着色器表 / 去噪 / ReSTIR
- 体积渲染与参与介质 — 体渲染方程 / Ray marching / 云
- GPGPU 与计算着色器 — 工作组模型 / 并行前缀和 / GPU 粒子
- 神经渲染 — NeRF / 3D Gaussian Splatting
- 现代物理仿真 — XPBD / SPH / FEM 概览
三十一、实时渲染优化
WARNING实时引擎的帧预算只有 16 ms(60 FPS)或 8 ms(VR/120 Hz)。靠”算得快”走不远,真正的瓶颈永远是少算、不算、批着算。三板斧:LOD(远处少算)、剔除(不可见不算)、实例化(同东西一次算完)。
31.1 层次细节(LOD)
31.1.1 距离与屏幕空间误差
距离 LOD 是最朴素的做法:
但真正正确的指标是屏幕空间误差——远处一个三角形投影到屏幕只占 0.1 像素时,简化它眼睛察觉不到:
其中 为焦距、 为像素尺寸、 为视距。 由 QEM 简化(Part 3 §15.3)给出——这是 Part 3 的网格简化直接产出的量。
31.1.2 LOD 链的构造
// 连续 LOD 链:每级三角数减半,误差单调上升struct LODLevel { Mesh mesh; float distance_threshold; // 切换阈值 float geometric_error; // 世界空间误差};
class LODChain { std::vector<LODLevel> levels_;public: void build(const Mesh& hi, int num_levels) { levels_.resize(num_levels); levels_[0] = { hi, 0.f, 0.f }; for (int i = 1; i < num_levels; ++i) { // Part 3 §15.3 的 QEM 简化 levels_[i].mesh = qem_simplify(hi, std::pow(0.5f, i)); levels_[i].geometric_error = hausdorff(hi, levels_[i].mesh); levels_[i].distance_threshold = 10.f * std::pow(2.f, i); } } const Mesh& pick(float distance) const { for (int i = levels_.size() - 1; i >= 0; --i) if (distance >= levels_[i].distance_threshold) return levels_[i].mesh; return levels_[0].mesh; }};TIPNanite(UE5)彻底放弃离散 LOD 链,改成 cluster hierarchy:网格切成 128 面的 cluster,运行时按屏幕像素误差在 BVH 里选一个切面。但底层的简化算法仍然是 QEM 的现代变体。
31.2 视锥剔除
从 MVP 矩阵 抽出六个平面:
用 AABB 的 p-vertex / n-vertex 技巧避免 8 个顶点全测:
bool aabb_in_frustum(const AABB& box, const Plane frustum[6]) { for (const auto& pl : frustum) { // 选 p-vertex:沿法线方向最远的 AABB 角点 Eigen::Vector3f p; p.x() = pl.n.x() >= 0 ? box.max.x() : box.min.x(); p.y() = pl.n.y() >= 0 ? box.max.y() : box.min.y(); p.z() = pl.n.z() >= 0 ? box.max.z() : box.min.z(); if (pl.n.dot(p) + pl.d < 0) return false; // 完全在外侧 } return true;}复杂度:每对象 6 次点积,O(n)。进一步用 BVH 层次剔除可降到 O(log n)。
31.3 遮挡查询与 Hi-Z 剔除
视锥剔除只排除”看不到的方向”。被前景挡住的物体同样浪费:
- 硬件遮挡查询(
GL_SAMPLES_PASSED):上一帧绘制一次代理几何体,统计通过深度测试的片元数。延迟一帧,容易卡渲染流水线。 - Hi-Z(Hierarchical Z):深度缓冲按 mipmap 降采样,每级存最大深度。测一个 AABB 遮挡只需从对应 mip 读一个 texel 对比——GPU 一次 tex lookup 搞定,现代引擎(Frostbite/UE)的主力。
31.4 实例化批处理
同一网格 + 不同变换 的场景(树林、砖墙、雪粒子),用实例化把 N 次 draw call 压成 1 次:
// 每实例 attributes:model_matrix(16B×4) + color(16B)glBindBuffer(GL_ARRAY_BUFFER, instance_vbo);glBufferData(GL_ARRAY_BUFFER, N * sizeof(InstanceData), data, GL_DYNAMIC_DRAW);for (int i = 0; i < 4; ++i) { // mat4 拆 4 个 vec4 glEnableVertexAttribArray(3 + i); glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE, sizeof(InstanceData), (void*)(i * sizeof(float) * 4)); glVertexAttribDivisor(3 + i, 1); // divisor=1 → 每实例更新}glDrawElementsInstanced(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, 0, N);NOTEDX12 / Vulkan 还有 Indirect Draw + Mesh Shader,把 draw call 参数也放 GPU buffer,由 compute shader 生成,CPU 端零 draw call——这是 Nanite/GPU-Driven 渲染的基础。
三十二、基于物理的渲染(PBR)
32.1 为什么叫”基于物理”
经验模型(Phong,见 Part 4 §18.3)能调出好看的反光,但:
- 不能量守恒 — 能量可以凭空变多
- 不互易 — 交换入射出射方向结果不一样(违反 BRDF 必要条件)
- 参数不可迁移 — 一盏灯调好换一盏就假
PBR 从微表面物理模型出发,保证:
- 能量守恒:
- 亥姆霍兹互易:
- 参数来自实测:粗糙度、金属度可以扫描测量
32.2 Cook-Torrance BRDF(完整)
Part 4 §18.4 给过轮廓。这里给完整公式 + GLSL 实现,做为 Part 6 的 canonical 参考。
32.2.1 微表面模型
把表面看作无数法向分布的微小镜面。对方向 ,只有法向恰好等于半程向量 的微面才贡献镜面反射。
三项分别刻画:
- — 法向分布 有多少微面的法向朝向
- — 菲涅尔 这些微面反多少
- — 几何项 微面之间自遮挡
32.2.2 GGX/Trowbridge-Reitz 法向分布
选 GGX 是因为它长尾——能画出粗糙金属的”柔高光”,Disney 2012 论文之后成为事实标准。
32.2.3 Smith 几何项
直接光的 ;IBL 的 (Disney 经验值)。
32.2.4 Schlick 菲涅尔近似
= 基础反射率。非金属 (几乎无色);金属 (带色反光)—— 这就是金属度参数的来源。
32.2.5 漫反射与能量守恒
关键点: 由 决定 —— 镜面反多少,漫反射就少多少,自动能量守恒。金属 metallic = 1 时 ,只有镜面反射。
📌 完整 GLSL 片段着色器
const float PI = 3.14159265359;
// ---------- 三个核心函数 ----------float D_GGX(float NdotH, float alpha) { float a2 = alpha * alpha; float x = NdotH * NdotH * (a2 - 1.0) + 1.0; return a2 / (PI * x * x);}
float G_SchlickGGX(float NdotX, float k) { return NdotX / (NdotX * (1.0 - k) + k);}
float G_Smith(float NdotV, float NdotL, float alpha) { float k = (alpha + 1.0) * (alpha + 1.0) / 8.0; // 直接光 return G_SchlickGGX(NdotV, k) * G_SchlickGGX(NdotL, k);}
vec3 F_Schlick(float VdotH, vec3 F0) { return F0 + (1.0 - F0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);}
// ---------- 单光源 PBR ----------vec3 pbr_direct(vec3 N, vec3 V, vec3 L, vec3 albedo, float roughness, float metallic, vec3 radiance){ vec3 H = normalize(V + L); float NdotV = max(dot(N, V), 0.0); float NdotL = max(dot(N, L), 0.0); float NdotH = max(dot(N, H), 0.0); float VdotH = max(dot(V, H), 0.0);
vec3 F0 = mix(vec3(0.04), albedo, metallic); float alpha = roughness * roughness;
float D = D_GGX(NdotH, alpha); float G = G_Smith(NdotV, NdotL, alpha); vec3 F = F_Schlick(VdotH, F0);
vec3 specular = (D * G * F) / max(4.0 * NdotV * NdotL, 1e-4);
vec3 kS = F; vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic); // 能量守恒 vec3 diffuse = kD * albedo / PI;
return (diffuse + specular) * radiance * NdotL;}TIP对多光源,循环累加
pbr_direct即可。IBL 部分不是简单的加法——需要分离漫反射和镜面两路预积分,见下一节。
32.3 基于图像的光照(IBL)
为什么不能直接对环境贴图做蒙卡尔洛积分?因为每个片元都要采几百次,实时承受不住。IBL 的核心是预积分 + 分离近似:
32.3.1 漫反射辐照度贴图
对环境贴图做 的预卷积,结果仍是一张立方体贴图(通常 32×32 足够,因为漫反射高频信息低)。运行时采一次。
32.3.2 镜面反射:分离近似(Split-Sum Approximation)
Epic 2013:
- 预过滤环境图:对不同
roughness做 importance sampling GGX,结果存成 mipmap 链(粗糙度越高采样到越高 mip)。 - BRDF LUT:只依赖 的二维查表。
运行时 IBL 只需 3 次贴图采样:
vec3 IBL(vec3 N, vec3 V, vec3 albedo, float roughness, float metallic) { vec3 F0 = mix(vec3(0.04), albedo, metallic); vec3 R = reflect(-V, N); float NdotV = max(dot(N, V), 0.0);
vec3 kS = F_Schlick(NdotV, F0); vec3 kD = (1.0 - kS) * (1.0 - metallic);
vec3 irradiance = texture(u_irradianceMap, N).rgb; vec3 diffuse = irradiance * albedo;
// 粗糙度 → mip level float mip = roughness * (MAX_PREFILTER_MIP - 1.0); vec3 prefiltered = textureLod(u_prefilterMap, R, mip).rgb; vec2 brdf = texture(u_brdfLUT, vec2(NdotV, roughness)).rg; vec3 specular = prefiltered * (F0 * brdf.x + brdf.y);
return kD * diffuse + specular;}NOTEDisney Principled BSDF(2012)在 Cook-Torrance 之上加 sheen / clearcoat / subsurface,UE / Blender / Substance 都基于它的 11 参数体系。原理相同,只是多了几层 BRDF 加和。
三十三、硬件光线追踪(RTX / DXR)
WARNINGPart 4 讲的是离线光线追踪,每秒可能跑几 fps。RTX 把加速结构构建 + 求交 + BVH 遍历全搬到 GPU 专用硬件单元(RT Core),让实时光追成为现实。API 层面 NVIDIA OptiX / Microsoft DXR / Vulkan KHR Ray Tracing 三家路线几乎同构。
33.1 管线架构
硬件光追管线有 5 种着色器,代替传统管线的 VS/FS:
| 着色器 | 作用 | 触发时机 | Ray Generation | 发射光线(相当于主循环) | 每像素一次 |
|---|---|---|---|---|---|
| Intersection | 自定义图元求交(三角形以外) | BVH 遍历到叶子 | Any-Hit | alpha 裁剪等”要不要采纳这次命中” | 每次可能命中 |
| Closest-Hit | 最近命中着色(= Part 4 的 shade) | BVH 遍历结束 | Miss | 未命中(= 背景 / 环境贴图) | 遍历结束且无命中 |
33.2 两级加速结构(TLAS / BLAS)
- BLAS(Bottom-Level):每个网格一个 BVH,存三角形。静态网格可预构建。
- TLAS(Top-Level):场景中所有实例(BLAS + 世界变换)的 BVH。动态物体每帧刷新。
分两级是因为:动态场景里只重建 TLAS 就够(O(实例数)),不用重建每个几何体的 BVH(O(三角数))。
// DXR 伪代码D3D12_RAYTRACING_GEOMETRY_DESC geom = {};geom.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES;geom.Triangles.VertexBuffer = ...;geom.Triangles.IndexBuffer = ...;
// BLAS:一次构建dev->GetRaytracingAccelerationStructurePrebuildInfo(&blas_inputs, &blas_info);cmd->BuildRaytracingAccelerationStructure(&blas_build, 0, nullptr);
// TLAS:每帧刷新for (auto& inst : scene_instances) { D3D12_RAYTRACING_INSTANCE_DESC& d = tlas_instances[i]; d.AccelerationStructure = inst.blas_gpu_va; memcpy(d.Transform, inst.world_matrix, sizeof(float) * 12); d.InstanceMask = 0xFF; d.InstanceContributionToHitGroupIndex = inst.material_id;}33.3 着色器表(Shader Binding Table)
硬件光追怎么知道命中某个三角形后跑哪个 Closest-Hit?靠一张 SBT 表:material_id → 着色器索引。这就是 Part 4 §19 BVH 叶子节点的硬件一般化版本。
33.4 低采样去噪(核心难题)
实时光追每像素只能发 1–4 条光线(否则帧率不够)。方差巨大,需要时空重建:
- SVGF(Schied 2017):空间双边滤波 + 时域复用,主流离线电影也在用
- ReSTIR(Bitterli 2020):重采样——对每像素维护一个小的候选”重要光路”蓄水池,邻居互相借光样本,等效采样数翻倍。是 Cyberpunk / Alan Wake 2 路径追踪的关键。
- 神经去噪(OIDN / OptiX Denoiser)
吃 (noisy RT, albedo, normal) 输出干净图,细节最多。
WARNINGReSTIR / SVGF 都假设场景时序稳定——快速相机旋转、disocclusion 会破坏时域复用,导致”鬼影 / 拖影”。工程上要配 motion vector + depth 判据回退到空间滤波。
33.5 动态全局光照(DDGI / ReSTIR GI)
实时 GI 的主流方案:
- DDGI(Dynamic Diffuse Global Illumination,Majercik 2019):场景里布一张稀疏的 probe 网格,每 probe 存一张 8×8 的辐照度图。着色时三线性插值。UE5 Lumen 的 diffuse GI 近亲。
- ReSTIR GI:把 ReSTIR 的蓄水池思路扩展到路径追踪 —— 整条路径(而不只是一次直接光采样)加入蓄水池。
三十四、体积渲染与参与介质
NOTE体积渲染不只是”画云”。CT/MRI 医学影像、光雾、次表面散射(皮肤/蜡/叶子)全是它的应用。核心方程把 Part 4 的渲染方程从”表面上一次事件”推广到”光沿路径的连续积分”。
34.1 体渲染方程
光线 在介质里的辐射度:
- 透射率 (Beer-Lambert 定律)
- 消光 (吸收 + 散射)
- 入散射
- 相位函数 常用 Henyey-Greenstein:
34.2 Ray Marching 求解
实时里几乎全用黎曼和离散化——均匀步长沿光线采样:
Eigen::Vector3f ray_march(const Ray& ray, float t_min, float t_max, const Volume& vol, const std::vector<Light>& lights) { Eigen::Vector3f L = Eigen::Vector3f::Zero(); float T = 1.0f; // 透射率 float dt = 0.1f; int steps = int((t_max - t_min) / dt);
for (int i = 0; i < steps; ++i) { float t = t_min + i * dt; Eigen::Vector3f p = ray.o + t * ray.d; float density = vol.sample(p); // 从 3D 纹理采样 if (density <= 0) continue;
float sigma_t = vol.extinction * density; float dT = std::exp(-sigma_t * dt); // 本步透射率衰减
// 直接光采样(只对每盏光算一次阴影 ray march) Eigen::Vector3f Ls = Eigen::Vector3f::Zero(); for (const auto& light : lights) { Eigen::Vector3f L_dir = (light.pos - p).normalized(); float T_light = shadow_transmittance(p, light.pos, vol); // 同法 float cos_theta = (-ray.d).dot(L_dir); float phase = henyey_greenstein(cos_theta, vol.g); Ls += light.color * T_light * phase * vol.scattering * density; }
L += T * (1.0f - dT) * Ls; // 本步入散射贡献 T *= dT; if (T < 0.01f) break; // 早期终止 } return L;}34.3 云渲染的工程技巧
离线的 ray march 每像素几百步。游戏要实时,靠这些 trick:
- 3D 噪声纹理
+ Perlin 混合做基础云密度,天气贴图做宏观覆盖率 - 大步长 + 抖动:64–128 步配合蓝噪声抖动起点,消除带状条纹
- 双尺度:低分辨率(1/4 分辨率)体 ray march + 高分辨率 upsample
- Horizon Zero Dawn 2015 GDC 演讲是云渲染的经典资料
TIP体渲染和 NeRF 本质一样 —— 都是”沿光线累积密度加权辐射”。NeRF 的不同只是把 从采样纹理换成查询 MLP。
三十五、GPGPU 与计算着色器
35.1 工作组模型
GPU 把线程按三层组织:
- Thread:最小单位,一段 shader
- Work Group(本地工作组):
local_size_{x,y,z},共享一块片上 shared memory(~48 KB),可 barrier 同步 - Dispatch:
num_groups_{x,y,z},一次glDispatchCompute
关键约束:同组内可同步,跨组不能。要跨组同步必须额外发起 dispatch。
#version 430layout(local_size_x = 16, local_size_y = 16) in;layout(rgba32f, binding = 0) uniform image2D img;
shared float tile[16][16]; // 组内共享
void main() { ivec2 gid = ivec2(gl_GlobalInvocationID.xy); // 全局坐标 ivec2 lid = ivec2(gl_LocalInvocationID.xy); // 组内坐标
tile[lid.y][lid.x] = imageLoad(img, gid).r; barrier(); // 组内同步
// ... 用 tile 做 blur / reduce}35.2 并行前缀和(Scan)
Prefix sum 是无数并行算法的基石——stream compaction、排序、BVH 构建全要用。Blelloch 算法 复杂度 深度:
输入 [3, 1, 7, 0, 4, 1, 6, 3]Upsweep(树形归约): 步长 1: [3, 4, 7, 7, 4, 5, 6, 9] 步长 2: [3, 4, 7,11, 4, 5, 6,14] 步长 4: [3, 4, 7,11, 4, 5, 6,25] # 根为总和末位清零 → [3, 4, 7,11, 4, 5, 6, 0]Downsweep(反向散播): 输出 [0, 3, 4,11,11,15,16,22] # exclusive scanWARNING算法核心:先算总和、再反向分发。比朴素的 的 Hillis-Steele 算法工作总量少一半。
35.3 GPU 粒子系统
全 GPU 粒子
// ========= Update Pass =========layout(std430, binding = 0) restrict buffer Particles { Particle p[]; };layout(std430, binding = 1) restrict buffer Counters { uint alive_count; uint dead_count; uint alive_list[];};
uniform float dt;uniform vec3 gravity;
void main() { uint i = gl_GlobalInvocationID.x; if (i >= p.length()) return;
Particle pt = p[i]; pt.life -= dt; if (pt.life > 0.0) { pt.vel += gravity * dt; pt.pos += pt.vel * dt; p[i] = pt; uint slot = atomicAdd(alive_count, 1u); // 原子分配 alive_list[slot] = i; } else { atomicAdd(dead_count, 1u); // 回收 }}要点:
atomicAdd做无锁计数分配alive_list只收集还活着的粒子索引,后续 draw 用 indirect draw 读这个 list 长度- 发射 pass 从
dead_count回收死粒子槽位,避免动态增长
三十六、神经渲染
WARNING神经渲染不是”画个图让 GAN 生成”。真正的 NeRF 和 3DGS 的数学基础仍然是体积渲染方程——它们只是把”场景表示”从传统几何+贴图换成了可微分的参数化结构。
36.1 NeRF(Mildenhall 2020)
核心思路:用 MLP 把 3D 位置 + 视角方向映到密度 + 颜色:
渲染时对光线做 ray marching(和 §34 一样):
监督信号:多视图照片。对渲染的像素 与真实照片 做 L2 loss,反向传播穿过整条 ray march 回到 MLP 权重。因为 ray march 是纯可微分加权和,梯度可传。
关键工程点
- 位置编码 让 MLP 能学高频细节
- 分层采样:粗网络预测密度 → 重要性采样细网络,避开空间中大量空白
- 慢:原版 NeRF 单场景训练 1–2 天。加速版 Instant-NGP(Müller 2022)用多尺度哈希网格,5 秒训练
36.2 3D Gaussian Splatting(Kerbl 2023)
把场景表示为上百万个显式 3D 高斯,每个高斯有位置 、协方差 、不透明度 、球谐 RGB。
核心优势:
- 光栅化而非 ray march:每帧把所有高斯投影到屏幕,按深度做 blend。GPU 友好。
- 显式 —— 可编辑、可动画、可导出。神经网络只用在训练优化中,推理零 MLP。
- 质量 > NeRF,速度 >> NeRF(100 FPS vs 0.1 FPS)
屏幕空间投影:3D 高斯 投影到 2D 后仍是高斯(仿射变换保高斯性):
是投影矩阵的雅可比, 是 view 矩阵。
渲染(前向合成):把所有高斯按深度排序,前到后合成:
TIP3DGS 的优化用 自适应致密化:梯度大的高斯分裂、位置近的合并、不透明度太低的删掉。这让百万高斯的数量可以动态调整,对高频细节自动变多、平坦区域自动变少。
36.3 对比
| NeRF | Instant-NGP | 3D Gaussian Splatting | 表示 | MLP 隐式 | 哈希网格 + 小 MLP | 显式高斯点云 | |
|---|---|---|---|---|---|---|---|
| 训练 | ~24 h | ~5 s | ~30 min | 推理 | 0.1 FPS | ~10 FPS | ~100 FPS |
| 可编辑 | 难 | 难 | 容易 | 底层 | 体渲染方程 | 体渲染方程 | 屏幕空间 alpha blend |
三十七、现代物理仿真
37.1 XPBD(eXtended PBD)
Part 5 §29.2 的 PBD 好用但刚度非物理——同样的 换迭代次数就软硬不同。XPBD (Macklin 2016) 把拉格朗日乘子作为显式状态:
其中 是柔度(compliance),直接对应物理弹性。 退化为 PBD。刚度从此和迭代次数解耦,UE5 Chaos Cloth / Houdini Vellum 都基于它。
37.2 SPH(Smoothed Particle Hydrodynamics)
把流体离散为粒子,用核函数加权平均邻居估计场变量:
常用 Poly6 / Spiky 核。离散 Navier-Stokes(Part 5 §30.2):
每项梯度/拉普拉斯用 的导数解析表达,纯局部计算,GPU 友好。
工程流程:
- 空间哈希 bucketize 粒子(Part 5 §27 的邻居查找)
- 每粒子求邻居 → 估计密度
- 由状态方程 算压强 → 压强梯度力
- 加粘性、表面张力、重力
- 积分(辛欧拉 / Verlet,见 Part 5 §28)
PBF(Position Based Fluids,Macklin 2013)把 SPH 的”压强力”改写成 PBD 风格的密度约束投影,稳定性和大步长都更好,是 Houdini / Flip Fluids 的主流。
37.3 FEM(有限元)概览
弹簧质点把物体离散为质点+弹簧,FEM 离散为四面体单元,每个单元上定义位移插值。应力-应变关系(线弹性):
是拉梅常数(可由杨氏模量 、泊松比 推出)。Co-rotational FEM 把旋转部分先通过 polar decomposition 提取出来,避免大变形下线性弹性的”体积爆炸”。
Corotated / Stable Neo-Hookean / ARAP 是业界主力的超弹性模型,Ten Minute Physics / PhysX / Chaos 都在用。
尾声:把六部分拼起来
| Part | 工具 | 在 Part 6 里被谁复用 |
|---|---|---|
| Part 2 光栅化 | 透视除法 / 深度测试 | §31 剔除 / §36 3DGS 投影 |
| Part 4 光线追踪 | 渲染方程 / BVH / 蒙卡洛 | §32 PBR / §33 RTX / §34 体渲染 / §36 NeRF |
| Part 6 前沿 | 上面的全部组合 | — |
NOTE六部分笔记的主线其实很简单:前五部分造积木,Part 6 搭城堡。真正难的不是任何单个算法,而是知道在什么场景下选哪个积木。下次看到一个新论文(比如”基于 Gaussian Splatting 的实时 relighting”),第一反应应该是:这是把 §36 的 3DGS 和 §32 的 PBR 拼起来了。积木法一分解,就不神秘了。
延伸阅读清单
| 主题 | 推荐 |
|---|---|
| 光线追踪 | 《PBRT》第 4 版 / Ray Tracing Gems I & II(免费 PDF) |
| 神经渲染 | NeRF / Instant-NGP / 3D Gaussian Splatting 三篇原论文 |
| GPU 架构 | 《GPU Gems》1-3 卷(免费) / 《Real-Time Collision Detection》 |
- 完 —