CD Code Digest Hub /GTA V · v2

模型、Drawable 与着色器层:grmodel / rmcore / shaderlib / blendshapes / mesh / edge

62 分钟阅读 14 个章节 2 张图

本页解释 RAGE 中“可绘制对象”这一层:一个美术资源(drawable)如何被组织为 LOD → 模型 → 几何体 → 顶点/索引缓冲,如何与 shader(grmShader 包装的 grcEffect 实例)绑定,如何在资源加载时重新连接到运行期 effect,以及每次 Draw 调用如何经过 LOD、draw bucket、视锥 AABB 剔除、technique group 选择最终落到 grcDevice::DrawIndexedPrimitive。同时盘点 shaderlib(共享 HLSL 头)、shaders(RAGE 样例 effect)、grblendshapes、shaderMaterial、mesh、edge、suite/src/demesh 在 GTA V 构建中的真实地位。

背景与相关页面#

相关源码文件#

目录 / 文件作用
rmcore/drawable.h/.cpprmcDrawableBase、rmcDrawable:drawable 顶层对象、加载、绘制入口
rmcore/lodgroup.h/.cpprmcLodGroup、rmcLod:四级 LOD、剔除球/AABB、bucket 掩码、DrawSingle/DrawMulti
rmcore/typefileparser.h/.cpprmcTypeFileParser:.type 文本格式的分节回调解析器
rmcore/instance.h/.cpprmcInstance:基于 drawable 的简单实例化
rmcore/drawablespu.*、rsxstallhandler.cppPS3:drawable 在 SPU 上执行
grmodel/model.h/.cppgrmModel:一组几何体 + 每几何体 shader 索引 + AABB
grmodel/geometry.h/.cppgrmGeometry、grmGeometryQB(普通 VB/IB)、grmGeometryEdge(PS3 Edge)
grmodel/shader.h/.cpp、shaderfactory.cpp、shadergroup.h/.cpp、shadervar.*、shadersorter.*grmShader、grmShaderFactory、grmShaderGroup
grmodel/matrixset.h/.cppgrmMatrixSet:蒙皮矩阵调色板
grmodel/modelfactory.h/.cpp、grmodel_config.h、curvedmodel.*模型工厂、配置开关、曲面模型
shaderlib/*.fxh、rage_constants.h、skinning_method_config.h运行时与 HLSL 共用的常量与公共头
shaders/*.fx、preload.listRAGE 样例 effect
grblendshapes/*混合形状(morph target)系统
shaderMaterial/*.geotemplate/.geotype/.mtlgeo 材质模板系统
mesh/*mshMesh 中间网格格式与处理算法
edge/*Sony PlayStation Edge 1.1.1 几何库(带 Rockstar 修改)
suite/src/demesh/*有向边网格与 QEM 简化(工具)

上表中的目录相对于 src/dev_ng/rage/base/src/(demesh 相对于 src/dev_ng/rage/suite/src/);正文引用一律显示相对源码根的完整路径。

1. 构建参与情况:哪些在游戏里真的生效#

游戏解决方案链接 RageGraphics 工程(以及 RageMisc 等)src/dev_ng/game/VS_Project_NM/game.slndef:28、src/dev_ng/game/VS_Project_NM/game.slndef:45。RageGraphics 编入的目录为 output、crmetadata、crskeleton、edge、grblendshapes、grcore、grmodel、grprofile、input、jpeg、mesh、rmcore、shaderlib、spatialdata src/dev_ng/rage/base/src/vcproj/RageGraphics/RageGraphics.txt:1-26。shaderMaterial、shaders(只有 .fx)与 demesh 不在其中。

此外有三个关键的编译期开关决定“编进去但是否生效”:

开关定义GTA V 游戏构建中的值后果证据
ENABLE_BLENDSHAPES(__TOOL || !HACK_GTA4)HACK_GTA4 = 1 → 仅工具构建为真游戏运行时 grblendshapes 整体被预处理掉src/dev_ng/rage/base/src/grblendshapes/blendshapes_config.h:9、src/dev_ng/rage/base/src/forceinclude/win64_release.h:515
USE_MTL_SYSTEM(HACK_RDR2)HACK_RDR2 = 0grmShader 不接入 shaderMaterialsrc/dev_ng/rage/base/src/grmodel/grmodel_config.h:13、src/dev_ng/rage/base/src/forceinclude/win64_release.h:517、src/dev_ng/rage/base/src/grmodel/shader.cpp:44-48
MESH_LIBRARY(!__FINAL)FINAL 构建为 0.type 文本加载、mshMesh 处理、demesh 只在非 FINAL/工具中存在src/dev_ng/rage/base/src/mesh/mesh_config.h:1

结论:GTA V 运行时实际依赖的是 rmcore + grmodel + shaderlib(HLSL 头)+ PS3 上的 edge;grblendshapes、shaderMaterial、mesh、demesh 只服务于工具/非最终构建或其他项目(RDR2、MC4)。

2. 对象层次#

图中证据:rmcDrawableBase : pgBase 与 m_ShaderGroup src/dev_ng/rage/base/src/rmcore/drawable.h:47-138;rmcDrawable 成员 src/dev_ng/rage/base/src/rmcore/drawable.h:160-379;rmcLod/rmcLodGroup 成员 src/dev_ng/rage/base/src/rmcore/lodgroup.h:38-204;grmModel 成员 src/dev_ng/rage/base/src/grmodel/model.h:392-396;grmGeometry/grmGeometryQB/grmGeometryEdge src/dev_ng/rage/base/src/grmodel/geometry.h:95-270、src/dev_ng/rage/base/src/grmodel/geometry.h:288-483、src/dev_ng/rage/base/src/grmodel/geometry.h:511-530;grmShaderGroup 成员 src/dev_ng/rage/base/src/grmodel/shadergroup.h:330-338;grmShader::m_EffectInstance src/dev_ng/rage/base/src/grmodel/shader.h:549。

2.1 概念 → 类型 → 职责#

概念类型职责
可绘制资源rmcDrawable拥有 shader group、LOD 组、可选骨骼与关节限制;对外的 Draw/DrawSkinned/DrawInstanced/IsVisible
LOD 组rmcLodGroup4 级 LOD(LOD_HIGH/MED/LOW/VLOW)、剔除球与 AABB、每级阈值与 bucket 掩码
单个 LODrmcLod若干 grmModel;“每个模型可绑定到不同骨骼,因此像汽车这样的复杂对象可一次 draw 调用完成”(类注释)
模型grmModel几何体数组、每几何体 shader 索引、模型/几何体 AABB(m_AABBs[0] 为整体,[i+1] 为第 i 个几何体)、矩阵索引、蒙皮标志、曲面细分几何体计数
几何体grmGeometryQB / grmGeometryEdge最多 4 组 VB/IB(多缓冲)、图元类型、索引数、矩阵调色板;Edge 版本供 PS3 SPU 处理
shader 组grmShaderGroup本 drawable 所有 shader;内嵌纹理字典;按名查变量的缓存 m_ShaderGroupVars
shadergrmShader持有 grcInstanceData(引用 grcEffect 的参数值、draw bucket、实例化标志)
蒙皮矩阵grmMatrixSet从 crSkeleton 更新的 Matrix43 数组

类注释“up to four levels of detail … each model within the LOD can be bound to a different bone” 见 src/dev_ng/rage/base/src/rmcore/drawable.h:142-159;LOD_HIGH..LOD_COUNT src/dev_ng/rage/base/src/rmcore/lodgroup.h:29-33;MAX_VERT_BUFFERS = 4 src/dev_ng/rage/base/src/grmodel/geometry.h:19。

2.2 容量与常量#

常量值用途证据
DRAWABLE_MAX_SHADERS / MODELS / GEOMS48 / 24 / 64PS3 SPU 路径的上限;超出则 MakePpuOnly() 回退 PPUsrc/dev_ng/rage/base/src/rmcore/drawable.h:25-28、src/dev_ng/rage/base/src/rmcore/drawable.cpp:1265-1271
DRAWABLE_MAX_CONTAINER_SIZE48 KB同上(SPU 本地存储容器)同上
LOD_COUNT4LOD 级数src/dev_ng/rage/base/src/rmcore/lodgroup.h:30
LODGROUP_MASK1 << 16在 bucket 掩码中标记“当前选中的 LOD”(注释:游戏只用低 16 位)src/dev_ng/rage/base/src/rmcore/lodgroup.h:21-22
rmcTypeFileParser最多 14 节、每节 25 实体、名字 128 字符.type 解析器上限src/dev_ng/rage/base/src/rmcore/typefileparser.h:62
rmcDrawable::RORC_VERSION84 + 纹理/骨骼/几何/shader 版本 + __64BIT资源版本组合src/dev_ng/rage/base/src/rmcore/drawable.cpp:57-59
SKINNING_COUNTDX11/SM≥4.0/Win64 255;PC DX9 32;Xenon/PS3/PSP2 48骨骼矩阵 cbuffer 容量src/dev_ng/rage/base/src/shaderlib/rage_constants.h:19-50
mshMaxMatricesPerMaterial / mshMaxBlendTargets / mshMaxTexCoordSets256 / 256 / 8中间网格上限src/dev_ng/rage/base/src/mesh/mesh.h:32-36

3. 加载#

3.1 资源路径(主机与最终构建)#

正式数据是离线资源化后的 drawable(扩展名由游戏层决定,如 #dr/#td 系列;RAGE 的 LoadResource 会给名字追加 _drw/_td 并用 pgRscBuilder::Load(..., "#td", ...) 先载入纹理字典)src/dev_ng/rage/base/src/rmcore/drawable.h:295-303、src/dev_ng/rage/base/src/rmcore/drawable.cpp:1333-1340。游戏通过流式模块载入(见 ../05-world/game-streaming-and-scene-streamer.md)。资源被放置时调用“资源构造函数”:

  1. rmcDrawable(datResource&) 修正 m_ContainerPtr/m_DebugName 指针,然后——因为“离线资源化时着色器未加载,且着色器随时可能变化”——在运行时重算 LOD 组 bucket 掩码,Optimize(),在支持曲面细分的平台按 shader 把几何体排序为“非细分在前、细分在后”,最后 CheckSizeForSpu src/dev_ng/rage/base/src/rmcore/drawable.cpp:1292-1313。另一个重载允许 drawable 借用外部 shader group(不拥有它,例如碎片对象借用父对象的组)src/dev_ng/rage/base/src/rmcore/drawable.cpp:1200-1216、src/dev_ng/rage/base/src/rmcore/drawable.cpp:1315-1331。
  2. grmShader(datResource&) 只是构造其 grcInstanceData(rsc) src/dev_ng/rage/base/src/grmodel/shader.cpp:141-148。
  3. grcInstanceData(datResource&) 按哈希重新连接 effect:资源里 Basis 与 BasisHashCode 共用 union;若无材质,Basis = grcEffect::LookupEffect(BasisHashCode),找不到则报错并用 fallback effect;若有材质哈希,则 LookupMaterial,并断言材质的 basis 与哈希一致 src/dev_ng/rage/base/src/grcore/effect_resource.cpp:307-355。随后(CHECK_FOR_SHADER_MATCHES)逐变量比较“是否纹理”与名字哈希;不匹配时置 grcEffect::sm_MatchError 并在普通内存中重建一份与当前 effect 匹配的实例数据(注释提示需要重新生成资源;Orbis/Durango 关闭该警告)src/dev_ng/rage/base/src/grcore/effect_resource.cpp:357-400。

因此 effect 必须在任何 drawable 资源放置之前预加载:游戏在 CShaderLib::LoadAllShaders 里 PreloadShaders("common:/shaders"),它直接转发到 grcEffect::Preload src/dev_ng/game/shaders/ShaderLib.cpp:265-275、src/dev_ng/rage/base/src/grmodel/shaderfactory.cpp:83-87。

3.2 文本路径(.type,仅 MESH_LIBRARY)#

rmcDrawable::Load(basename) 读取 .type 文本(版本 103;低于 103 或无版本号均不再支持)src/dev_ng/rage/base/src/rmcore/drawable.h:153-157。LoadFromParser 向 rmcTypeFileParser 注册分节回调:shadinggroup → LoadShader、lodgroup/mesh → LoadMesh、skel/skel、skel/jlimits、skel/properties、edge/edge(blendshapes 注册被注释掉)src/dev_ng/rage/base/src/rmcore/drawable.cpp:209-227。解析器语义:节名匹配后由回调自行消费实体数据(含大括号)src/dev_ng/rage/base/src/rmcore/typefileparser.h:25-62。LoadShader 在一个 32 KB 容器内调用 grmShaderFactory::GetInstance().LoadGroup(T) src/dev_ng/rage/base/src/rmcore/drawable.cpp:229-239,后者解析 shadinggroup { ... } src/dev_ng/rage/base/src/grmodel/shaderfactory.cpp:95-108。grmShader::Load 若名字以 .sps 结尾,则在当前材质库中查找预设并 Clone 其实例数据(找不到时尝试去扩展名再查)src/dev_ng/rage/base/src/grmodel/shader.cpp:541-560。游戏的材质库从 common:/shaders/db/ 预载 src/dev_ng/game/shaders/ShaderLib.cpp:457。

可通过静态 SetLodLoadFilter(bitmask) 只加载部分 LOD、SetLodForceRemap 强制映射(注释:非线程安全)src/dev_ng/rage/base/src/rmcore/drawable.cpp:73-83。

3.3 工厂单例#

  • grmShaderFactory:可 Push/PopInstance 的单例;Create/CreateGroup/LoadGroup/PlaceShader/PlaceShaderGroup/PreloadShaders;默认实现 grmShaderFactoryStandard src/dev_ng/rage/base/src/grmodel/shader.h:50-130。grmShader::Place 走 PlaceShader src/dev_ng/rage/base/src/grmodel/shader.cpp:146-148。
  • grmModelFactory:Create(fiTokenizer&,...) 与资源 PlaceModel src/dev_ng/rage/base/src/grmodel/modelfactory.h:119-153。

4. drawable 内部的 LOD 选择#

  • rmcDrawable::IsVisible(mtx, vp, lod&, zDist*):把 LOD 组剔除球中心变换到世界空间,调用 vp.IsSphereVisible 得到裁剪状态与沿视线的距离;可见时 lod = ComputeLod(zDist - radius),不可见时返回 LOD_VLOW src/dev_ng/rage/base/src/rmcore/drawable.cpp:383-408。
  • ComputeLod(zDist):从 LOD_HIGH 开始,只要距离超过该级阈值或该级不存在就往下走,最多到 LOD_VLOW src/dev_ng/rage/base/src/rmcore/lodgroup.h:106-111。
  • GetBucketMask(lod):从给定 LOD 向更低 LOD 找第一个非零掩码并去掉 LODGROUP_MASK 位 src/dev_ng/rage/base/src/rmcore/lodgroup.cpp:155-166;SetLodIndex/GetLodIndex 把“当前 LOD”编码在各级掩码的第 16 位 src/dev_ng/rage/base/src/rmcore/lodgroup.h:174-190。
  • 每级掩码在资源放置时由 rmcLod::ComputeBucketMask(group) 汇总各几何体 shader 的掩码 src/dev_ng/rage/base/src/rmcore/lodgroup.cpp:77、src/dev_ng/rage/base/src/rmcore/lodgroup.cpp:317-323。

注意:GTA V 的实体级 LOD(基于 fwEntity LOD 距离、父子 LOD 树)在 rmcDrawable::IsVisible 之外由游戏决定,然后把 LOD 索引直接传给 Draw(mtx, bucketMask, lod);本节只描述 drawable 自带的 4 级 LOD 机制。

5. 绘制路径#

5.1 非蒙皮#

证据(按图中顺序):

  • rmcDrawable::Draw:PS3 且 DRAWABLE_ON_SPU 时打包 rmcDrawable__Draw SPU 命令(含容器指针、bucket 低 16 位、LOD);否则 GetLodGroup().DrawSingle(...) 并累加统计 src/dev_ng/rage/base/src/rmcore/drawable.cpp:513-566。DRAWABLE_ON_SPU = (SPU_GCM_FIFO > 2) src/dev_ng/rage/base/src/rmcore/drawablespu.h:10。
  • rmcLodGroup::DrawSingle:bucket 过滤、每 LOD 只设置一次世界矩阵、按 sm_TessellatedDrawMode 选择绘制非细分/细分部分(5 种模式,例如把细分网格当普通网格画,用于反射等)src/dev_ng/rage/base/src/rmcore/lodgroup.cpp:363-429、src/dev_ng/rage/base/src/rmcore/lodgroup.h:70-81。
  • grmModel::DrawUntessellatedPortion/DrawTessellatedPortion 分别以 [0, untess) 与 [m_Count - tess, m_Count) 区间调用 DrawPortion,后者在 reallyTessellate 时用 RMC_DRAW_TESSELLATED src/dev_ng/rage/base/src/grmodel/model.cpp:1385-1412。这一组函数位于 #else // !RAGE_SUPPORT_TESSELLATION_TECHNIQUES 分支,即 PC D3D11/Durango/Orbis 的活动路径;旧主机走同文件 #if !RAGE_SUPPORT_TESSELLATION_TECHNIQUES 下的 grmModel::Draw(group, bucketMask, lod) src/dev_ng/rage/base/src/grmodel/model.cpp:1202-1250。
  • grmModel::DrawPortion:模型掩码排除、AABB 剔除回调(g_grcCommandBuffer 录制时禁用)、强制 shader(sm_ForceShader)与其 bucket、单几何体特例、逐几何体剔除与统计、BANK 下的自定义参数回调,最后 shader->DrawInternal(drawType, *this, i, true) src/dev_ng/rage/base/src/grmodel/model.cpp:1415-1488。
  • 剔除回调默认是 ModelVisibleWithAABB(用当前视口的局部 LRTB 平面做 AABB 测试)src/dev_ng/rage/base/src/grmodel/model.cpp:91、src/dev_ng/rage/base/src/grmodel/model.cpp:1158-1165;它是 DECLARE_MTR_THREAD 的函数指针,可被游戏临时替换或置空,例如水面反射阶段经 drawlist 命令 DLC_Add(grmModel::SetModelCullback, NULL) 关闭模型级剔除 src/dev_ng/game/renderer/RenderPhases/RenderPhaseWaterReflection.cpp:1632、src/dev_ng/game/renderer/RenderPhases/RenderPhaseWaterReflection.cpp:1658。
  • grmShader::DrawInternal:BANK 下跳过“闪烁/禁用”标记的 shader;BeginDraw → Bind(i) → model.Draw(geom) → UnBind() → EndDraw() src/dev_ng/rage/base/src/grmodel/shader.cpp:486-506。Bind/UnBind 就是 grcEffect::BeginPass/EndPass src/dev_ng/rage/base/src/grmodel/shader.cpp:275-285。
  • grmModel::Draw(int geom) → m_Geometries[geom]->Draw() src/dev_ng/rage/base/src/grmodel/model.cpp:1138-1140。
  • grmGeometryQB::Draw:按多缓冲标志选 VB/IB 下标;若有偏移缓冲(混合形状/伤害偏移),先把它绑到流 1 并用 m_VtxDeclOffset 声明,否则用普通声明,调用 GRCDEVICE.DrawIndexedPrimitive(m_PrimType, decl, VB, IB, m_IndexCount) src/dev_ng/rage/base/src/grmodel/geometry.cpp:1811-1846。

5.2 蒙皮#

  • rmcDrawable::DrawSkinned(mtx, grmMatrixSet&, ...) → rmcLodGroup::DrawMulti src/dev_ng/rage/base/src/rmcore/drawable.cpp:570-602。DrawMulti 对每个模型:若 GetSkinFlag()(真正蒙皮)则用根矩阵并 DrawSkinned(或曲面细分变体);若不是蒙皮而是“刚性挂骨”模型,则用 model.GetMatrixIndex() 从矩阵集中取对应骨骼矩阵作为世界矩阵(注释:需要反转置并加回偏移,开销略大)src/dev_ng/rage/base/src/rmcore/lodgroup.cpp:450-520。缺少该 LOD 时只打印错误并返回 src/dev_ng/rage/base/src/rmcore/lodgroup.cpp:453-458。
  • grmMatrixSet:头部 + Matrix43 m_MatrixData[](Xenon 半精度可选);Create(skel)/Update(skel, isSkinned);Allocate 在 Xenon 用物理内存、PS3 128 字节对齐 src/dev_ng/rage/base/src/grmodel/matrixset.h:22-130。MTX_IN_CB 只在 PC D3D11 为真(矩阵集直接作为 cbuffer 绑定),MTX_IN_VB/MTX_IN_TEX 关闭 src/dev_ng/rage/base/src/shaderlib/skinning_method_config.h:1-8。
  • grmGeometryQB::DrawSkinned 根据几何体的矩阵调色板 m_MtxPalette 把用到的骨骼矩阵复制到蒙皮常量:PS3/PC-DX9 用 alloca 拷贝后 SetVertexShaderConstant;Xenon 直接写 GPU 常量;Durango/Orbis(非 MTX_IN_CB)通过 BeginVertexShaderConstantF(SKINNING_CBUFFER, size) 写入,数量取 min(cbuffer 容量, 调色板数, 矩阵集数) 并断言 ≤255;PC D3D11 走 ms.Bind(SKINNING_CBUFFER) src/dev_ng/rage/base/src/grmodel/geometry.cpp:1957-2030。

5.3 实例化#

rmcDrawable::DrawInstanced(grcInstanceBuffer*) → rmcLodGroup::DrawSingle(group, ib, ...) → grmModel::Draw(group, ib, bucketMask):与普通路径相反,只画 IsInstanced() 的 shader,用 RMC_DRAW technique 与 grmGeometry::DrawInstanced(ib) src/dev_ng/rage/base/src/rmcore/drawable.cpp:604-613、src/dev_ng/rage/base/src/rmcore/lodgroup.cpp:432-447、src/dev_ng/rage/base/src/grmodel/model.cpp:1167-1193。另有 rmcInstance : rmcDrawable 的通用“每实例上传若干常量寄存器”方案 src/dev_ng/rage/base/src/rmcore/instance.h:7-46、src/dev_ng/rage/base/src/rmcore/instance.h:94-135。另一套“视口实例化”(一次 draw 写多个级联/视口)不在此层,而是 grcViewport::SetInstancing + RAGE_INSTANCED_TECH 让 BeginDraw 自动加 sm_InstancedTechOffset 选择 *_drawinstanced technique src/dev_ng/rage/base/src/grmodel/shader.cpp:328-330。

5.4 PS3:SPU drawable 与 Edge#

见 §10。要点:rmcDrawable 在 PS3 默认把整套 LOD/模型/shader 组打包进容器交给 SPU 执行;容器超过 48 KB 或模型/几何/shader 数超限、或实例数据指针不在 shader group 容器内时,CheckSizeForSpu 把它标为 PPU-only src/dev_ng/rage/base/src/rmcore/drawable.cpp:1220-1290。

6. Draw bucket 掩码与过滤#

grmShader::SetDrawBucket(id) 写入 DrawBucket 并生成 BUCKETMASK_GENERATE(id) src/dev_ng/rage/base/src/grmodel/shader.h:360-368。掩码格式(低 8 位基础桶、高 8 位子桶、位 15 曲面细分)见 grcore-device-and-effects.md。过滤发生在三层:

层判据证据
LODBUCKETMASK_MATCH_LOD(m_BucketMask[lod], passed) —— LOD 内任一 shader 命中src/dev_ng/rage/base/src/rmcore/lodgroup.cpp:368
模型BUCKETMASK_MODELMATCH(m_Mask, passed) 为真则整模型跳过(模型掩码与传入子桶无交集)src/dev_ng/rage/base/src/grmodel/model.cpp:1417-1418
几何体BUCKETMASK_MATCH(shaderMask, passed) && !IsInstanced();sm_ForceShader 不继承桶时用强制 shader 的掩码src/dev_ng/rage/base/src/grmodel/model.cpp:1431-1484

游戏为各渲染阶段(不透明、透明、贴花、水…)分配的桶编号属于 ../11-rendering/render-pipeline-and-phases.md 的范畴。

7. Technique group 选择#

  • grmShader::InitClass 依次注册 unlit(必须为 0)、default(必须为 1)、blit,DEV/BANK 再注册 tool,并 SetForcedTechniqueGroupId(-1) src/dev_ng/rage/base/src/grmodel/shader.cpp:99-112。
  • GetTechniqueGroupId():若有强制组(DECLARE_MTR_THREAD sm_ForcedTechniqueGroupId,每渲染线程独立)则用之;DEV/BANK 工具模式用 tool;否则返回 grcLightState::IsEnabled()(0=unlit,1=default)src/dev_ng/rage/base/src/grmodel/shader.cpp:265-273、src/dev_ng/rage/base/src/grmodel/shader.cpp:306-315。
  • BeginDraw(type, restoreState, techOverride):type += sm_InstancedTechOffset(实例化模式),technique = techOverride ? techOverride : GetTechniqueHandle(groupId, type);结果 0 表示该 shader 无此 technique,调用者不得绘制;BANK 统计 MissingTechnique;PIX 捕获时打标签 src/dev_ng/rage/base/src/grmodel/shader.cpp:321-377。
  • 游戏在加载任何 shader 之前注册大量组(ld、wdcascade、waterreflection、cube、alt0..8、lightweight* 等),渲染阶段通过 SetForcedTechniqueGroupId 切换 src/dev_ng/game/shaders/ShaderLib.cpp:287-345;详见 ../11-rendering/shaders-and-materials.md。

8. grmShaderGroup 与按名设置变量#

grmShaderGroup 提供 LookupVar(name) → grmShaderGroupVar,以及与 grcEffect::SetVar 对应的一整套 SetVar 重载(float、向量、矩阵、纹理、Color32 等),它们通过 SetVarCommon(var, data, stride, count) 写入组内每个含该变量的 shader src/dev_ng/rage/base/src/grmodel/shadergroup.h:156-200、src/dev_ng/rage/base/src/grmodel/shadergroup.h:319-321。变量缓存 m_ShaderGroupVars 是 grcEffectVarEntry { Hash, Pairs(shader 索引, 变量) } 的数组,“从不资源化”src/dev_ng/rage/base/src/grmodel/shadergroup.h:27-57、src/dev_ng/rage/base/src/grmodel/shadergroup.h:333-338。D3D11/Orbis 上可直接取变量所在 cbuffer 与偏移(GetVarCBufferAndOffset)src/dev_ng/rage/base/src/grmodel/shadergroup.h:324-328。m_ShaderDatas 缓存每个 shader 的 Entries 指针以省去 SPU 一次 DMA src/dev_ng/rage/base/src/grmodel/shadergroup.h:338。组内还有若干材质分类辅助(布料、广告牌、人物、置换、相机)src/dev_ng/rage/base/src/grmodel/shadergroup.h:305-310。游戏的 CCustomShaderEffect* 体系就是在绘制前对实例数据做这类写入(见 ../11-rendering/shaders-and-materials.md)。

9. shaderlib:公共 HLSL 头#

shaderlib 是 C++ 与 HLSL 共享的头文件库,RageGraphics 工程收录它但其中只有 dummy.cpp 一个 C++ 源文件(其余为 .fxh/.h)。游戏着色器直接包含它,例如 game/shader_source/common.fxh 引入 rage_common.fxh、config_switches.h、rage_diffuse_sampler.fxh 与 PN 三角形曲面细分头 src/dev_ng/game/shader_source/common.fxh:26-36,Util/skin.fxh 引入 rage_skin.fxh src/dev_ng/game/shader_source/Util/skin.fxh:6。

头内容证据
rage_constants.h运行时与着色器共享的寄存器布局:MATRIX_BASE 0/COUNT 4、GLOBALS_SIZE 64、SKINNING_BASE 64、SKINNING_COUNT、实例化 cbuffer 基址(160/184/208/212)、MAX_NUM_CBUFFER_INSTANCING 6(与级联视口数一致)、CASCADE_SHADOWS_COUNT 4src/dev_ng/rage/base/src/shaderlib/rage_constants.h:1-50
rage_constantbuffers.fxhBeginConstantBuffer*/EndConstantBuffer* 宏:SM≥4.0 生成 cbuffer name : register(bN),FXL(360)按寄存器基址,其他平台为空src/dev_ng/rage/base/src/shaderlib/rage_constantbuffers.fxh:1-43
rage_common.fxh引入常量/宏/采样器/实例化头;骨骼矩阵 cbuffer rage_bonemtx(b4,float3x4 gBoneMtx[SKINNING_COUNT]);矩阵 cbuffer rage_matrices(b1:gWorld/gWorldView/gWorldViewProj/gViewInverse,正是 grcViewport::RegenerateDevice 上传的 256 字节);实例化 cbuffer rage_cbinst_matrices(b5)与 rage_cbinst_update(b6)src/dev_ng/rage/base/src/shaderlib/rage_common.fxh:1-64
rage_defines.fxh引入 forceinclude/hacks.h 与 grcore/config_switches.h,定义 ROW_MAJOR、REGISTER/REGISTER2src/dev_ng/rage/base/src/shaderlib/rage_defines.fxh:5-6、src/dev_ng/rage/base/src/shaderlib/rage_defines.fxh:259-278
skinning_method_config.hMTX_IN_CB = RSG_PC && __D3D11 等蒙皮矩阵传递方式src/dev_ng/rage/base/src/shaderlib/skinning_method_config.h:1-8
其他 .fxh光照(rage_fastlight、lighting、rage_sphericallight)、阴影(rage_shadow*)、PN 三角形、草、水、视差、megashader、粒子(rmptfx_common)、HDR、噪声、memorycs(Durango/Orbis 的 GPU memcpy/memset)等目录清单

