CD Code Digest Hub /GTA V · v2

延迟光照与阴影:GBuffer、DeferredLighting、灯光系统、级联/局部阴影、SSAO

56 分钟阅读 14 个章节 1 张图

本页解释 GTA V 的光照实现:GBuffer 的 4 个颜色目标存什么;DeferredLighting 在光照阶段按什么顺序累加方向光/环境光/植被/场景灯/LOD 灯/皮肤;灯光如何从地图实体(CLightEntity)或代码(Lights::AddSceneLight)进入每帧列表并被剔除、排序;方向光级联阴影(CRenderPhaseCascadeShadows)如何划分级联、生成阴影图并“reveal”回 GBuffer;局部灯阴影(CParaboloidShadow,实际是立方体/半球/聚光三类)如何缓存;SSAO 有哪些技术。最后核实 suite/src/{lightshaders,manylights,rmlighting,terrainlighting} 与 RAGE 的 grshadowmap/grpostfx/grlightprepass 在游戏中的地位。

背景与相关页面#

相关源码文件#

文件内容
src/dev_ng/game/renderer/Deferred/DeferredConfig.hGBuffer 布局枚举、延迟着色器/变量/technique/pass 枚举、编译开关
src/dev_ng/game/renderer/Deferred/GBuffer.{h,cpp}GBuffer 目标创建、锁定、拷贝、平台变体(Xenon 平铺、Durango ESRAM 备用目标)
src/dev_ng/game/renderer/Deferred/DeferredLighting.{h,cpp}几何 pass 准备、方向/环境光、植被、积雪、水坑、皮肤、边缘标记、各 Prepare*Pass
src/dev_ng/game/renderer/Lights/lights.{h,cpp}、LightSource.h、LightCommon.h、LightGroup.*、LightCulling.h、LightsVisibilitySort.*Lights 命名空间:场景灯收集、可见性排序、渲染、体积光;CLightSource;前向灯光组
src/dev_ng/game/renderer/Lights/LightEntity.{h,cpp}CLightEntity(地图灯光实体)、CLightExtension
src/dev_ng/game/renderer/Lights/LODLights.*、AmbientLights.*、TiledLighting.*、TiledLightingSettings.h、AsyncLightOcclusionMgr.*LOD 灯、环境体积、分块分类、异步灯光遮挡
src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.{h,cpp}级联阴影阶段与 CCSMGlobals
src/dev_ng/game/renderer/RenderPhases/RenderPhaseParaboloidShadows.*、Shadows/ParaboloidShadows*.{h,cpp}、Shadows/ShadowTypes.h局部灯阴影与缓存
src/dev_ng/game/renderer/SSAO.{h,cpp}、SSAO_shared.hSSAO
src/dev_ng/game/shader_source/Lighting/Shadows/cascadeshadows_common.fxhCSM 分辨率等与着色器共享的常量
src/dev_ng/rage/framework/src/fwlight/wantedlighteffect.{h,cpp}手柄灯条“通缉”效果(不是渲染灯光,见 §8)
src/dev_ng/rage/suite/src/{lightshaders,manylights,rmlighting,terrainlighting}RAGE 样例/工具级灯光库(未进入游戏,见 §9)

1. GBuffer 布局#

GBufferRT 枚举与注释(注释要求与着色器 lighting.fxh 中 DeferredGBuffer 保持一致):

目标RGB / R G BA证据
GBUFFER_RT_0漫反射 albedoSSA(子采样 alpha)src/dev_ng/game/renderer/Deferred/DeferredConfig.h:22-40
GBUFFER_RT_1法线twiddle同上
GBUFFER_RT_2R=漫反射/高光混合,G=高光指数,B=菲涅耳阴影(级联阴影 reveal 写入,§5.4)同上
GBUFFER_RT_3R=自然环境光,G=人工环境光,B=自发光皮肤/反射强度同上
GBUFFER_DEPTH(=4)、GBUFFER_STENCIL(Xenon/D3D11/Orbis)、GBUFFER_FMASK_0..3(EQAA)深度/模板/片段掩码同上

