FidelityFX Variable Shading 示例深度解析:可变速率着色(VRS)从原理到实现

📅 发布时间:2026/10/4 10:02:18
FidelityFX Variable Shading 示例深度解析:可变速率着色(VRS)从原理到实现
图形学游戏开发【免费下载链接】dlssg-to-fsr3Adds AMD FSR 3 Frame Generation to games by replacing Nvidia DLSS Frame Generation (nvngx_dlssg).项目地址https://gitcode.com/gh_mirrors/dl/dlssg-to-fsr3点击查看免费下载本篇指南围绕 AMD FidelityFX SDK 的 Variable ShadingVRS示例展开系统讲解其运行需求、六个核心 UI 控制项、计算着色器生成 ShadingRateImage 的完整数据流以及基于ffx_vrs.h的上下文创建与调度 API 用法。读者读完可以掌握 FidelityFX VRS 1.2 在 Cauldron 框架内的落地方式并能据此在自己的 DX12 / Vulkan 应用中接入 Tier 1 / Tier 2 可变速率着色。示例概览与运行需求FidelityFX Variable Shading 示例演示了 FidelityFX VRS 计算算法的实际应用——它通过分析上一帧颜色缓冲与当前帧运动矢量生成一张按屏幕 Tile 指定着色速率的 VRS 控制图像ShadingRateImage从而在不明显损失画质的前提下削减像素着色器执行次数。示例截图见 示例截图截图展示了 VRS 控制图像叠加在渲染场景上的效果。该示例的运行需求如下操作系统Windows图形 APIDirectX(R) 12图形 APIVulkan(R)关于 VRS 效果底层的完整技术原理可参考配套的 技术文档示例在 SDK Samples 体系中的位置见 示例索引。为什么需要可变速率着色与 MSAA 的类比在渲染一帧时以高分辨率逐像素着色是所有像素工作量的主要瓶颈。高分辨率渲染带来的一个副作用是同一图元内相邻片元的世界空间位置通常非常接近相邻像素着色器执行的输出颜色往往也高度相似。而几何边缘通常比图元内部携带更多重要视觉信息——MSAA 正是利用这一点让像素着色器执行次数保持在较低水平。FidelityFX VRS 提供了类似的手段当多个相邻像素被同一图元覆盖时只执行一次像素着色器并复用其输出。这可以显著减少渲染一帧所需的 ALU 与带宽开销在不明显损失画质的前提下提升帧率省下的 GPU 时间可挪用于对最终画面影响更大的效果。一个直观的理解方式是与 MSAA 对照如果 4K 帧全程使用 2x2 VRS 渲染其计算量与最终画质大致相当于开启 4x MSAA 渲染 1080p 图像。但 VRS 更进一步允许对画面不同区域进行更细粒度的着色速率控制。适合降低着色速率的场景技术文档 variable-shading.md 列举了多个不会明显损伤画质的适用场景远处物体通常由几何轮廓而非纹理细节决定观感且远处像素的颜色差异常被雾、大气效果或半透明几何如粒子系统进一步抹平已知处于景深之外、或将被运动模糊、泛光等后处理效果模糊化的物体被雾、水、磨砂玻璃等半透明介质扭曲或模糊的物体细节方差极小的物体如卡通渲染或处于场景极暗区域如环境光微弱时物体的未受光/阴影部分快速运动的场景中高帧率与低输入延迟更重要而微小细节不易被玩家察觉可激进使用 VRS。此外通过眼动追踪注视点渲染或游戏设计引导可以主动把玩家的注意力引向某些屏幕区域例如降低背景几何的着色速率而保证敌人全质量渲染。因此 VRS 既能像动态分辨率缩放一样保证目标帧时间但细节控制更精细也适合便携设备在不明显牺牲画质的情况下省电。示例 UI 元素详解示例提供了一套 UI 元素用于交互式探索 VRS 行为全部由VRSRenderModule::BuildUI()在 vrsrendermodule.cpp 中注册到 Cauldron 的UIManager。下表汇总了各元素及其控制的内容元素名称取值说明Enable Variable ShadingChecked/Unchecked启用/禁用可变速率着色。PerDraw VRS1x1, 1x2, 2x1, 2x2逐 Draw 的着色速率值额外着色速率还包括 2x4、4x2、4x4。ShadingRateImage EnabledChecked/Unchecked启用/禁用 Tier 2 VRS 控制图像。ShadingRateImage CombinerChecked/Unchecked每个组合器Combiner的选项透传上一阶段状态即禁用当前阶段、覆盖忽略之前阶段、min、max 与 sum。VRS variance Threshold0.0 - 1.0定义一个阈值用于在生成 VRS 图像的计算着色器中将亮度方差与其比较。VRS Motion Factor0.0 - 1.0设置一个系数像素自上一帧以来的运动量按此系数缩放后用于调整着色速率。ShadingRateImage OverlayChecked/Unchecked启用/禁用 ShadingRateImage 叠加层渲染场景上的调试图像。对应源码中的默认值与细节m_VRSThreshold默认0.015fm_VRSMotionFactor默认0.01f见 vrsrendermodule.hUI 滑块步进为0.1fPerDraw VRS下拉框的可用项并非写死而是依据设备特性m_FeatureInfoVRS.NumShadingRates与ShadingRates动态生成因此在不同硬件上可见的着色速率集合可能不同Combiner 下拉框同样由m_FeatureInfoVRS.Combiners位掩码过滤选项包含Passthrough、Override、Min、Max、Sum、MulMul为源码扩展项见 vrsrendermodule.cpp。UI 元素的交互逻辑从源码可以还原各勾选框之间的联动关系关闭Enable Variable Shading时ToggleVariableShadingvrsrendermodule.cppShadingRateImage、Overlay、Combiner 与 PerDraw 索引全部复位VRS 上下文被销毁开启ShadingRateImage Enabled时ToggleShadingRateImageCombiner 索引被自动设为 1即第一个非透传组合器并同步更新设备 VRS 信息ShadingRateImage Overlay依赖 ShadingRateImage 开启ToggleOverlay否则强制关闭。UpdateVRSInfo()vrsrendermodule.cpp会把这些 UI 状态汇总为VariableShadingRateInfo并调用GetDevice()-SetVRSInfo(info)下发给驱动VariableShadingMode_None、VariableShadingMode_Per_Draw、VariableShadingMode_Image三种模式根据勾选状态切换BaseShadingRate与ShadingRateTileWidth/Height取自设备特性Tile 尺寸来自m_FeatureInfoVRS.MaxTileSize[0]。硬件 Tier 探测与资源创建Init()vrsrendermodule.cpp首先探测设备 VRS 能力if (GetFramework()-GetDevice()-FeatureSupported(DeviceFeature::VRSTier2)) { m_VRSTierSupported 2; GetFramework()-GetDevice()-GetFeatureInfo(DeviceFeature::VRSTier2, m_FeatureInfoVRS); } else if (GetFramework()-GetDevice()-FeatureSupported(DeviceFeature::VRSTier1)) { m_VRSTierSupported 1; GetFramework()-GetDevice()-GetFeatureInfo(DeviceFeature::VRSTier1, m_FeatureInfoVRS); }若设备完全不支持 VRSUI 区域仅显示一行 GPU does not support VRS! 文本。资源创建包括三块历史颜色缓冲HistoryColorBuffer与渲染分辨率等大的HistoryColorBuffer纹理用于在色调映射ToneMapping阶段回调中拷贝当前帧线性颜色供下一帧 VRS 计算使用VRS 图像VRSImageR8_UINT格式、带AllowShadingRate | AllowUnorderedAccess标志的 2D 纹理尺寸由ffxVrsGetImageSizeFromeRenderResolution(w, h, width, height, MaxTileSize[0])计算得出并在分辨率变化回调中按渲染分辨率重算回调注册CopyColorBufferCallback与DrawOverlayCallback注册在ToneMappingRenderModule之后当配置MotionVectorGeneration VRSRenderModule时还注册GenerateMotionVectorsCallback并调用InitMotionVectors()创建运动矢量 pass 的管线。设置 Variable Shading 的完整流程引入头文件包含 VRS API 后端接口头文件#include FidelityFX/host/ffx_vrs.h示例中同时以#define FFX_CPP方式包含 GPU 端算法头文件以便使用ffxVariableShadingGetDispatchInfo等工具函数#define FFX_CPP #include FidelityFX/gpu/vrs/ffx_variable_shading.h创建 VRS 上下文if (m_AllowAdditionalShadingRates) m_InitializationParameters.flags | FFX_VRS_ALLOW_ADDITIONAL_SHADING_RATES; m_InitializationParameters.shadingRateImageTileSize m_FeatureInfoVRS.MaxTileSize[0]; ffxVrsContextCreate(m_VRSContext, m_InitializationParameters);从 ffx_vrs.h 的定义可知FfxVrsContextDescription包含三个字段flagsFfxVrsInitializationFlagBits位集合目前仅有FFX_VRS_ALLOW_ADDITIONAL_SHADING_RATES1 0启用后着色速率可扩展到 2x4、4x2、4x4shadingRateImageTileSizeShadingRateImage 的 Tile 尺寸示例直接取硬件报告的MaxTileSize[0]backendInterface指向 DX12/Vulkan 后端的接口指针。示例在InitFfxBackend()中完成后端初始化通过SDKWrapper::ffxGetScratchMemorySize(FFX_VRS_CONTEXT_COUNT)申请 scratch 内存调用ffxGetInterface获取后端接口并用CauldronAssert校验后端 SDK 版本为 1.1.2、ffxVrsGetEffectVersion()为 1.2.0vrsrendermodule.cpp。注意FfxVrsContext生命周期应与 VRS 使用期一致销毁前应保证 GPU 不再访问相关资源因此示例在DestroyFfxContext()中先FlushAllCommandQueues()再调用ffxVrsContextDestroyvrsrendermodule.cpp。填充调度参数并派发FfxVrsDispatchDescription dispatchParameters {}; dispatchParameters.commandList ffxGetCommandList(pCmdList); ... FfxErrorCode errorCode ffxVrsContextDispatch(m_VRSContext, dispatchParameters);示例ExecuteVRSImageGen()vrsrendermodule.cpp展示了完整的字段填充FfxVrsDispatchDescription dispatchParameters {}; dispatchParameters.commandList SDKWrapper::ffxGetCommandList(pCmdList); dispatchParameters.output SDKWrapper::ffxGetResource(m_pVRSTexture-GetResource(), LVRSImage, FFX_RESOURCE_STATE_UNORDERED_ACCESS); dispatchParameters.historyColor SDKWrapper::ffxGetResource(m_pHistoryColorBuffer-GetResource(), LHistoryColorBuffer, FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ); dispatchParameters.motionVectors SDKWrapper::ffxGetResource(m_pMotionVectors-GetResource(), LVRSMotionVectorsTarget, FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ); dispatchParameters.motionFactor m_VRSMotionFactor; dispatchParameters.varianceCutoff m_VRSThreshold; dispatchParameters.tileSize m_FeatureInfoVRS.MaxTileSize[0]; dispatchParameters.renderSize {width, height}; dispatchParameters.motionVectorScale.x -1.f; dispatchParameters.motionVectorScale.y -1.f; FfxErrorCode errorCode ffxVrsContextDispatch(m_VRSContext, dispatchParameters); CAULDRON_ASSERT(errorCode FFX_OK);FfxVrsDispatchDescriptionffx_vrs.h字段含义如下字段含义commandList用于记录 VRS 渲染命令的命令列表historyColor上一帧颜色缓冲渲染分辨率用于读取亮度并计算方差motionVectors当前帧速度缓冲渲染分辨率output输出 ShadingRateImage 的缓冲renderSize输入资源的渲染分辨率varianceCutoff亮度方差可接受阈值即 UI 的 variance ThresholdmotionFactor数值越低像素需要移动越快才会被降低着色速率tileSizeShadingRateImage 的 Tile 尺寸motionVectorScale将运动矢量缩放到不同格式/坐标系示例在派发前将 VRS 纹理从 shader 读状态过渡到UnorderedAccess派发后再转回并在派发后调用SetAllResourceViewHeaps(pCmdList)恢复 Cauldron 的资源视图堆因为 FidelityFX 上下文会修改堆设置。VRS 控制图像生成算法原理GPU 端算法位于 ffx_variable_shading.h核心入口为VrsGenerateVrsImage(Gid, Gtid, Gidx)。它要求调用方 shader 实现三个读写函数FfxFloat32 ReadLuminance(FfxInt32x2 pos); // 读取上一帧颜色缓冲的亮度 FfxFloat32x2 ReadMotionVec2D(FfxInt32x2 pos); // 读取当前帧运动矢量 void WriteVrsImage(FfxInt32x2 pos, FfxUInt32 value); // 写入 VRS 图像算法流程非额外着色速率路径为重投影采样VrsGetLuminance用当前帧运动矢量回卷到上一帧位置pos - round(v)越界则 clamp 到屏幕边缘从而读取“该像素上一帧所在位置”的亮度方差计算在 2x2 粗像素区域内计算 2x1、1x2、2x2 三种方差delta.x/y/z同时记录 min/max 亮度运动量修正v length(ReadMotionVec2D(...)) * MotionFactor()将运动量乘以 MotionFactor 后从方差中减去——运动越快的像素越容易落入低着色速率档位邻域扩展读取上下左右相邻粗像素的 min/max 亮度补偿帧间依赖造成的“烧屏burn in”效应用dMin dMax抬高本 Tile 方差波前归约通过WaveActiveMax/subgroupMaxTile 大于 8 时或 wave intrinsics 配合 LDS 归约取 Tile 内最大方差速率决策var VarianceCutoff()时选 2x2否则按水平/垂直方差大小关系在 1x2、2x1、1x1 中选择最终WriteVrsImage写出。当启用FFX_VARIABLESHADING_ADDITIONALSHADINGRATES时对应FFX_VRS_ALLOW_ADDITIONAL_SHADING_RATES每个 4x4 区域按 4x4、4x2、2x4、2x2、2x1、1x2、1x1 的优先级链决策并再次用邻域 min 归约抑制闪烁。线程组布局方面ffxVariableShadingGetDispatchInfo会按 Tile 尺寸与是否启用额外着色速率计算numThreadGroupsX/Y例如非额外速率且 TileSize8 时每个线程组计算 2x2 个 VRS Tile而额外速率模式下每个线程组覆盖 32x32 像素。着色速率编码遵循 DirectX 约定FFX_VARIABLESHADING_MAKE_SHADING_RATE(x,y) (x 2) | y其中 x 轴 0x01X、0x12X、0x24Xy 轴同理因此 1x10、1x20x1、2x10x4、2x20x5、2x40x6、4x20x9、4x40xa。Tier 1 与 Tier 2 的控制方式DirectX 12 的 VRS 支持分为两个层级Tier 1仅允许按 Draw Call 设置着色速率对应 UI 的PerDraw VRS可按物体类型重要性、到相机距离、运动速度或屏幕位置调整Tier 2额外提供两种更细粒度的控制从顶点或几何着色器导出SV_ShadingRate实现逐图元控制例如背光面、蒙皮网格快速运动部分或按美术在顶点属性中指定的速率绑定包含每个屏幕 Tile 着色速率的VRS 控制图像对应 UI 的ShadingRateImage与几何粒度解耦可按屏幕区域、用户注视区域或通过分析上一帧 back buffer 判断哪些区域降低速率不易察觉。三种模式可同时使用Tier 2 定义了组合器树Combiner Tree决定各阶段结果如何与前序阶段结果合并成最终着色速率。每个组合器的选项为passthrough透传上一状态即禁用当前阶段、override覆盖、min、max、sum。示例通过设备特性Combiners位掩码动态决定 UI 提供哪些选项。使用 VRS 的注意事项技术细节技术文档 variable-shading.md 给出了四条重要的工程实践约束填充率不会下降VRS 不减少写入渲染目标的像素数量因此对填充率受限的 Pass 启用 VRS 并不推荐性能收益主要来自大图元 昂贵像素着色器占主导的场景小图元密集的 Draw Call 收益有限但画质无损失深度/模板 Pass 无收益深度与模板通常在像素着色器执行前计算写入且深度 Pass 大多没有或不昂贵像素着色器因此阴影贴图等仅深度 Pass 不会从 VRS 获益注意采样位置偏移VRS 下像素着色器的采样位置可能不是深度值存储的像素中心读取深度缓冲的几何如贴花、软粒子必须考虑深度差值可能显著大于未启用 VRS 的情况必须启用质心插值centroid interpolationVRS Tile 中心可能落在渲染几何之外未启用质心插值会导致纹理采样越界伪影质心插值可保证用于插值顶点属性的重心坐标始终位于图元覆盖范围内。若像素着色器需要知道 Tile 内哪些像素被几何覆盖如 alpha 测试可读取SV_Coverage导出 coverage mask 虽然可行但不推荐性能原因。示例配置与命令行运行示例配置文件VRSRenderModule的样例配置位于 vrsconfig.json关键设置{ FidelityFX VRS: { MotionVectorGeneration: VRSRenderModule, RenderModules: [ RasterShadowRenderModule, VRSRenderModule, GBufferRenderModule, LightingRenderModule, SkyDomeRenderModule, TranslucencyRenderModule, TAARenderModule, ToneMappingRenderModule ], RenderModuleOverrides: { GBufferRenderModule: { VariableShading: true }, TranslucencyRenderModule: { VariableShading: true } }, FeatureSupport: { VRSTier1: true }, Content: { Scenes: [../media/Hangar/Hangar_1105.gltf], Camera: Hangar_Camera_000, DiffuseIBL: ../media/IBL/sunflowers_puresky_Diffuse.dds, SpecularIBL: ../media/IBL/sunflowers_puresky_Specular.dds, SceneExposure: 1.355 } } }要点说明MotionVectorGeneration: VRSRenderModule指定由本模块负责生成运动矢量m_GenerateMotionVectors为 true从而在几何渲染前执行运动矢量 PassRenderModuleOverrides为 GBuffer 与 Translucency 渲染模块开启VariableShading使这些 Pass 实际应用着色速率FeatureSupport声明本示例最低要求VRSTier1: true若设备不满足会在初始化时断言并停止执行配置文件层级加载顺序为cauldronconfig.json框架默认值 → 示例配置覆盖 → 各渲染模块自身配置 → 示例配置再次覆盖模块默认值详见 运行示例文档。常用命令行参数按 running-samples.md 的说明所有命令行参数形如-option [参数]与本示例调试相关的主要有参数作用-devmode启用 CPU 验证、内存泄漏检查并以调试信息编译 Cauldron shader-cpulimiter [TARGETFRAMERATE]通过系统睡眠强制 CPU 帧率限制便于获得稳定计时-gpulimiter [TARGETFRAMERATE]通过 GPU 负载任务强制 GPU 帧率限制-resolution [WIDTH] [HEIGHT]覆盖默认启动分辨率-loadcontent [SCENE PATH] ...覆盖要加载的 glTF 场景至少 1 个可多个-fullscreen无边框全屏运行-screenshot退出前抓取最后一帧截图-benchmark durationX pathPATH append json运行指定帧数后导出基准数据默认 CSV可强制 JSON示例代码结构速览本示例在 samples/vrs 目录下结构如下vrsrendermodule.hVRSRenderModule类定义职责包括查询硬件 VRS 支持、生成当前帧运动矢量、基于运动矢量与历史颜色缓冲生成 VRS 图像、拷贝颜色到历史缓冲、显示 VRS 图像叠加层、设置 VRS 选项vrsrendermodule.cpp核心实现含 UI 构建、资源创建、FFX 后端初始化、回调与派发逻辑config/vrsconfig.json示例运行配置shaders/VrsOverlay.hlslVRS 图像叠加层着色器mainVS/mainPSSM6_0用于将 VRS 纹理绘制到色调映射后的交换链代理目标上shaders/motionvectorsps.hlsl运动矢量 Pass 像素着色器与transformVS.hlsl配对仅需 Position 顶点属性即可构建。叠加层 Pass 的管线InitOverlay使用 alpha 混合SrcAlpha/InvSrcAlpha绘制 3 个顶点全屏三角形并通过常量缓冲传递{width, height, tileSize}供着色器按 Tile 网格划分颜色。延伸阅读Variable Shading 技术文档VRS 原理、Tier 说明与工程注意事项的完整阐述运行示例指南Cauldron 配置层级、配置项与命令行参数全集命名规范FidelityFX API 命名与使用约定示例索引SDK 全部示例的导航赞分享图形学游戏开发【免费下载链接】dlssg-to-fsr3Adds AMD FSR 3 Frame Generation to games by replacing Nvidia DLSS Frame Generation (nvngx_dlssg).项目地址https://gitcode.com/gh_mirrors/dl/dlssg-to-fsr3点击查看免费下载相关推荐FidelityFX Variable ShadingVRS技术解析原理、适用场景与 SDK 集成实战FidelityFX Variable ShadingVRS技术解析原理、适用场景与 SDK 集成实战 本文基于 FidelityFX SDK 官方技术文图形学游戏开发Godot 4.7 可变速率着色VRS3D 性能优化实战godot-demo-projects 演示项目源码解析Godot 4.7 可变速率着色VRS3D 性能优化实战godot demo projects 演示项目源码解析 本文围绕 godot demo proj示例工程终极性能优化可变速率着色(VRS)在DirectX-Graphics-Samples中的实战指南终极性能优化可变速率着色 VRS 在DirectX Graphics Samples中的实战指南 可变速率着色 VRS 技术作为DirectX 12 Ulti示例工程上一篇终极指南melonJS游戏变现模式全解析 - 广告、订阅与付费下载下一篇当你在视频号看到精彩内容却无法保存时一款跨平台网络资源下载工具的解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考