与 grcore-device-and-effects.md 对照:C++ 侧的 RegenerateDevice 一次写入 256 字节,正好对应 rage_matrices 的四个 float4x4。

10. 其余目录#

10.1 shaders:RAGE 样例 effect#

rage/base/src/shaders 只有 .fx 与批处理脚本,preload.list 列出 rage_bump*、rage_skin、rage_glass、rmptfx_*、rage_im、scaleform_shaders 等 RAGE 样例/通用 effect src/dev_ng/rage/base/src/shaders/preload.list:1-74;编译经 %RAGE_DIR%\base\bin\makeshader.bat,输出到 $(RAGE_ASSET_ROOT)\tune\shaders\lib\ src/dev_ng/rage/base/src/shaders/makeshader.bat:1、src/dev_ng/rage/base/src/shaders/makefile.bat:1-2。其中 rage_im.fx 只是包含 grcore/rage_im.fx src/dev_ng/rage/base/src/shaders/rage_im.fx:1。GTA V 不直接使用这些样例:游戏在 game/shader_source 中有自己的同名/对应包装(Custom/rage_bink.fx、Util/scaleform_shaders.fx、Util/im.fx、Util/imMS.fx、Custom/rage_fastmipmap.fx),例如 Util/im.fx 包含 grcore/rage_im.fx、Custom/rage_fastmipmap.fx 包含 grcore/rage_fastmipmap.fx src/dev_ng/game/shader_source/Util/im.fx:143、src/dev_ng/game/shader_source/Custom/rage_fastmipmap.fx:1,并在启动早期从 update2:/common/shaders 预建 im、rage_bink、scaleform_shaders、rage_fastmipmap src/dev_ng/game/shaders/ShaderLib.cpp:244-262。