PC/新主机路径下四个颜色目标都以 grctfA8R8G8B8、MSAA 取设备设置创建,名称 GBUFFER_0..3;EQAA 时只有 RT0 保留完整覆盖信息;Durango 的 DYNAMIC_GB 额外创建 ALT_GBUFFER_* 以在 ESRAM/DRAM 间切换 src/dev_ng/game/renderer/Deferred/GBuffer.cpp:427-541。另有 Xenon 平铺/PS3 版本的 CreateTargets(同文件的另两个定义)src/dev_ng/game/renderer/Deferred/GBuffer.cpp:206、src/dev_ng/game/renderer/Deferred/GBuffer.cpp:370。模板缓冲的 DEFERRED_MATERIAL_* 位用于区分材质类别(树、皮肤等),后续各光照 pass 用模板测试选择像素,例如方向光前的植被预处理使用 DEFERRED_MATERIAL_TREE|DEFERRED_MATERIAL_SPECIALBIT src/dev_ng/game/renderer/Deferred/DeferredLighting.cpp:3154-3165。

2. 两个阶段:几何(GBuffer)与光照(LightsToScreen)#

写入 GBuffer 的阶段与 drawlist 已在 render-pipeline-and-phases.md §6.1 给出;关键点:几何 pass 前后由 DeferredLighting::PrepareGeometryPass/FinishGeometryPass(Xenon 用 *Tiled 版本)包围 src/dev_ng/game/renderer/Deferred/DeferredLighting.cpp:1968、src/dev_ng/game/renderer/Deferred/DeferredLighting.cpp:2128;每个 pass/bucket 前由 RenderStateSetupDeferred 选择 PrepareDefaultPass/PrepareCutoutPass/PrepareSSAPass/PrepareDecalPass/PrepareFadePass/PrepareTreePass(见 pipeline 页 §5.4)。GBuffer 阶段结束时执行 CTiledLighting::DepthDownsample 生成分块分类数据,PC MSAA 下执行边缘标记 ExecuteEdgeMarkPass src/dev_ng/game/renderer/RenderPhases/RenderPhaseDefLight.cpp:899-908。

光照阶段 CRenderPhaseDeferredLighting_LightsToScreen::BuildDrawList 的顺序(src/dev_ng/game/renderer/RenderPhases/RenderPhaseDefLight.cpp:1377-1634):

图证据:SSAO src/dev_ng/game/renderer/RenderPhases/RenderPhaseDefLight.cpp:1444-1466;全局与阴影变量 src/dev_ng/game/renderer/RenderPhases/RenderPhaseDefLight.cpp:1481-1486;积雪 src/dev_ng/game/renderer/RenderPhases/RenderPhaseDefLight.cpp:1512-1528;方向/植被/环境体积/LOD 灯/查询/场景灯 src/dev_ng/game/renderer/RenderPhases/RenderPhaseDefLight.cpp:1531-1595;水坑与皮肤 src/dev_ng/game/renderer/RenderPhases/RenderPhaseDefLight.cpp:1608-1618。

延迟着色器与 technique 枚举(eDeferredShaders:LIGHTING/DIRECTIONAL/SPOT/POINT/CAPSULE/VOLUME/TILED/AMBIENT;eDeferredTechnique:SPOTCM、POINTCM、CAPSULE 及其 _VOLUME、AMBIENT_VOLUME、DIRECTIONAL、TILED_DIRECTIONAL/AMBIENT/SHADOW、SSS_SKIN、PUDDLEPASS、VOLUME_INTERLEAVE_RECONSTRUCTION、AMBIENT_LIGHT、SNOW、FOLIAGE_PREPROCESS)src/dev_ng/game/renderer/Deferred/DeferredConfig.h:67-144。局部灯的 pass 组合(标准/阴影/内外景/补光 filler/投影纹理/软阴影/焦散/车辆双灯)展开为 eLightPass 的约 70 个枚举值 src/dev_ng/game/renderer/Deferred/DeferredConfig.h:148-228;由 DeferredLighting::CalculatePass(light, hasTexture, hasShadow, …) 选出(src/dev_ng/game/renderer/Lights/lights.cpp:2906)。

3. 方向光与分块(tiled)分类#

