延迟光照与阴影:GBuffer、DeferredLighting、灯光系统、级联/局部阴影、SSAO
本页解释 GTA V 的光照实现:GBuffer 的 4 个颜色目标存什么;DeferredLighting 在光照阶段按什么顺序累加方向光/环境光/植被/场景灯/LOD 灯/皮肤;灯光如何从地图实体(CLightEntity)或代码(Lights::AddSceneLight)进入每帧列表并被剔除、排序;方向光级联阴影(CRenderPhaseCascadeShadows)如何划分级联、生成阴影图并“reveal”回 GBuffer;局部灯阴影(CParaboloidShadow,实际是立方体/半球/聚光三类)如何缓存;SSAO 有哪些技术。最后核实 suite/src/{lightshaders,manylights,rmlighting,terrainlighting} 与 RAGE 的 grshadowmap/grpostfx/grlightprepass 在游戏中的地位。
背景与相关页面#
- 这些阶段在帧中的位置(GBuffer=10、阴影=12/13、LightsToScreen=18)及 drawlist 内容总览见 render-pipeline-and-phases.md;状态与参数如何跨线程传递见 drawlists-and-render-thread-handoff.md。
- 着色器侧(
shader_source/Lighting/*、deferred_lighting.fx、lighting.fxh)见 shaders-and-materials.md。 - timecycle(光照强度、SSAO 强度/类型、阴影距离倍率等的来源)见 environment-rendering.md。
- 灯光实体所附着的地图数据(2dfx
CLightAttr、LOD 灯 IMAP)见 ../05-world/map-data-and-lod.md;远处灯(CDistantLights)、日冕(coronas)属于视效,见 ../12-vfx/decals-and-vfx-systems.md。 - RAGE 通用模块
grshadowmap/grpostfx/grlightprepass不进入游戏构建,见 ../03-rage-graphics/postfx-shadows-and-occlusion.md;本页 §9 给出与之一致的补充核实。
相关源码文件#
| 文件 | 内容 |
|---|---|
src/dev_ng/game/renderer/Deferred/DeferredConfig.h | GBuffer 布局枚举、延迟着色器/变量/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.h | SSAO |
src/dev_ng/game/shader_source/Lighting/Shadows/cascadeshadows_common.fxh | CSM 分辨率等与着色器共享的常量 |
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 B | A | 证据 |
|---|---|---|---|
GBUFFER_RT_0 | 漫反射 albedo | SSA(子采样 alpha) | src/dev_ng/game/renderer/Deferred/DeferredConfig.h:22-40 |
GBUFFER_RT_1 | 法线 | twiddle | 同上 |
GBUFFER_RT_2 | R=漫反射/高光混合,G=高光指数,B=菲涅耳 | 阴影(级联阴影 reveal 写入,§5.4) | 同上 |
GBUFFER_RT_3 | R=自然环境光,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,其余 512 | src/dev_ng/game/renderer/Lights/TiledLightingSettings.h:21-26 |
MAX_STORED_LIGHT_SHAFTS / MAX_STORED_BROKEN_LIGHTS | 64 / 128 | src/dev_ng/game/renderer/Lights/lights.h:49-50 |
MAX_CORONA_ONLY_LIGHTS | D3D11/Orbis 1280,其余 384 | src/dev_ng/game/renderer/Lights/lights.h:79 |
MAX_RENDERED_LIGHTS(前向灯光组,每次绘制最多 8 盏) | 8 | src/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*访问lightingDatasrc/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)),实例化绘制;半径受 timecycleTCVAR_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 盏灯填入CLightGroupsrc/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/MOONLIGHTsrc/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/PS3 | 640²(注释: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,高空模式把最后一个分割推到 7000src/dev_ng/game/renderer/RenderPhases/RenderPhaseCascadeShadows.cpp:7056-7077。 - 扫描:
BuildRenderList在 active 时调用COcclusion::BeforeSetupRender(…, SHADOWTYPE_CASCADE_SHADOWS, …)与ConstructRenderListNew;阴影遮挡用独立的 Hi-Z 存储SCAN_OCCLUSION_STORAGE_CASCADE_SHADOWSsrc/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);其余 256 | src/dev_ng/game/renderer/Shadows/ParaboloidShadows.cpp:612-633 |
| 活动条目标志 | SHADOW_FLAG_ENABLED/STATIC/DYNAMIC/EXTERIOR/INTERIOR/LATE_UPDATE/CUTSCENE/DELAY_RENDER/HIGHRES/REFRESH_CHECK_ONLY | src/dev_ng/game/renderer/Shadows/ParaboloidShadows.h:26-39 |
| 缓存状态 | CACHE_STATUS_UNUSED/NOT_ACTIVE/REFRESH_CANDIDATE/GENERATING | src/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/manylights | simpleLightManager、lightgrid(CPU 生成“每格最强 4 盏灯”纹理的 NearestMap)、lightfog、lightshadercontrollers | 无游戏引用 | src/dev_ng/rage/suite/src/manylights/lightgrid.h:317-320 |
suite/src/rmlighting | rmlLightingMgr、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. 平台差异小结#
| 项 | PC | Durango/Orbis | Xenon/PS3 |
|---|---|---|---|
| GBuffer | 4×A8R8G8B8,MSAA 可选,MSAA 边缘 pass(MSAA_EDGE_PASS 仅 PC) | 同格式;Durango ESRAM 备用目标;Orbis EQAA FMASK | Xenon 平铺 GBuffer;PS3 SPU 辅助 |
| 方向光 | 默认全屏(分块为 bank 选项) | Orbis 默认全屏;植被预处理 | 分块方向光 |
| CSM 分辨率 | 设置驱动,最高 4096 | 1024(Orbis 可调) | 640 |
| 局部阴影分辨率 | 256/512(设置) | 512 | 256 |
| 体积光 | 低分辨率 + HQ 选项 | 低分辨率 | 全分辨率 |
| SSAO | HBAO(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. 约束与边界行为#
- 线程访问:
Lights::GetRender*断言渲染线程并读取帧快照,GetUpdate*断言 update 线程;AddSceneLight只能在 update 线程调用。 - 场景灯上限:超过
MAX_STORED_LIGHTS的灯无法加入(本次未追踪溢出处理路径,见 §12)。 - EMP / 禁用人工光:
CRenderer::SetDisableArtificialLights生效时只保留 FX(及可选车辆)灯,LOD 灯同时关闭(CLODLights::Enabled()检查同一标志)src/dev_ng/game/renderer/Lights/LODLights.h:54。 - 投影纹理灯:要求纹理字典带
REF_RENDER引用,否则警告/断言(防止渲染线程使用已释放的纹理)。 - 阴影与可见性联动:扫描判定“全部在阴影中”时同时关闭 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 设置映射未展开。