10.2 grblendshapes:混合形状(GTA V 运行时关闭)#

  • 结构:grbTargetManager : pgBaseRefCounted 管理 grbBlendShape 与以 16 位骨骼名哈希为键的 grbTarget 映射,提供 ResetBlendShapeOffsets/PrepareBlendShapeOffsets/ReleaseBlendShapeOffsets/SwapBuffers(偏移缓冲多缓冲)src/dev_ng/rage/base/src/grblendshapes/manager.h:30-120;grbMorphable 管理单个子网格的目标数据,可从 mshMaterial 或 Edge 几何初始化 src/dev_ng/rage/base/src/grblendshapes/morphable.h:34-60。渲染侧通过 grmGeometryQB 的偏移缓冲(流 1)把位移叠加到顶点上(§5.1)。
  • 但 ENABLE_BLENDSHAPES 在游戏构建为假(§1),游戏中的相关调用均被 #if ENABLE_BLENDSHAPES 包裹,例如 CDynamicEntity 为 creature 添加 blendshape 组件的代码 src/dev_ng/game/scene/DynamicEntity.cpp:1315-1358。GTA V 的面部/头部变形走其他机制(MeshBlend、表情/骨骼),见 ../06-peds/ped-motion-and-rendering.md。

10.3 shaderMaterial:材质模板系统(未接入)#