ExecuteAmbientAndDirectionalPass:方向光强度为 0 或不投影阴影时视为“无方向光”;非 PC 主机先用模板 TREE|SPECIALBIT 执行植被预处理(注释 BS#2069821);PC MSAA 边缘模式下分别对边缘/非边缘、两种模板组合各画一次,否则 RenderDirectionalHelper(…, MM_DEFAULT, EM_IGNORE) src/dev_ng/game/renderer/Deferred/DeferredLighting.cpp:3141-3237。

RenderDirectionalHelper 二选一:分块版 RenderTiledDirectionalLight(bank 开关默认值为 !(__D3D11 || RSG_ORBIS),即 D3D11/Orbis 默认不走分块方向光;水下也不走),否则全屏 RenderDirectionalLight src/dev_ng/game/renderer/Deferred/DeferredLighting.cpp:3123-3139。分块版把屏幕切成 tile,以实例化四边形绘制(DrawInstancedPrimitive(…, 6, GetTotalNumTiles())),每个 tile 读取分类纹理 src/dev_ng/game/renderer/Deferred/DeferredLighting.cpp:1765-1825。

CTiledLighting:tile 大小默认 16(m_tileSize = 16)src/dev_ng/game/renderer/Lights/TiledLighting.cpp:63;每 tile 4 个分量 TILE_DEPTH_MIN/MAX/SPECULAR/INTERIOR src/dev_ng/game/renderer/Lights/TiledLightingSettings.h:6-19。DepthDownsample 用计算着色器对深度与 GBUFFER_RT_2 做并行归约写入分类 UAV(PC 低于 DX11 特性级别时断言“Need to implement shader version”),MSAA>4 要求 tile=8、MSAA>1 要求 tile=16,然后 GenerateMinimumDepth src/dev_ng/game/renderer/Lights/TiledLighting.cpp:762-830。最小深度纹理数在 PC 为 3 + GPU 数(多 GPU 安全)src/dev_ng/game/renderer/Lights/TiledLighting.h:94-95。

要点:GTA V 的“tiled lighting”是分类(classification)——为方向光/环境/阴影 reveal、PostFX 等提供每 tile 的深度范围与材质信息;场景局部灯并不做 tile 级灯光列表剔除,而是逐灯绘制体积(§4.3)。这是对代码路径的归纳:本次未在 RenderSceneLights 中发现按 tile 的灯光列表(推断,依据 §4.3 的逐灯循环)。

4. 灯光系统(Lights 命名空间)#

4.1 CLightSource 与容量#

灯光类型(位标志):LIGHT_TYPE_POINT=1、SPOT=2、CAPSULE=4、DIRECTIONAL=8、AO_VOLUME=16 src/dev_ng/game/renderer/Lights/LightCommon.h:15-23。标志位(节选):LIGHTFLAG_INTERIOR_ONLY/EXTERIOR_ONLY、VEHICLE、FX、TEXTURE_PROJECTION、CAST_SHADOWS、CAST_STATIC/DYNAMIC_GEOM_SHADOWS、DRAW_VOLUME、NO_SPECULAR、CORONA_ONLY、NOT_IN_REFLECTION/ONLY_IN_REFLECTION、USE_CULL_PLANE、CAST_HIGHER_RES_SHADOWS/ONLY_LOWRES_SHADOWS、CUTSCENE、MOVING_LIGHT_SOURCE(需要后期更新阴影)、USE_VEHICLE_TWIN、DELAY_RENDER(过场提前一帧创建阴影灯以免跳变)、ALREADY_TESTED_FOR_OCCLUSION src/dev_ng/game/renderer/Lights/LightSource.h:130-161。

容量值证据
MAX_STORED_LIGHTS(每帧场景灯)PC 1024,Orbis/Durango 768,其余 512src/dev_ng/game/renderer/Lights/TiledLightingSettings.h:21-26
MAX_STORED_LIGHT_SHAFTS / MAX_STORED_BROKEN_LIGHTS64 / 128src/dev_ng/game/renderer/Lights/lights.h:49-50
MAX_CORONA_ONLY_LIGHTSD3D11/Orbis 1280,其余 384src/dev_ng/game/renderer/Lights/lights.h:79
MAX_RENDERED_LIGHTS(前向灯光组,每次绘制最多 8 盏)8src/dev_ng/game/renderer/Lights/LightGroup.h:24

4.2 收集:AddSceneLight#

灯光的来源包括:可见的 CLightEntity(由 PostScan 在主线程调用 ProcessVisibleLight,见 pipeline 页 §4.4)、车辆/武器/特效/过场代码直接调用 Lights::AddSceneLight。AddSceneLight(断言 update 线程)依次:EMP(CRenderer::GetDisableArtificialLights)时只放行 FX(和可选车辆)灯;半径或强度过小直接丢弃;过场中丢弃 DONT_USE_IN_CUTSCENE;计算距离淡出四元组(灯光/阴影/高光/体积),对应分量为 0 时分别丢弃灯光、去掉阴影、加 NO_SPECULAR、去掉体积;按远裁剪面剔除;非实体来源的灯额外做视锥 AABB/球测试,并在非内部、非 AO 体积时做 Hi-Z 遮挡测试 IsLightVisible;CUTSCENE|FX 灯强制不剔除 src/dev_ng/game/renderer/Lights/lights.cpp:4097-4203。灯光实体侧:CLightEntity 是 CEntity 派生、页式池(FW_REGISTER_CLASS_PAGEDPOOL),提供 ProcessLightSource/ProcessVisibleLight/CalcLightIntensity/ApplyEffectsToLight/AddCorona src/dev_ng/game/renderer/Lights/LightEntity.h:51-118。

4.3 可见性排序与渲染#

  • 主线程 Lights::ProcessVisibilitySortAsync(在 PreRenderEntities 末尾、抛物面阴影更新之后调用):按 update 缓冲索引选择输出数组,设置视锥平面/远裁剪/近平面包围增量,向 sysDependency 提交 LightsVisibilitySort 任务,输入为整个场景灯数组与上一帧阴影灯数组(各 MAX_STORED_LIGHTS * sizeof(CLightSource))src/dev_ng/game/renderer/Lights/lights.cpp:2192-2264、src/dev_ng/game/renderer/Renderer.cpp:1037-1040。安全区内 PC 上等待该任务(见 threading 页 §7.2)。
  • 渲染线程每帧开始时 dlCmdSetupLightsFrameInfo 把 Lights::m_currFrameInfo 指向本帧 LightingData 快照;方向光等通过 GetRenderDirectionalLight() 从快照读取(断言渲染线程),主线程通过 GetUpdate* 访问 lightingData src/dev_ng/game/renderer/Lights/lights.h:343-365、drawlist 页 §5.3。
  • RenderSceneLights:遍历排序后的可见灯,分“相机在灯外/相机在灯内”两轮;跳过 DELAY_RENDER 灯;投影纹理灯检查纹理字典持有 REF_RENDER(否则警告/断言);按阴影索引取 CParaboloidShadow::GetDeferredShadowType;CalculatePass 选 pass;(useStencil 时)先用 DrawLightStencil 画体积模板(含 cull plane),再画灯光体积;内部/外景限制可用模板实现 src/dev_ng/game/renderer/Lights/lights.cpp:2759-2990。
  • LOD 灯:CLODLights::RenderLODLights 把可见 LOD 灯写入点/聚光/胶囊三种实例顶点缓冲(Lock(grcsNoOverwrite)),实例化绘制;半径受 timecycle TCVAR_LIGHT_FALLOFF_MULT 调节 src/dev_ng/game/renderer/Lights/LODLights.cpp:396-460;CLODLights 接口与可见性双缓冲 src/dev_ng/game/renderer/Lights/LODLights.h:24-60。
  • 环境体积:AmbientLights::Render,参数为拉近/拉远/衰减(含 eb 变体)src/dev_ng/game/renderer/Lights/AmbientLights.h:8-31。
  • 体积光(DrawScene 的 Finalize 段):RenderVolumetricLightingFx 先画全部光柱(light shafts),再在有体积灯时画灯光体积;MSAA 用解析后的深度,D3D11 可用只读深度 src/dev_ng/game/renderer/Lights/lights.cpp:3653-3700。LIGHT_VOLUME_USE_LOW_RESOLUTION 在 PC/新主机为 1(低分辨率体积 + 交错重建 technique)src/dev_ng/game/renderer/Lights/LightCommon.h:29-37;PC 另有 HQ 体积光 pass(SUPPORT_HQ_VOLUMETRIC_LIGHTS)src/dev_ng/game/renderer/Deferred/DeferredConfig.h:58-63、src/dev_ng/game/renderer/Deferred/DeferredConfig.h:232-247。
  • 前向光照(半透明、镜面等):Lights::UseLightsInArea(box, inInterior, …) 为当前绘制挑选最多 8 盏灯填入 CLightGroup src/dev_ng/game/renderer/Lights/lights.h:306、src/dev_ng/game/renderer/Lights/LightGroup.h:24-30;draw handler 的 SetupLightsAndGlobalInInteriorFlag 在前向 pass(m_SetupLights)中以实体局部包围盒 DLC_Add(Lights::UseLightsInArea, …) src/dev_ng/game/renderer/Entities/EntityDrawHandler.cpp:730-745。

远处灯(CDistantLights/CDistantLights2,路灯/车流的远景光点)在 DrawScene 中由 CVisualEffects::RenderDistantLights 绘制,属 12 页范围 src/dev_ng/game/renderer/RenderPhases/RenderPhaseStd.cpp:2059-2063。

5. 方向光级联阴影(CSM)#

5.1 结构#

  • 级联数 CASCADE_SHADOWS_COUNT = 4(定义在 shaderlib/rage_constants.h,与着色器共享)src/dev_ng/rage/base/src/shaderlib/rage_constants.h:59。
  • 阴影类型 eCSMShadowType:CSM_TYPE_CASCADES(4 级联)、CSM_TYPE_QUADRANT、CSM_TYPE_HIGHRES_SINGLE;质量模式 CSM_QM_IN_GAME/CUTSCENE_DEFAULT/MOONLIGHT src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.h:20-34。脚本可通过 Script_SetShadowType/SetCascadeBounds/SetSplitZExpWeight/SetDepthBias/SetSlopeBias/SetShadowSampleType/SetAircraftMode/SetDynamicDepthMode… 调整 src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:10614-10944。
  • 全局对象 gCascadeShadows(CCSMGlobals)持有双份 CCSMState m_state[2],GetUpdateState()/GetRenderState() 以 m_stateIndex ^ 0/1 选择,在安全区 Synchronise 中翻转 src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:3144-3148、src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:3342、src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:10321-10326。

5.2 分辨率#

平台阴影图分辨率证据
Xenon/PS3640²(注释:PS3 RT 内存池假定宽 640);VS/mini 320²src/dev_ng/game/shader_source/Lighting/Shadows/cascadeshadows_common.fxh:11-21
SM4+(PC/新主机)着色器常量 gCSMResolution(运行时);VS/mini 512²src/dev_ng/game/shader_source/Lighting/Shadows/cascadeshadows_common.fxh:22-32
运行时(VARIABLE_RES_SHADOWS)基线 256;Durango 256<<2 = 1024(注释:限制为 1024×4096);Orbis 默认 1024,-shadowQuality 可调;PC 为 256 << ShadowQuality,ShadowQuality≥3 且开启 Ultra 时 4096,ShadowQuality=0 时为 1,上限 DX10 2048 / 其他 4096;-dx11shadowres 可覆盖src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:6743-6769

注释“1024 x 4096”表明 4 个级联在一张纵向排列的图集中(推断:未阅读阴影图 RT 创建代码;CASCADE_SHADOW_TEXARRAY 时改为纹理数组,见 §5.3 的清除代码)。PC 软阴影/硬件 PCF:CASCADE_SHADOWS_USE_HW_PCF_DX10 与 CASCADE_SHADOWS_DO_SOFT_FILTERING 在 PC/新主机开启 src/dev_ng/game/shader_source/Lighting/Shadows/cascadeshadows_common.fxh:54-60;代码中还有 NVIDIA/AMD 厂商阴影开关(m_enableNvidiaShadows/m_enableAMDShadows)src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:11330-11340。

5.3 级联划分与阴影图生成#

  • 分割平面:初始化为 CSM_DEFAULT_SPLIT_Z_MIN(视口默认近平面)到 CSM_DEFAULT_SPLIT_Z_MAX = 256 的线性等分 src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:357-360、src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:5460-5473;AdjustSplitZ(expWeight) 在线性分割与对数分割 z0·(zn/z0)^t 之间按权重插值(默认权重 0.9,可由脚本覆盖)src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:7035-7053、src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:10039-10043。权重还随“焦点对象”半径(车辆/飞机)用曲线调整,飞机模式使用 m_airCraftExpWeight,非切镜头时级联缩放做 0.8/0.2 指数平滑 src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:1600-1647。最终 CalcSplitZ 乘以 timecycle 距离倍率 m_distanceMultiplier,高空模式把最后一个分割推到 7000 src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:7056-7077。
  • 扫描:BuildRenderList 在 active 时调用 COcclusion::BeforeSetupRender(…, SHADOWTYPE_CASCADE_SHADOWS, …) 与 ConstructRenderListNew;阴影遮挡用独立的 Hi-Z 存储 SCAN_OCCLUSION_STORAGE_CASCADE_SHADOWS src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:11096-11117、src/dev_ng/game/scene/world/GameScan.cpp:176-177。扫描后若 IsEverythingInShadow(),同时关闭级联与方向光(避免光照跳变)src/dev_ng/game/scene/world/GameScan.cpp:601-611。
  • BuildDrawList:阴影质量为 0 直接返回;先更新 timecycle 相关设置(即使阴影关闭也要更新雾阴影参数);选择采样类型/云阴影;随后两种路径:
    • 实例化(GS_INSTANCED_SHADOWS 且启用):一个 drawlist 内把 4 个级联视口传给设备(SetCurrentInstVP),以几何实例一次绘制所有级联,再单独为前 3 个级联绘制草 src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:11371-11453。
    • 逐级联:每个级联一个 drawlist(拆分开关时为 DL_RENDERPHASE_CASCADE_SHADOWS_0..3),设置级联视口后 CRenderListBuilder::AddToDrawListShadowEntities(…, bDirLightShadow=true, …);门户阴影可按窗口重叠跳过级联 src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:11459-11572。
    • 最后一个 drawlist(_RVL):alpha 阴影(粒子、电缆)BuildDrawListAlphaShadows,然后 DLC_Add(ShadowReveal) src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:11577-11613。
    • 清除:每级联清深度(纹理数组时逐 slice),Durango 可用异步清除 ClearShadowMapsAsync(在 DL_BEGIN_DRAW 中调度)src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:11133-11153、src/dev_ng/game/system/system.cpp:1159-1161。

5.4 Shadow reveal:把阴影写回 GBuffer#

CCSMGlobals::ShadowReveal(注释“this gets called immediately before deferred lighting”)以 GBuffer::GetTarget(GBUFFER_RT_2) 为目标,读取深度(PC 可能先拷贝深度、或使用只读深度视图)与阴影图,可选合并粒子阴影、云阴影、软阴影,结果写入 RT2 的 alpha(与 §1 中“A = shadow”一致)src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:9159-9240。因为 reveal drawlist 在级联阴影阶段末尾、早于 LightsToScreen 执行,方向光 pass 读取 RT2.a 即得到屏幕空间阴影项。Xenon 另有在光照阶段执行的 LateShadowReveal src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:10268-10277。阴影图还被降采样为 mini 图(雾光、水下、粒子使用)与 VS 图(粒子/光柱在顶点着色器采样,使用级联 2)src/dev_ng/game/shader_source/Lighting/Shadows/cascadeshadows_common.fxh:79-80。

6. 局部灯阴影(CParaboloidShadow)#

名字是历史遗留:当前实现按灯光类型使用 立方体贴图(点光,6 面)、半球(5 面,非正面被“压扁”以提高纹理利用率)或单张透视(聚光) src/dev_ng/game/renderer/Shadows/ParaboloidShadows.h:213-221、src/dev_ng/game/renderer/Shadows/ShadowTypes.h:21-27。

概念值 / 结构证据
活动阴影槽4 高分辨率 + 4 低分辨率 = 8(对应 8 个 CRenderPhaseParaboloidShadow);前 4 个低分辨率槽与高分辨率槽内存重叠src/dev_ng/game/renderer/Shadows/ParaboloidShadows_shared.h:24-40
静态缓存高分辨率 8 + 低分辨率 48(RT 可重叠的平台)或 8 + 32;注释给出每项显存估算src/dev_ng/game/renderer/Shadows/ParaboloidShadows_shared.h:27-34
静态缓存刷新预留至少 1 个活动槽用于刷新静态缓存,防止活动灯过多时远处灯永远拿不到阴影src/dev_ng/game/renderer/Shadows/ParaboloidShadows_shared.h:43
基础分辨率主机 512(-shadowQuality<2 为 256);D3D11 PC 由设置决定 256/512(质量 0 时 2);其余 256src/dev_ng/game/renderer/Shadows/ParaboloidShadows.cpp:612-633
活动条目标志SHADOW_FLAG_ENABLED/STATIC/DYNAMIC/EXTERIOR/INTERIOR/LATE_UPDATE/CUTSCENE/DELAY_RENDER/HIGHRES/REFRESH_CHECK_ONLYsrc/dev_ng/game/renderer/Shadows/ParaboloidShadows.h:26-39
缓存状态CACHE_STATUS_UNUSED/NOT_ACTIVE/REFRESH_CANDIDATE/GENERATINGsrc/dev_ng/game/renderer/Shadows/ParaboloidShadows.h:41-44
缓存条目优先级、高低分辨率互链、静态几何模型哈希校验和与可见数(用于判断静态缓存是否需重建)、多 GPU 计数、RT 索引src/dev_ng/game/renderer/Shadows/ParaboloidShadows.h:108-151

机制:静态几何(建筑、HiLOD 建筑、树)渲染进静态缓存,只在校验和变化时重建;动态物体(ped/车/物体)每帧渲染,最终合成。阶段的 UpdateViewport 按条目的静态/动态标志设置实体可见性掩码——STATIC 只收集建筑与树,DYNAMIC 只收集 ped/车辆/物体/树(D3D11/Orbis 才含树)src/dev_ng/game/renderer/RenderPhases/RenderPhaseParaboloidShadows.cpp:240-288。条目再生成时标记 CACHE_STATUS_GENERATING,多 GPU 时记录 GPU 计数 src/dev_ng/game/renderer/Shadows/ParaboloidShadows.cpp:1080-1090。CParaboloidShadow::PreRender/UpdateAfterPreRender 在 CRenderer::PreRender/PreRenderEntities 中调用(后者注释“needs to be after all moving lights are added”)src/dev_ng/game/renderer/Renderer.cpp:927、src/dev_ng/game/renderer/Renderer.cpp:1033;UpdateActiveCachesfromStatic 在 DL_BEGIN_DRAW 中执行 src/dev_ng/game/system/system.cpp:1158。光照时 RenderSceneLights 用灯的 m_ShadowIndex 取对应阴影类型并选择阴影 pass(§4.3)。

7. SSAO#

SSAO::CalculateAndApply 在光照阶段最开始执行,强度取自 timecycle TCVAR_LIGHT_SSAO_INTEN × 0.25;技术由 timecycle TCVAR_LIGHT_SSAO_TYPE 选择,但 PC 64 位在“下一代版本”开启时固定使用 HBAO(与主机一致)src/dev_ng/game/renderer/SSAO.cpp:1005-1018;m_useNextGenVersion 由设置决定 src/dev_ng/game/renderer/SSAO.cpp:255、src/dev_ng/game/renderer/SSAO.cpp:501。技术枚举:qsssao、cpssao、pmssao、cpqsmix、cp4Dirqsmix、cp8Dirqsmix,D3D11/Orbis 另有 mrssao、hdao、hdao2、qsssao2、hbao(SUPPORT_HBAO 在 PC/Orbis/Durango 为 1)src/dev_ng/game/renderer/SSAO.h:46-64、src/dev_ng/game/renderer/SSAO_shared.h:48。结果写入 GBuffer 的环境项(主机路径锁定 GBUFFER_RT_3)src/dev_ng/game/renderer/SSAO.cpp:1107-1134。PS3 有 SPU 版本(SSAOSPU,见 environment-rendering.md 的 SPU 简述)。

8. fwlight:不是渲染灯光#

rage/framework/src/fwlight 只有 fwWantedLightEffect:继承 ioLightEffect,在输入设备的灯条(如 PS4 手柄光条)上交替显示红蓝色,注释提醒避免 5–70 Hz 闪烁 src/dev_ng/rage/framework/src/fwlight/wantedlighteffect.h:15-39、src/dev_ng/rage/framework/src/fwlight/wantedlighteffect.cpp:16-41。游戏唯一使用者是 CPlayerInfo::m_WantedLightEffect src/dev_ng/game/Peds/PlayerInfo.h:1466、src/dev_ng/game/Peds/PlayerInfo.cpp:2874。它与渲染光照无关,输入设备侧见 ../22-input-platform/input-and-control-mapping.md。

9. 未进入游戏的灯光/阴影库(核实)#

库内容(以类为依据)游戏引用证据
suite/src/lightshaders只有 rage_manylights*.fx/.fxh、rage_neon.fx、rage_spotlightfog.fx、rage_spotshadow.fx 等着色器与 dummy.cpp,.dtx 为未填写模板game/、rage/base、rage/framework 中无 #include "lightshaders/…"目录盘点;检索结果为空
suite/src/manylightssimpleLightManager、lightgrid(CPU 生成“每格最强 4 盏灯”纹理的 NearestMap)、lightfog、lightshadercontrollers无游戏引用src/dev_ng/rage/suite/src/manylights/lightgrid.h:317-320
suite/src/rmlightingrmlLightingMgr、rmlLightSource、rmlShadowMap : CShadowMap;依赖 grshadowmap;makefile 已命名为 .dead只被 suite/samples/sample_rmlighting 引用src/dev_ng/rage/suite/src/rmlighting/lightingmgr.h:10-31、src/dev_ng/rage/suite/src/rmlighting/lightingmgr.h:196
suite/src/terrainlighting离线地形光照烘焙:TerrainMapGenerator、光线追踪 Tracer/RayEngine/RayTileRenderer、kd 树、高度图无游戏引用src/dev_ng/rage/suite/src/terrainlighting/terrainMapGenerator.h:36、src/dev_ng/rage/suite/src/terrainlighting/tracer.h:15-95

核实方法:在 game/、rage/base、rage/framework 中检索 #include "<库名>/ 均为 0 条;在 rage/ 全树中只有 suite/samples/sample_rmlighting/*、suite/samples/sample_lighting/* 包含这些库;引用它们的 .sln 全部是样例或已弃用工具(sample_rmlighting、sample_lighting、viewfragnm、deprecated/viewshot 等)。结论:这四个库与 grshadowmap/grpostfx/grlightprepass 一样不参与游戏构建,GTA V 的光照与阴影全部由 game/renderer 实现。该结论与 03 页 一致,并补充了 03 页列出的 rmlighting/manylights 包含 grshadowmap 这一事实只发生在非游戏代码中。

10. 平台差异小结#

项PCDurango/OrbisXenon/PS3
GBuffer4×A8R8G8B8,MSAA 可选,MSAA 边缘 pass(MSAA_EDGE_PASS 仅 PC)同格式;Durango ESRAM 备用目标;Orbis EQAA FMASKXenon 平铺 GBuffer;PS3 SPU 辅助
方向光默认全屏(分块为 bank 选项)Orbis 默认全屏;植被预处理分块方向光
CSM 分辨率设置驱动,最高 40961024(Orbis 可调)640
局部阴影分辨率256/512(设置)512256
体积光低分辨率 + HQ 选项低分辨率全分辨率
SSAOHBAO(next-gen 开)或 timecycle 选择HBAO 等timecycle 选择;PS3 SPU

证据见上文各节;MSAA_EDGE_PASS = 1 && RSG_PC && DEVICE_MSAA src/dev_ng/game/renderer/Deferred/DeferredConfig.h:11。

11. 约束与边界行为#

  1. 线程访问:Lights::GetRender* 断言渲染线程并读取帧快照,GetUpdate* 断言 update 线程;AddSceneLight 只能在 update 线程调用。
  2. 场景灯上限:超过 MAX_STORED_LIGHTS 的灯无法加入(本次未追踪溢出处理路径,见 §12)。
  3. EMP / 禁用人工光:CRenderer::SetDisableArtificialLights 生效时只保留 FX(及可选车辆)灯,LOD 灯同时关闭(CLODLights::Enabled() 检查同一标志)src/dev_ng/game/renderer/Lights/LODLights.h:54。
  4. 投影纹理灯:要求纹理字典带 REF_RENDER 引用,否则警告/断言(防止渲染线程使用已释放的纹理)。
  5. 阴影与可见性联动:扫描判定“全部在阴影中”时同时关闭 CSM 与方向光;阴影质量设置为 0 时 CSM 阶段直接返回。

12. 未决问题 / 证据不足#

  • RenderPhaseCascadeShadows.cpp(约 1 万行)只读了划分、BuildDrawList、reveal 入口;级联包围体计算、阴影图图集布局、软阴影/厂商阴影(NVIDIA/AMD)、云阴影、粒子阴影、门户阴影的细节未展开。
  • DeferredLighting.cpp 中积雪、水坑、皮肤(SSS)、植被、边缘标记 pass 的着色细节未读;DrawLightStencil 的模板算法(单 pass/双 pass)未展开。
  • LightsVisibilitySort 依赖任务的排序键、g_SortedVisibleLights 的结构未读;场景灯数组满时的行为未核实。
  • CTiledLighting 分类结果被哪些 pass(PostFX 的 g_UseTiledTechniques、阴影 reveal)消费只看到引用,未逐一核对。
  • SSAO 各技术(HBAO、HDAO、MR-SSAO 等)的实现细节与 PC 设置映射未展开。