ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

屏幕空间反射(SSR)原理与实战:从射线步进到性能优化

屏幕空间反射(SSR)原理与实战:从射线步进到性能优化 1. 内容整体设计与思路拆解1.1 为什么屏幕空间反射成了实时渲染的“标配”我在做图形学相关项目时最早接触反射方案是从平面反射Planar Reflection开始的——那种方案只对完全平整的表面有效一旦模型换成凹凸不平的金属、湿润的地面、光滑的漆面平面反射立刻露馅。后来也试过预烘焙的环境贴图反射Cube Map Reflection静态场景还行但只要场景里出现动态物体比如一个移动的角色经过镜子前方反射贴图里的影像却还是旧的这就显得特别“假”。屏幕空间反射Screen Space Reflection简称SSR解决的核心问题是让反射内容跟画面实际渲染结果保持一致。它直接复用当前帧的颜色缓冲、深度缓冲和法线缓冲在屏幕像素这个二维空间里做射线步进沿着反射方向去“看”场景里有没有能反射的物体。换句话说画面里已经画出来的东西SSR才有机会反射出来画面外或者被遮挡的物体SSR管不到。这正是它与传统反射方案最大的不同。我在实际项目中体会最深的一点是SSR的定位从来不是为了“物理上精确”而是为了“视觉上可信”。它用极低的额外成本把反射效果从静态贴图升级为动态、实时的信息尤其在室内场景、潮湿路面、车辆漆面、道具金属边框这些局部高光区域观感提升非常明显。对实时渲染来说这个性价比几乎是无可替代的。1.2 整套算法的关键环节拆解SSR的全流程可以拆成几个相对独立又互相衔接的环节生成反射射线根据像素法线、视角方向和材质粗糙度计算反射方向或反射锥体。屏幕空间步进从当前像素出发沿着反射方向在屏幕UV空间里近似推进逐点比较深度找出射线与场景表面的交点。命中判定与内容获取确认射线击中某个屏幕像素后从颜色缓冲里取对应颜色再结合距离衰减、粗糙度做模糊或降权。边缘处理与Fallback射线步进到屏幕边缘或超出有效深度范围时需要跟环境贴图或低精度反射做平滑过渡避免反射突然消失。这一套流程里有三个最容易出问题的地方射线步进的步长选择、自相交self-intersection的规避、以及反射Ray Marching的采样质量。后面我会逐段展开结合参数和实际踩过的坑来说明。1.3 方案选型为什么要“在屏幕空间里做”这个问题的答案要从代价说起。传统反射方案要在渲染场景前先镜像摄像机再渲染一遍场景Draw Call直接翻倍而且复杂曲面反射还得切分多个镜像平面工程上很痛苦。光线追踪理论上最精确但实时光追在大多数平台上的帧预算仍然紧张全场景做反射更是奢侈。SSR选择的是一条中间路线不重新渲染也不处理场景几何体只在已经生成好的屏幕图像上“投机取巧”。这种思路有个很直观的类比就像你在办公室拍照想拍窗户玻璃上的倒影不需要把整栋楼都重新建模你只需要对着玻璃表面找找周围已经拍到的内容把合适的像素“搬”过来用。所以SSR本质上是一个图像空间的近似算法它的上限取决于当前帧里已经渲染出多少信息。SSR的典型应用场景包括第一人称射击游戏里的潮湿地板和金属武器反光、赛车游戏里的车身漆面反射、室内设计渲染器里的地砖倒影、CG预览工具里的动态物体反光。如果你在做一个画面的实时预览方案需要兼顾画质和性能SSR几乎是最适合优先上手的反射方案。2. 核心细节解析与实操要点2.1 反射射线的构建一切要从法线说起SSR的第一步是根据当前像素的世界坐标或观察空间坐标和法线算出反射射线的起点和方向。实际工程里这一步通常在延迟渲染的G-Buffer阶段就能拿到现成的数据。我们逐个梳理关键参数像素位置从深度纹理重建办法是把屏幕UV还原到裁剪空间再用逆投影矩阵变换到世界空间。这一步最常用的函数是ViewPosFromDepth(uv, depth)本质上是“逆变换”。法线从法线缓冲中读取需要确保法线是世界空间或观察空间且已经归一化后续反射方向计算才准确。视角方向从摄像机位置指向像素位置一般归一化成单位向量。反射方向标准公式是R reflect(V, N)也就是视角方向关于法线的镜面反射。它代表理想镜面反射的主方向。如果是粗糙表面单一光线不够表达真实物理效果。通常的做法是用一个反射锥体Cone来描述反射方向的范围或者做多次采样再模糊。工程上常用GGX重要性采样生成多条方向但这在实时SSR里开销偏大。折中方案是先按主反射方向做步进命中后进行模糊处理伪造出粗糙表面的散射观感。2.2 屏幕空间步进的数学基础屏幕空间步进是SSR最核心的循环。主流的实现方法是“逐步投射”从射线起点开始每步沿反射方向在屏幕平面UV空间移动固定距离或自适应距离然后取出该UV对应的深度值跟射线的深度做比较。射线在3D空间中的方程是P(t) Origin Dir * t。把这个点投影到屏幕空间得到UV坐标和深度。然后我们在这个投影后的位置采样深度纹理比较采样深度和射线深度如果射线深度大于场景深度表示射线穿入了物体表面内部视为命中如果射线深度小于场景深度表示射线还在物体前方或后方继续步进。这里有很多初学者容易踩坑的地方。比如UV移动量和实际3D位移之间不是线性关系尤其是射线方向接近屏幕边缘时透视投影会让UV坐标的移动非常不均匀。所以有经验的实现会采用“线性屏幕空间步进”或“带深度步进的自适应步长”保证近处和远处都不至于步进过大导致漏掉薄片物体。2.3 命中判定与自相交问题命中的判定并不是简单的“浅深比较”就完事。一次步进直接穿越一个薄片物体时离散采样很可能直接跳过命中。这就是常说的“漏检”。另一个反面问题是“自相交”射线刚出发不久由于深度缓冲精度有限射线深度与起点深度接近很容易误判为命中结果反射出自己脚下的颜色形成黑斑或彩色噪点。规避自相交常用三种手段深度偏移在射线起点沿法线方向推出一小段距离比如0.01到0.05个单位让射线离开表面一段安全距离再开始步进。这个偏移量需要根据场景尺度调太大会造成反射错位太小则无法避免自相交。射线起点高度剔除如果射线起点本身不在屏幕空间的表面之上而是浮空或嵌入几何体内部则直接放弃该像素的SSR改回环境贴图。厚壳检测步进时要求射线深度比场景深度大出一定阈值至少超过某个相对深度阈值才认为是有效命中避免在物体内部游荡时被误判。我在项目里常用的参数是厚度偏差Thickness Bias设为场景单位0.02自相交回避偏移Ray Origin Offset设为0.01两个参数配合使用。不同场景尺寸需要微调最好提供调试面板实时查看命中率。2.4 步长选择的艺术步长直接影响性能和漏检率。步长过大容易跳过细长物体而且在深度变化剧烈的区域比如轮廓边缘容易出现“闪烁”。步长过小步进次数暴增帧率骤降。工程上常用的自适应步长方案有固定步数方案设定最大步数如32步、64步、128步每步移动固定屏幕空间距离。简单易实现但远处细节丢失严重。线性步长方案每步的距离与射线深度的倒数挂钩使得屏幕空间中的移动更均匀——近处大深度移动慢远处移动快。这套方案在大多数引擎里都有优化版本。Hi-Z层级步进先构建深度金字塔从高层开始大步前进检测到相交再降到低层细化效率和精确度都高但实现复杂度明显提升。从性价比角度说如果项目性能预算紧张先用固定64步的线性步长实在不行再上Hi-Z。很多游戏的“终极画质”档位才会打开Hi-Z SSR就是这个原因。3. 实操过程与核心环节实现3.1 从G-Buffer准备到render target设置SSR的着色器通常以全屏Pass的方式挂在延迟渲染链路上。实操时我习惯分两步先生成射线方向纹理再执行步进着色器。原因是为了方便调试也方便单独对射线方向做模糊或限制。关键步骤是创建一张与屏幕等分辨率的纹理格式用R16G16B16A16_FLOAT用来存储反射射线方向和起点信息。浮点格式不能省因为方向向量都是小数。全屏顶点着色器里按屏幕UV重建世界坐标。这一步需要读深度纹理深度精度建议用D32F或D24S8精度越高自相交越少。像素着色器里采样法线计算反射矢量存入输出纹理。粗糙度比较大的像素可以直接在这里转向低精度Fallback路径。以下是典型的射线构建Shader伪代码HLSL风格核心是逆变换和反射计算float3 ReconstructViewPos(float2 uv, float depth) { float4 clipPos float4(uv * 2.0 - 1.0, depth, 1.0); float4 viewPos mul(ClipToView, clipPos); return viewPos.xyz / viewPos.w; } float3 ReconstructWorldPos(float2 uv, float depth) { float4 clipPos float4(uv * 2.0 - 1.0, depth, 1.0); float4 worldPos mul(ClipToWorld, clipPos); return worldPos.xyz / worldPos.w; } float4 MainPS(float2 uv : TEXCOORD0) : SV_Target { float depth DepthTex.SampleLevel(Sampler, uv, 0).r; if (depth 0.9999) return float4(0, 0, 0, 0); float3 worldPos ReconstructWorldPos(uv, depth); float3 normal normalize(NormalTex.SampleLevel(Sampler, uv, 0).rgb); float3 viewDir normalize(CameraPos - worldPos); float3 reflectDir reflect(-viewDir, normal); float roughness RoughnessTex.SampleLevel(Sampler, uv, 0).r; // 粗糙度过高直接标记为低置信度由后续Fallback处理 float hitConfidence (roughness 0.6) ? 0.0 : 1.0; return float4(reflectDir, hitConfidence); }3.2 步进循环的完整实现射线步进是SSR的主体。这个Pass要读取射线方向纹理、当前场景深度纹理以及颜色缓冲。我的习惯是把颜色缓冲提前拷贝一份用于最终采样避免和当前Pass产生依赖链冲突。步进循环可以使用RayCast函数它接收起点、方向、最大步数返回UV坐标和命中深度。一个经典的实现如下基于线性屏幕空间步进fixed4 SSRPass(float2 uv) { float3 rayDir RayDirTex.SampleLevel(Sampler, uv, 0).rgb; float confidence RayDirTex.SampleLevel(Sampler, uv, 0).a; float depth DepthTex.SampleLevel(Sampler, uv, 0).r; float3 worldPos ReconstructWorldPos(uv, depth); float3 startPos worldPos GetNormal(uv) * RAY_ORIGIN_OFFSET; float3 endPos startPos rayDir * MAX_RAY_LENGTH; float4 startClip mul(WorldToClip, float4(startPos, 1.0)); float4 endClip mul(WorldToClip, float4(endPos, 1.0)); float2 startUV startClip.xy / startClip.w * 0.5 0.5; float2 endUV endClip.xy / endClip.w * 0.5 0.5; float startDepth startClip.z / startClip.w; float endDepth endClip.z / endClip.w; float2 uvStep (endUV - startUV) / MAX_STEPS; float depthStep (endDepth - startDepth) / MAX_STEPS; float2 currentUV startUV; float currentDepth startDepth; for (int i 0; i MAX_STEPS; i) { currentUV uvStep; currentDepth depthStep; if (currentUV.x 0 || currentUV.x 1 || currentUV.y 0 || currentUV.y 1) { return FALLBACK_REFLECTION; } float sceneDepth DepthTex.SampleLevel(Sampler, currentUV, 0).r; float depthDiff currentDepth - sceneDepth; if (depthDiff 0 depthDiff THICKNESS_BIAS) { float2 hitUV currentUV; float3 hitColor ColorTex.SampleLevel(Sampler, hitUV, 0).rgb; float fade 1.0 - saturate(distance(currentUV, uv) * edgeFade); return float4(hitColor * fade, confidence * fade); } } return FALLBACK_REFLECTION; }这里有三个细节需要重点解释第一RAY_ORIGIN_OFFSET的取值。在实际项目里我把它设为一个相对值跟场景距离相关比如0.01 * LinearEyeDepth(depth)。这样做的好处是摄像机拉远或近时偏移量都能保持合理的屏幕尺寸。第二深度比较的“厚度偏差”THICKNESS_BIAS。它决定了射线与表面相交的容忍度。通常取值为0.01到0.1之间因为我用了相对深度裁剪空间z/w所以这个值代表视锥深度方向的相对偏差。出现漏检时适当调大出现自相交时调小。第三命中后的边缘衰减edgeFade。屏幕边缘或UV接近0/1的像素射线容易跳到屏幕外这时如果不做淡出反射会像刀切一样断掉。通常做法是取uv到屏幕边缘距离的最小值映射到0到1之间再做pow调整衰减曲线。3.3 命中结果与颜色采样的细化实际项目中一次步进的命中结果往往不够稳定。常见的做法是围绕命中点做多次偏移采样然后取平均这相当于一个简单的抗锯齿。以命中点为中心做4个像素偏移的四次采样能够显著减少高光区域的闪烁。更进一步项目里我常会用颜色缓冲的mipmap来模拟粗糙反射。命中后直接采样颜色纹理的某个Mip层级粗糙度越高采样的MipLevel越大模糊度自然增加。这是从UE4、Unity HDRP的SSR实现里学到的思路很适合在低成本下模拟材质粗糙度的观感变化。举个例子如果材质的roughness为0到1我会把MipLevel映射为roughness乘6这样粗糙度为0.5的材质反射会采样到Mip3层画面看起来有一种“磨砂感”比单纯做模糊要好得多。优化到这里反射的整体效果已经能看。但接下来的采样质量、时间稳定性、性能消耗仍然有一堆问题要处理。4. 采样策略与画质优化的实战方案4.1 为什么会出现“噪点”和“闪烁”SSR的画面问题主要有三类。第一类是步进精度不足导致的“条纹断裂”第二类是每帧采样方向不同导致的“时间闪烁”第三类是UV边缘采样导致的“边缘渗色”也就是反射到某一块颜色完全不应该出现的区域。要理解为什么会有闪烁得从SSR的逐像素过程去想每个像素独立发射一条或多条射线没有跨像素的关联。如果某帧射线恰好命中一个亮斑下一帧采样方向抖动后没有命中亮斑就消失了。这个现象在金属表面高光和光源反射部分尤其明显。行业里通用的做法包括使用屏幕空间抖动Blue Noise替代随机采样让误差分散到高频噪声而后再做空间模糊使用时间累积Temporal Accumulation把历史帧的结果缓存起来做加权平均显著提升稳定性使用多个射线按G buffer的粗糙度做重要性采样配合多帧累积。4.2 抖动采样与Blue Noise在步进之前给射线方向添加一个小幅度的抖动让相邻像素的射线方向不同这样原本精度不足造成的“颗粒感”会变成均匀的噪声分布而噪声在后续模糊处理中更容易被抹平。我推荐使用预生成的Blue Noise纹理因为它具有高频均匀分布的特性能够避免普通白噪声在低频上的不均匀。每帧需要偏移纹理坐标例如blueNoiseUV uv * NoiseScale randomOffset这样可以保证每帧的噪声图案都在变化时间累积时更容易收敛。抖动幅度一般设为反射锥角的十分之一到五分之一。如果幅度过大反射会变得模糊过小则无法有效掩盖步进误差。具体数值需要按场景和材质反复试。4.3 时间重投影与累积时间累积的做法是把上一帧的SSR结果保存在一张独立的纹理里在当前帧计算完新的SSR结果后用运动向量把上一帧的SSR结果投影到当前像素位置然后按6:4或7:3的权重跟当前帧结果混合。但时间累积不是无脑混合它有几个坑场景中发生剧烈变化的区域比如新出现的遮挡物运动向量无法正确对齐容易产生“鬼影”。摄像机快速旋转时投影后的上一帧信息可能落在完全不相干的位置。解决方案是在混合前做“邻域钳制”对上一帧结果在当前像素邻域内计算颜色均值和方差把历史值钳制在一个可信范围内超出范围的直接丢弃。这一步说白了就是防止半透明的“拖影”污染画面。我在项目里实现了一套简化的Temporal SSR步骤是用运动向量纹理把上一帧SSR结果重投影到当前帧在3x3邻域内对历史值做AABB钳制用当前帧命中置信度做自适应混合置信度低的像素多采信历史值输出到当前帧SSR缓存。这样改完反射的闪烁至少降低80%视觉上的稳定感一下子就有了。4.4 基于粗糙度的渐进式模糊粗糙度是调节SSR表现的关键维度。光滑表面roughness接近0应该呈现清晰锐利的镜面反射粗糙表面roughness接近0.3到0.6则应该模糊、散射极粗糙表面roughness大于0.6一般直接放弃SSR改用漫反射IBL或环境贴图。我实践下来比较好用的映射规则是粗糙度范围SSR策略采样处理0 ~ 0.1全分辨率射线单点采样或4次偏移采样镜像清晰0.1 ~ 0.3全分辨率射线采样Mip1~2保留部分高频过渡自然0.3 ~ 0.6半分辨率射线采样Mip2~4模糊效果明显节省性能0.6 ~ 1.0不使用SSR使用环境贴图或预过滤镜面IBL粗糙度引发的另一个问题是反射亮度衰减。物理上粗糙表面反射的能量更分散高光峰值降低所以SSR命中后的颜色需要乘以菲涅尔项和粗糙度相关的能量系数避免表面看起来“过曝”。5. 性能分析与常见问题排查实录5.1 性能开销的量化分析SSR的性能开销主要集中在步进循环上。步数每翻一倍着色器执行时间差不多也翻一倍。在1080p分辨率下固定64步的朴素实现在中等显卡上每帧大约消耗2到3毫秒降到32步能压到1.2到1.8毫秒而Hi-Z层级步进在同样画质下通常能控制在1毫秒左右。半分辨率优化是性价比最高的手段。具体操作是把射线纹理的分辨率设为屏幕分辨率的一半在最终合成时再上采样到全分辨率。付出的代价是反射边缘会有些许锯齿但配合Temporal累积和模糊肉眼几乎察觉不到。我在项目中经常直接采用“半分辨率32步Temporal累积”的组合画质接近全分辨率性能却节省了一半以上。此外针对动态物体多的场景可以使用“深度阈值提前终止”步进过程中如果发现射线深度已经超出最大反射距离就直接返回Fallback结果节省掉后续所有步进迭代。5.2 经典问题边缘断裂与反射跳变边缘断裂是最常见的SSR问题之一。表现是反射区域边缘出现明显的锯齿或断裂尤其是场景里的细长物体栏杆、树枝、电线在反射中时断时续。原因是投影后的UV采样比较粗糙射线步进在这些物体的窄细区域时每步移动的屏幕距离过大直接跨越了整个细长物体。解决思路有三条一是降低每步的屏幕空间步长让射线在小物体上也能命中二是使用深度金字塔碰到深度突变区域就自动降级至更细层级三是在最终合成阶段对SSR结果做边缘检测和形态学修复把断裂处的缝隙填平。我实际项目中多数是先用方案一把步数从32提高到48就能解决大部分断裂再辅以方案三的简单修复观感就很OK了。5.3 经典问题屏幕边缘反射突然消失射线步进到屏幕外时因为没有任何深度信息SSR无法继续。这个问题的观感表现为反射图案一到屏幕边缘或画面角落就“硬切”成环境贴图非常突兀。处理办法包括边缘淡出根据射线终点或命中点离屏幕边缘的距离设置一个透明度衰减带。扩展屏幕区域步进时用边缘Clamp采样让射线“滑”到边缘后再退出而不是直接判定失败。Fallback混合Hit失败后用射线方向去采样环境贴图再按距离或屏幕边缘度混合让反射没有跳变。5.4 经典问题自相交黑斑与荧光色伪迹自相交黑斑的根源我在2.3节提过是射线起点和表面深度太接近造成误命中。实战中如果只加固定偏移还不够可以再加一层“像素深度差”检查如果当前步进采样的场景深度和射线起点的场景深度过于接近说明射线可能还在同一表面上游弋不视为命中。荧光色伪迹多半来自颜色缓冲里的HDR值命中一个极亮的Bloom源或高光点时SSR会把整个颜色搬过来导致反射区域出现超纯白或彩色光斑。解决方法是做颜色钳制把采样颜色的亮度限制到合理范围同时结合粗糙度降低其权重。以下是我整理的排查速查表直接对着表格查就行问题表现可能原因解决方案反射区域黑斑射线起点与表面深度过近增加Ray Origin Offset、降低Thickness Bias反射闪烁采样方向无抖动或缺少时间累积启用Blue Noise抖动开启Temporal累积边缘断裂步长过大或深度缓冲精度不足增加步数或改用Hi-Z步进屏幕边缘硬切射线出屏未做衰减增加边缘淡出与Fallback混合反射偏亮或偏暗缺少菲涅尔衰减或能量系数添加Fresnel项和粗糙度加权反射内容错位射线起始点偏移过大减小Ray Origin Offset检查法线空间一致性性能开销高步数过多或全分辨率计算改为半分辨率减少步数开早退5.5 调试技巧把中间量可视化遇到疑难杂症我建议先把中间量可视化出来而不要盯着最终画面瞎猜。可以分别查看射线方向纹理R、G、B通道观察反射方向是否连续合理命中计数纹理如果大片区域命中数为0说明步进范围或深度比较参数有问题置信度纹理粗糙度高的区域应显示低置信度如果不是检查粗糙度传递链路最终Fallback权重确认混合权重是否平滑。这些中间量的可视化用RenderDoc设几个断点就能搞定排查效率能提升好几倍。6. 一个完整的实战调优案例6.1 场景复现潮湿路面反射我拿一个常见的场景举例雨天城市道路路面积水需要很强的SSR反射来体现路面湿润感。刚开始加SSR时画面效果很差路面反射一直在闪烁远一点的车辆反射完全断裂而且性能从3毫秒直接飙到6毫秒。排查过程是这样的。先看射线方向纹理发现路面像素的反射方向普遍朝上因为法线朝上这是合理的。再看命中计数远处的车辆命中率很低原因是步数32在远距离上每步的屏幕移动量太大。然后我把步数提到64命中率有明显提升但性能又回到了5毫秒附近。最后决定采用半分辨率计算性能降到1.8毫秒左右闪烁问题则靠Temporal累积解决。这个案例说明SSR的调优不是单纯加参数而是在步数、分辨率、时域稳定性之间做取舍是一个系统工程。6.2 参数推荐与调节顺序根据项目经验我提供一个比较通用的参数起点可结合场景微调参数推荐值说明最大步数32 ~ 64低端平台32高端平台64步进分辨率半分辨率1080p以下建议半分辨率Ray Origin Offset0.01 × 线性深度按场景尺度换算Thickness Bias0.02 ~ 0.08出现自相交时调小边缘淡出宽度0.05UV范围控制反射消失的平滑度时间累积权重0.6 ~ 0.7太高容易鬼影太低不降噪调节顺序建议是“先步长、后偏移、再增采样”。先把步数调到肉眼看不到断裂再调节起点偏移和厚度偏差到没有自相交最后再开Temporal和模糊优化稳定性。不要一上来就调模糊否则会掩盖掉真实的步进问题。6.3 SSR与其他反射技术的组合SSR并不是万能的。对于屏幕外的物体反射、被遮挡物体反射、半透明物体身后的反射SSR天生无能为力。成熟的商业引擎通常把SSR放在反射链路的第一层第二层是区域平面反射Planar Reflection第三层是预计算的Cubemap或Sphere Reflection最后用距离或粗糙度做权重混合。组合方式可以参考近距离、光滑表面SSR占主要权重因为它最符合当前帧动态中距离、粗糙表面反射细节模糊SSR权重降低Cubemap权重上升远距离反射像素太小直接使用预过滤环境贴图省下性能这个“LOD式”的权重分配既能保持动态物体在反射中的正确性又不会暴露SSR在远距离上的低精度问题。7. 写在最后的一些经验SSR这个技术刚学的时候感觉原理不难无非是“从像素出发反射、步进采样、取色返回”但真正放到项目里跑起来各种边缘情况会让初学者头大。我经历过最难忘的一次调试是某个金属雕像表面一直出现一圈黑色的轮廓排查了两天才发现是法线贴图的切线空间转换问题——法线方向偏了一点点整个反射方向全乱了。后来我养成了一个习惯任何反射类效果出问题先检查法线是否可靠、是否和场景法线空间一致这能过滤掉一半的坑。第二个习惯是永远保留一个“调试可视化”的开关不追求最终画面的完美而是先把每一条中间管线看到清清楚楚。第三个习惯是做SSR调优时别只看静态画面一定要让摄像机动起来因为静态画面稳定不代表动态时没有闪烁和断裂。如果你正要给自己的引擎或渲染器接入SSR建议按这个顺序来先实现朴素的屏幕空间步进确认基础反射正确再逐步加入自相交规避、粗糙度模糊、边缘淡出最后再上Hi-Z和时间累积这类重型优化。每一步都能看到明确效果提升而且每一步都有独立的调试入口。屏幕空间反射是一个典型的“用有限信息换来强视觉表现”的图形学方案在实时渲染领域会继续发光发热。这篇内容基于我多年做渲染器和游戏画面的实际工作方式希望对你手上的项目有参考价值。
返回列表