shaderMaterialGeoManager 单例从 .geotemplate(模板)→ .geotype(类型)→ 实例(.mtlgeo)三级文件构建参数集,并记录哪些 grmShader 使用某实例以支持热重载 src/dev_ng/rage/base/src/shaderMaterial/shaderMaterialGeoManager.h:21-80、src/dev_ng/rage/base/src/shaderMaterial/shaderMaterialGeoManager.cpp:912-1039。每种参数类型有一对 ParamDescription*/ParamValue* 类(Bool/Float/Int/Matrix34/Matrix44/String/Texture/Vector2-4),类型码复用 grcEffect::VarType src/dev_ng/rage/base/src/shaderMaterial/shaderMaterialGeoParamValue.h:27-47。grmShader 仅在 USE_MTL_SYSTEM(=HACK_RDR2)时包含它,GTA V 为 0;游戏代码也没有包含该目录。

10.4 mesh:中间网格(工具/非最终构建)#

mshMesh 是导出器与资源构建使用的可编辑网格:材质(mshMaterial)、通道、绑定、切线、混合目标增量;提供 Weld/Triangulate/Tristrip/CacheOptimize/ComputeNormals/ComputeTanBi 等处理 src/dev_ng/rage/base/src/mesh/mesh.h:562-620。grmModel::Prepare(mshMesh&, ...) 与 grmModel::Create(const mshMesh&, ...) 把它转为运行时几何 src/dev_ng/rage/base/src/grmodel/model.h:69-94。整体受 MESH_LIBRARY(!FINAL)保护。

