引擎中的平面反射(Planar Reflections in Our Engine)
现实场景中,光洁的地面、透亮的窗户总能吸引目光,游戏开发中我们常会模拟这类视觉效果。本引擎选用了一种实用且稳定的实现方案 ——平面反射(Planar Reflections)。本文将讲解平面反射的定义、引擎选用该方案的原因、具体实现方式,以及其他反射方案的适用场景。
什么是平面反射?
平面反射是指沿单一平面(如平整地面、窗户)渲染场景的镜像画面,核心是创建一个「镜像相机」,从反射面的另一侧拍摄场景。我们会将这个镜像视角的画面渲染到一张纹理中,绘制玻璃(或其他平面反射表面)时,对该纹理进行采样即可实现反射效果。
平面反射的适用场景
- 平面镜、平静的水面、抛光地面、玻璃幕墙等平面反射表面。
- 对反射效果有稳定性、高质量要求,且希望避免大量噪点、时间域不稳定性的场景。
平面反射的不适用场景
- 曲面、粗糙表面,这类表面需要全视角的光泽模糊效果。
- 任意反射方向的场景(如具有复杂微观几何结构的金属材质)。
引擎选用平面反射的原因
本引擎对反射方案的核心要求为:
- 易理解:仅需新增一个渲染通道、一张纹理,实现逻辑简洁。
- 确定性与稳定性:无画面「闪烁」问题,无需处理时间域累积带来的各类问题。
- 实用性:适配前向渲染器中存在单个主要反射面(玻璃、地面)的场景。
平面反射完美满足以上三点要求,且无需光线追踪硬件,能在不同性能的 GPU 上稳定运行、良好扩展。
引擎中的实现逻辑
我们仅在主渲染通道外新增一个轻量级渲染通道,并在主通道中增加一次简单的混合操作,即可实现平面反射:
镜像通道(离屏渲染)
- 将相机沿目标平面做镜像变换,计算得到镜像视图矩阵。
- 关闭(或调整)面剔除功能,将场景中的不透明物体渲染到反射纹理的颜色 + 深度渲染目标中。
- 完成同步操作,确保反射纹理能在后续通道中被正常采样。
主通道(原相机视角)
- 按常规流程绘制不透明物体与透明物体。
- 绘制玻璃时,采样反射纹理,并结合菲涅尔公式(Fresnel)、粗糙度(roughness) 以及用户可控的反射强度(reflection intensity),将反射效果与玻璃的着色效果混合。
整个实现无复杂的渲染图逻辑,无需光线查询,也无需时间域累积,核心逻辑简洁易懂。
核心镜像变换算法(简洁高效)
- 在世界空间中定义反射平面,公式为:ax+by+cz+d=0。
- 基于该平面构建反射矩阵 R,将原相机的视图矩阵与 R 相乘,即可得到镜像相机的视图矩阵。
- 实际实现中,需对镜像通道的面剔除进行调整(如设置
cullMode = none或翻转正面),因为反射变换会改变顶点的环绕顺序。
同时,我们会将反射平面参数传入着色器,实现可选的裁剪功能:通过将世界空间位置与平面参数进行简单的点积运算,即可在镜像通道中丢弃平面「后方」的片元,避免无效渲染。
详细渲染步骤
镜像通道
- 创建反射颜色纹理(格式与主通道匹配,确保合成 / 玻璃绘制通道可轻松采样)与反射深度纹理。
- 渲染前:通过 Vulkan Synchronization 2 的
vkCmdPipelineBarrier2,将反射颜色纹理的布局从SHADER_READ_ONLY_OPTIMAL转换为COLOR_ATTACHMENT_OPTIMAL;深度纹理同理,转换为DEPTH_ATTACHMENT_OPTIMAL。

