跳到主要内容
腾讯题库 · Q06

原题 Q06 · 当前收录 5 / 49

学习状态 未开始

SSR 屏幕空间反射

Shader SSR · screen-space 对应课程:Shader 与材质

面试官问

PDF 第 13 页

请描述 SSR(屏幕空间反射)的实现流程,并说明其在移动端的性能瓶颈与优化手段。(常问题,理清逻辑)

学习进度

0/9 已勾

只保存在这台浏览器;勾满 9 项即视为本题完成。

先把题目说成人话

大厅的光滑地面本来照得到一根柱子;相机稍微转动,柱子离开画面边缘,倒影也跟着断掉。这里要解决的现实问题是:不用再渲染一遍完整世界,只借当前画面已有的颜色和远近记录,快速找到一份反射近似;同时承认画面外、遮挡后和背向相机的信息根本不在手里。

这种在当前画面里找反射的办法叫屏幕空间反射(Screen Space Reflection,简称 SSR)。记录每个像素最近表面远近的图叫深度缓冲(Depth Buffer),以相机为原点描述三维位置叫视图空间(View Space)。沿反射方向一小步一小步探测叫视线步进(Ray March),资料缺失时交给探针或环境贴图的答案叫备用方案(Fallback)。

这是虚构教学故事,不是个人项目经历。

场景:先让问题变得看得见

大厅地面反射着柱子。相机转动时,柱子一接近屏幕边缘,倒影就被切掉;相机看不到的柱背也不会出现在反射中。

SSR 通常读取相机颜色、深度和法线,并用投影矩阵在 view space 与 screen space 之间转换。若管线没有法线缓冲,就要额外生成或从 G-buffer 取得;因此“只需要三张图”不是跨管线的固定事实。

(右脑)画面:大厅里有一块擦得发亮的地砖,一根红柱完整地映在上面。你把相机慢慢转向右边,柱子本身刚刚离开画框,地上的倒影也突然断掉;可你站在现场,明明知道那根柱子还在原处。再往前走,另一根柱子被近处的墙挡住,它在地砖上的倒影也找不到了。倒影缺的不是光泽,而是这张画面已经看不见的东西。

(左脑)对表:核验对象是 view-space 起点、单位方向、步长、投影 UV、线性场景深度、射线深度与置信度。绿色命中、红色越界和黄色误穿是观察事实;“加步数就能补全反射”是假设,证据必须来自深度交叉 debug 与抓帧。所有深度必须采用同一正负号与单位,UV 只在 0–1 屏幕域内有效,厚度容差要与线性深度口径一致;颜色缓冲还可能缺少被遮挡或屏幕外信息。手电地图类比解释信息边界,但类比不等于命中正确。

小明:先形成一个完整猜测

小明认为把 SSR 提到全分辨率、增加模糊和步数,就能补回缺失的倒影。这能减轻锯齿和步进断层,却不能生成屏幕外的数据。

把屏幕边缘外的区域标红后,增加步数仍然 miss。这个失败把两类问题分开了:采样不足可以提高质量,信息不存在只能回退。

小红:把猜测放回现实检查

小红把射线经过的屏幕路径画出来:起点来自当前像素,方向由视线和法线决定;路径每走一段,就拿自己的深度去问深度图“这里有没有表面”。绿色表示找到候选表面,红色表示越界,黄色表示深度容差过大而穿到了错误物体。

她分别调整步数、厚度容差和分辨率。步数不足产生断层;容差过大容易穿墙或粘到错误表面;降分辨率会使细反射模糊。三种现象不能用同一个“质量”旋钮解释,屏幕外的红色区域更不能靠增加步数变绿。

我:用检查结果修正模型