10.5 edge:PS3 Edge 几何#

  • 目录是 Sony PlayStation Edge 1.1.1(edge_version.h)src/dev_ng/rage/base/src/edge/edge_version.h:1-15,含 SPU 端 geom/(解压、蒙皮、变换、剔除、遮挡、压缩)、离线工具库 libedgegeomtool/(分区、k-cache 优化、索引压缩、混合形状缓冲)、post/(SPU 后处理作业的预编译 .spa/.o)。
  • Rockstar 的修改记录在 hacked_files.txt:混合形状支持三缓冲、EdgeGeomPpuConfigInfo 中插入资源期自定义字段、新增 UV4/UV5/SPECULAR/TANGENT1/BINORMAL1 属性 ID、ring buffer 使用量统计、空输出格式支持、CUSTOM_EDGE_SAFE_MEMORY 安全失败等 src/dev_ng/rage/base/src/edge/hacked_files.txt:1-49。
  • 运行时接入:grmGeometryEdge(GEOMETRYEDGE 类型)提供 Draw/DrawSkinned 与把顶点/索引解出到 CPU 的 GetVertexAndIndex src/dev_ng/rage/base/src/grmodel/geometry.h:511-530;grcore/edge_jobs.*、edgegeomspu.* 负责 SPU 作业;effect_config.h 的 USE_EDGE = __PS3 与 Edge 阴影类型枚举 src/dev_ng/rage/base/src/grcore/effect_config.h:21、src/dev_ng/rage/base/src/grcore/effect_config.h:49-57。游戏也注册了 PS3 专用的 wdcascadeedge technique group src/dev_ng/game/shaders/ShaderLib.cpp:298-300。

