公司动态

从公式到代码:VCTRenderer 体素锥追踪算法的完整指南

📅 2026/8/21 14:24:23
从公式到代码:VCTRenderer 体素锥追踪算法的完整指南
从公式到代码VCTRenderer 体素锥追踪算法的完整指南【免费下载链接】VCTRendererA real time global illumination solution that achieves glossy surfaces, diffuse reflection, specular reflection, ambient occlusion, indirect shadows, soft shadows, emissive materials and 2-bounce GI. Published here http://ieeexplore.ieee.org/abstract/document/7833375/项目地址: https://gitcode.com/gh_mirrors/vc/VCTRenderer体素锥追踪Voxel Cone TracingVCT是近年来实时渲染领域最具突破性的全局光照算法之一而 VCTRenderer 正是这套算法的开源完整实现。它以延迟体素着色Deferred Voxel Shading为核心在 GPU 上实时呈现光泽表面、漫反射、镜面反射、环境光遮蔽、间接阴影、软阴影、自发光材质甚至二次反弹全局光照2-bounce GI配套论文已发表于 IEEE。本文将从渲染方程出发一步步拆解 VCTRenderer 如何把抽象的全局光照公式变成一套可运行、可复现的 GPU 着色器代码。为什么需要体素锥追踪传统渲染的痛点传统光栅化管线一次只处理一个方向的入射光阴影要靠阴影贴图、间接光只能靠环境光贴图假装。要还原真实的光线反弹离线渲染会使用路径追踪但一张图往往要渲染几分钟甚至几小时。体素锥追踪的核心洞察非常巧妙把场景预计算成体素网格再把成百上千条光线打包成一个锥去采样。锥在行进过程中直径不断增大配合体素纹理的 mipmap 级别自动扩大采样范围——用一次采样代替无数次光线求交把指数级的光线数压缩成几十次纹理采样从而在普通显卡上跑出实时的全局光照。一张图看懂VCTRenderer 的四步渲染流水线整个算法可以浓缩为四步体素化 → 体素光照 → 各向异性过滤与体素 GI → 逐像素锥追踪。下面这张官方管线示意图能帮你建立整体印象对应到工程代码这四步分别在engine/assets/shaders/下的voxelization.*、inject_radiance.comp、inject_propagation.comp、light_pass.frag等着色器文件中实现由engine/assets/code/renderers/gi_deferred_renderer.cpp统一调度。接下来我们逐一拆解。第一步体素化——把场景装进乐高积木体素化是 VCTRenderer 的基石。场景里的三角形网格会被离散化到一张三维纹理中每个格子体素像一块乐高积木记录这片空间的材质信息。体素结构三张 3D 纹理受延迟渲染 G-Buffer 的启发项目把三种属性分开存储voxelAlbedo反照率/颜色、voxelNormal法线、voxelEmission自发光。每张都是独立的三维纹理用imageAtomicRGBA8Avg原子操作做平均写入保证多个三角形同时落入同一体素时结果正确。这套结构在 inject_radiance.comp 顶部就能看到对应的声明。保守体素化不放过任何细节普通体素化容易漏掉细薄几何体项目采用了保守体素化几何着色器 voxelization.geom 会按三角形法线选择最佳投影轴向并外扩三角形包围盒半个体素确保薄片、边缘也被完整覆盖。动态场景方面工程用一张flag volume标记静态体素每帧只清空并重写动态物体部分大幅降低了动态体素化的开销。下图是体素化后的 Cornell Box 对比可以清楚看到红墙、绿墙被离散成彩色立方体网格第二步体素光照——逐体素计算直接光照体素化完成之后VCTRenderer 在计算着色器 inject_radiance.comp 中为每一个体素计算直接光照输出到辐射度体radiance volume。这一步兼容平行光、点光、聚光灯且不强制依赖阴影贴图。法线加权衰减公式解决平均法线失真体素内所有几何法线被平均后可能指向一个不存在的方向导致光照计算错误。论文提出的解法是法线加权衰减Normal-Weighted Attenuation先计算每个轴面的法线衰减 D再用法线平方作为权重合成W N² Vr Li × (ρ/π) × (Wx·Dx Wy·Dy Wz·Dz)其中 Li 是光源强度ρ 是体素反照率N 是体素平均法线。这套公式在inject_radiance.comp的BRDF()函数中与代码一一对应比普通 Lambert 模型在锐利边缘的表现好得多。软体素阴影与自发光体素还可以直接用来算阴影从体素位置向光源方向逐格步进遇到遮挡就累积遮挡值ν ν (1 − ν) × κ / tκ 是碰撞系数、t 是已行进距离。碰撞越多、距离越近阴影越深天然形成柔和的软阴影连点光源、聚光灯这种没有阴影贴图的光源也能获得遮挡信息。最后直接把体素化的自发光值加到辐射度上albedo.rgb emissive就能模拟霓虹灯、屏幕等自发光物体。下图是法线体素数据的可视化每个体素用 RGB 编码法线方向第三步各向异性体素与第一次 GI 反弹直接光照做完VCTRenderer 还要为锥追踪准备过滤后的数据并预先算好第一次间接漫反射。各向异性体素六个方向的 mipmap普通 3D 纹理的 mipmap 会把四周颜色均匀混合导致漏光。项目采用 Crassin 提出的各向异性体素用 aniso_mipmapbase.comp 和 aniso_mipmapvolume.comp 生成六个方向体正负 X/Y/Z 各一张半分辨率锥沿哪个方向采样就按轴权重线性插值这三个方向的结果有效抑制了漏光和锯齿。体素 GI预计算第一次反弹计算着色器 inject_propagation.comp 在每个体素上以平均法线为轴发射4 个扩散锥用蒙特卡洛近似把渲染方程的半球积分拆成若干锥积分之和锥的贡献按立体角加权PI/3、2·PI/9等权重直接写在源码里。结果乘以反照率后加回辐射度体就得到了第一反弹间接漫反射再做一次各向异性过滤后最终逐像素锥追踪时就能顺带看到第二反弹——这也是项目能实现 2-bounce GI 的原因。第四步核心算法——体素锥追踪的公式与代码现在进入重头戏逐像素的体素锥追踪实现在延迟光照着色器 light_pass.frag 的TraceCone()函数中。锥的几何孔径、直径与 mip 级别一个锥由起点 Co、方向 Cd、孔径角 θ 决定。行进距离 t 处的锥直径以及该直径对应的 mipmap 级别由两条公式给出d 2t × tan(θ/2) V_level log2(d / V_size)锥越粗t 越大采样的 mip 级别越高、数据越糊这恰好模拟了光线随距离扩散、细节衰减的物理特性。代码里float mipLevel log2(diameter / voxelWorldSize)正是这条公式的直接翻译。前到后合成体积累加的经典公式每一步从各向异性体采样到颜色 c₂ 与遮挡 α₂用体积渲染标准的前到后合成更新结果c α·c (1 − α)·α₂·c₂ α α (1 − α)·α₂对应代码中一行coneSample (1.0f - coneSample.a) * anisoSample;——简洁得惊人这正是体素锥追踪的魅力复杂的全局光照浓缩成几十次循环与一次纹理采样。一个锥的代码骨架while(coneSample.a 1.0f dst maxDistance) { float diameter 2.0f * aperture * dst; // 锥直径随距离增大 float mipLevel log2(diameter / voxelWorldSize); // 由直径选 mip 级 vec4 anisoSample AnistropicSample(coord, weight, face, mipLevel); coneSample (1.0f - coneSample.a) * anisoSample; // 前到后合成 dst diameter * samplingFactor; // 大步前进 }由锥衍生出的四大效果间接漫反射沿法线半球均匀分布 6 个约 60° 孔径的大锥加权求和即得间接漫反射镜面反射沿反射方向发射 1 个窄锥孔径由高光指数决定指数越高锥越细、反射越锐利。下图中的金属球与镜面地板就是实时锥追踪出来的高光反射环境光遮蔽AO复用漫反射锥只累加遮挡项并用衰减函数加权δ δ (1−δ)·α₂·f(r)其中 f(r) 1/(1λr)λ 控制衰减速度软阴影从表面点向光源方向打一个锥孔径越大阴影越柔和从 1° 到 25° 孔径可以无缝控制阴影软硬。效果一览这些画面都是实时算出来的把四步串起来VCTRenderer 就能在普通显卡上渲染出接近离线渲染的画面中世纪的龙像大厅里光在墙壁、旗帜和地面之间真实反弹自发光佛像把暖色光晕染到周围物体上发光的球体既能照亮场景也能产生带色彩的间接阴影。性能表现1920×1080 下稳定超过 30 FPS论文在 AMD R9 380 上对三个经典场景做了压测Cornell Box、Sibenik 大教堂、Crytek Sponza在 1920×1080 分辨率、物体与光源每帧动态更新包括动态体素化、体素 GI、各向异性过滤的极限工况下单帧耗时约 17~18 ms平均帧率超过 30 FPS。场景直接光照间接光照漫反射镜面合计全动态更新Cornell Box1.03 ms7.27 ms7.67 ms17.81 msSibenik 大教堂1.32 ms7.62 ms8.32 ms17.60 msCrytek Sponza1.34 ms7.81 ms8.41 ms16.97 ms工程还内置了非常实用的优化手段动态更新可改为每 3~5 帧执行一次视觉上几乎无差别却能轻松把帧率推到 60 FPS 以上并且光照变化时无需重新体素化只有物体移动才需要更新体素。动手体验编译并运行 VCTRenderer想亲手跑起来非常简单仓库已内置全部依赖GLEW、GLFW、GLM、Assimp、FreeImage、TBB、OGLplus 等git clone https://gitcode.com/gh_mirrors/vc/VCTRendererWindows 下直接用VCTRenderer.sln打开编译运行后按Shift F切换自由相机模式想深入阅读代码建议按这个顺序engine/core/引擎基类与渲染器抽象→engine/rendering/窗口与界面渲染→engine/scene/场景、相机、灯光、材质→engine/assets/code/各阶段着色器程序与渲染器。总结从公式到代码其实只隔一层锥回顾全文VCTRenderer 用四个精心设计的步骤把渲染方程变成了可交互的实时画面体素化把连续几何变成离散数据体素光照把直接光写进辐射度体各向异性过滤与体素 GI预计算第一次反弹最后逐像素锥追踪用一两个公式完成从采样到合成的全部工作。论文里那些看似高深的公式落到代码里往往只有一两行。对于想学习实时全局光照的开发者VCTRenderer 是一份极其珍贵的公式 ↔ 代码对照教材——渲染方程、BRDF、体积累加、锥追踪都能在这里找到最直观的实现。希望这篇指南能帮你少走弯路更快地读懂并驾驭这套优雅的算法。【免费下载链接】VCTRendererA real time global illumination solution that achieves glossy surfaces, diffuse reflection, specular reflection, ambient occlusion, indirect shadows, soft shadows, emissive materials and 2-bounce GI. Published here http://ieeexplore.ieee.org/abstract/document/7833375/项目地址: https://gitcode.com/gh_mirrors/vc/VCTRenderer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考