我优先缩小使用范围:只让足够光滑、屏幕占比较大的反射面运行 SSR;粗糙或远处像素直接用探针。移动端再组合:

  • 半分辨率或棋盘格追踪,配合边缘感知上采样;
  • 较少的粗步进,加二分细化或 Hi-Z 粗到细查找;
  • 按 roughness、距离、屏幕边缘和材质 mask 提前退出;
  • 复用前一帧结果时加入运动向量、邻域钳制与失效处理,避免拖影;
  • 始终提供 Reflection Probe/cubemap fallback。

移动端瓶颈不只在 ALU。全屏读颜色、深度、法线和层级深度会产生带宽成本;循环步数带来纹理采样与分支发散;半分辨率则以稳定性和细节为代价。必须在目标设备的 Unity Player 构建中测量。

(右脑)画面修正:小明让射线多走几步,画框里面原本断续的倒影确实连贯了一些,可那根已经离开画框的红柱仍旧没有回来。小红把寻找过程显示出来后,才分清哪些路线真正碰到了表面,哪些只是穿到了错误物体,哪些已经走出屏幕。屏幕里找得不够细可以继续改进;屏幕根本没有记录的部分,只能让反射探针等备用结果接过去。

(左脑)严格对表:算法按序执行:由深度重建 PviewP_{view};把 NN 与指向相机的单位 VV 置于 view space;计算 R=reflect(−V,N)R=reflect(-V,N);沿 P+tRP+tR 步进并投影到 UV;若 UV 越界立即 miss;在线性深度上检测跨越并细化候选命中;通过法线、距离、边缘与厚度检查生成置信度;最后以置信度混合相机颜色和 Probe。粗糙表面还要按反射波瓣读取合适的颜色层级或采用锥形近似,单个清晰命中点不能自动代表粗糙反射。验收需分别记录 hit、错误命中、越界、步数、纹理读取与 GPU 时间,不能用最终倒影“看起来连续”替代这些条件。

老师解惑

实现流程按空间顺序展开:

  1. 从深度重建表面在 view space 的位置。
  2. 将法线变到同一空间,并约定 V 是“从表面指向相机”的 view-space 单位向量;因此入射方向是 -V,用 R=reflect⁡(−V,N)R=\operatorname{reflect}(-V,N) 得到从表面射出的反射方向。
  3. 沿 RR 前进,将样本投影到屏幕 UV。
  4. 比较射线深度与深度缓冲;跨过表面时判为候选命中,再用二分或更细步进收敛。
  5. 命中后读取相机颜色,并根据边缘、粗糙度、射线长度和法线一致性计算置信度。
  6. 越界、背面、遮挡或未命中时,混合 Reflection Probe、cubemap 或其他 fallback。
  • 深度命中:比较射线当前深度与场景深度,判断是否跨过已渲染表面。
  • 厚度容差:允许两份深度有小范围误差;太小容易漏,太大容易穿墙。
  • Hi-Z:深度金字塔让射线先跳过大块空区域,再逐级细化。
  • 置信度:把边缘、角度、距离和命中质量汇成权重,使 SSR 平滑过渡到 fallback。
  • 固有缺陷:屏幕外、遮挡后和背面没有可靠颜色,任何步数都无法补齐。

工具分工

  • SSR failure Lab:显示 hit、miss、越界和 fallback,建立屏幕空间边界直觉。
  • Unity Frame Debugger:确认 SSR Pass 读取、写入了哪些缓冲,以及合成顺序。
  • Unity Profiler(GPU Usage):在目标 Player 上测步数、分辨率和 temporal 路径的真实成本。
  • RenderDoc:查看深度层级、命中纹理和合成中间结果。

记忆钩子

SSR 是在当前屏幕地图上探路:命中取颜色,越界就回退;步数只能修采样,不能创造不存在的信息。

这是教学场景,不是目标项目的真实捕获或个人经历。

下一步:验证这条因果

让每次 SSR 失败都能归因到步进、深度或屏幕边界。

教学边界:它只验证这一条画面因果,不替代完整答案、项目经历或目标设备数据。

进入教学 Lab:「SSR 为什么会在屏幕边缘“凭空消失”?」