10.6 suite/src/demesh:有向边网格与简化(工具)#

deMesh 实现 Campagna/Kobbelt/Seidel 的 Directed Edges 结构(半边 + 邻接,要求有界流形、一致绕序)src/dev_ng/rage/suite/src/demesh/demesh.h:20-80;deSimplify 用二次误差矩阵(QEM)与边折叠堆做简化,可调二面角/边界/不连续权重 src/dev_ng/rage/suite/src/demesh/desimplify.h:16-70;错误报告结构由 demesh.psc 反射 src/dev_ng/rage/suite/src/demesh/demesh.psc:1-41。全部受 MESH_LIBRARY 保护,使用者是 rage/suite/tools/cli/demeshtools/*(mesh↔demesh 转换、简化、切块、加裙边等)与 Maya LOD 插件;游戏代码不引用。

约束与边界行为#

  1. effect 先于 drawable:资源中的 shader 只存 effect 名哈希,放置时必须能 LookupEffect 命中,否则用 fallback(并报错);sm_AllowShaderLoading 关闭后再加载 effect 会断言。
  2. 变量布局漂移:资源实例数据与当前 effect 变量不匹配时会运行期重建并置错误标志——这是“需要重新生成资源”的信号,而非正常路径。
  3. bucket 掩码在放置时重算,因此修改 shader 的 draw bucket 不需要重建资源,但需要重新加载 drawable。
  4. technique 缺失:BeginDraw 返回 0 时上层不绘制(不会报错,除非开启 WARN_ON_MISSING_TECHNIQUE)。
  5. 剔除回调是线程局部的全局状态,游戏在若干阶段临时置空/替换,务必成对恢复。
  6. DrawMulti 要求矩阵集足够大:模型的矩阵索引必须小于 ms.GetMatrixCount()(断言提示“尝试绘制无骨骼的铰接 drawable”)src/dev_ng/rage/base/src/rmcore/lodgroup.cpp:473-474。
  7. PS3 上 drawable 默认在 SPU 执行,超限对象回退 PPU 并输出美术警告。

未决问题 / 证据不足#

  • grmGeometryQB::DrawInstanced、grmGeometryEdge::Draw 与 PS3 drawablespu.cpp 的完整流程未逐行阅读。
  • grmShaderGroup::Load 的文本格式细节、grmShaderSorter(shadersorter.*)是否在 GTA V 中启用未核实(grmModel::Draw 中对 sorter 的使用被注释掉 src/dev_ng/rage/base/src/grmodel/model.cpp:1208)。
  • curvedmodel.*(rage_curvedModel.fx)在游戏中的用途未追踪。
  • 游戏的 drawable 资源扩展名与字典组织由 05-world/11-rendering 页面负责,本页未展开。