1. Complex 材质公共开销if (!SubstrateMaterialComplexity.bIsSimple !SubstrateMaterialComplexity.bIsSingle !bCustomEncoding !bIsFastWaterPath) { // Packed Header SubstrateMaterialRequestedSizeByte UintByteSize; // Shared Local Bases SubstrateMaterialRequestedSizeByte UsedSharedLocalBasesCount * SUBSTRATE_PACKED_SHAREDLOCALBASIS_STRIDE_BYTES; }增加Packed Header4 Bytes Shared Local Basis UsedSharedLocalBasesCount × BasisStride触发条件不是 Simple 不是 Single 不是 Custom Encoding 不是 Fast Water这部分是 Complex 材质的公共成本不属于某一个 Feature。2. Simple 材质if (SubstrateMaterialComplexity.bIsSimple) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; // Disney material SubstrateMaterialRequestedSizeByte UintByteSize; break; }增加Simple Header 4 Bytes Disney Material 4 Bytes 总计 8 Bytes3. Single 材质else if (SubstrateMaterialComplexity.bIsSingle) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; }增加Single Header4 BytesF90、Fuzz、SSS 等功能可能使材质进入 Single但它们不一定各自对应一个独立的 UintByteSize。4. Custom Encoding 材质else if (bCustomEncoding) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; }增加Custom Encoding Header4 BytesHair、Eye 等特殊 BSDF 会走这类路径。5. Fast Water 路径else if (bIsFastWaterPath) { // Header Data SubstrateMaterialRequestedSizeByte UintByteSize; // Data SubstrateMaterialRequestedSizeByte UintByteSize; break; }增加Water Header4 Bytes Water Data 4 Bytes 总计 8 Bytes6. Complex 材质的 Weight 数据顶层 Slabelse { // BSDF state with gray scale weight SubstrateMaterialRequestedSizeByte UintByteSize; }增加BSDF State 灰度 Weight4 Bytes非顶层 Slabelse if (bMayHaveColoredWeight) { // BSDF state SubstrateMaterialRequestedSizeByte UintByteSize; // Color weight SubstrateMaterialRequestedSizeByte UintByteSize; // Light transmittance weight SubstrateMaterialRequestedSizeByte UintByteSize; }增加BSDF State 4 Bytes Color Weight 4 Bytes Light Transmittance Weight 4 Bytes 总计 12 Bytes所以同样的 Slab放在顶部和底部Byte Count 可能不同。7. Slab 基础数据进入case SUBSTRATE_BSDF_TYPE_SLAB:后通常会先增加SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize;增加Slab 基础数据8 Bytes这不是某个 Feature 的专属成本而是 Slab 的基础 Payload。8. Edge Color、Second Roughness、Simple Clear Coatif (It.Has(ESubstrateBsdfFeature::EdgeColor) || It.Has(ESubstrateBsdfFeature::SecondRoughness) || It.Has(ESubstrateBsdfFeature::SimpleClearCoat)) { SubstrateMaterialRequestedSizeByte UintByteSize; }增加额外 Slab 参数块4 Bytes重点是这几个条件使用了||Edge Color Second Roughness Simple Clear Coat它们命中任意一个通常只增加一个数据块不是三个 Feature 分别收费。9. 底层 SSS 或非底层 MFP源码会先判断类似const bool bHasSSS It.bIsBottom It.Has(ESubstrateBsdfFeature::SSS); const bool bIsSimpleVolume !It.bIsBottom It.Has(ESubstrateBsdfFeature::MFP);然后if (bHasSSS || bIsSimpleVolume) { SubstrateMaterialRequestedSizeByte UintByteSize; }增加底层 SSS 4 Bytes 非底层 MFP Simple Volume4 Bytes但两者共用同一个条件块bHasSSS || bIsSimpleVolume所以不是SSS 4 MFP 4而是SSS 或 MFP 命中同一个数据块 → 410. Fuzzif (It.Has(ESubstrateBsdfFeature::Fuzz)) { SubstrateMaterialRequestedSizeByte UintByteSize; }增加Fuzz 数据4 Bytes但 Fuzz 还可能改变材质的 Simple/Single/Complex 分类因此实际总成本可能不只是 4 Bytes。11. Glintif (It.Has(ESubstrateBsdfFeature::Glint)) { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; }增加Glint4 × 4 Bytes 16 BytesGlint 是当前这些 Feature 中直接增加 Payload 较多的一类。12. Specular Profileif (It.Has(ESubstrateBsdfFeature::SpecularProfile)) { SubstrateMaterialRequestedSizeByte UintByteSize; }增加Specular Profile4 Bytes但 Specular Profile 还会使材质进入 Complex Special 路径因此它的最终影响不一定只有 4 Bytes。13. Haircase SUBSTRATE_BSDF_TYPE_HAIR: { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; break; }增加Hair Custom Data8 Bytes14. Eyecase SUBSTRATE_BSDF_TYPE_EYE: { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; break; }增加Eye Custom Data8 Bytes如果 Eye 使用额外自定义参数还会继续增加if (It.Has(ESubstrateBsdfFeature::ExCustomParameter)) { SubstrateMaterialRequestedSizeByte UintByteSize; }即每个额外自定义数据块4 BytesUE5.7 的源码分析中也明确展示了 Eye Custom Encoding 会在HLSLMaterialTranslator.cpp中增加额外 uint 数据。UE5.7.1 源码分析15. UnlitUnlit 不会像 Slab 一样增加普通材质 Payloadcase SUBSTRATE_BSDF_TYPE_UNLIT: { // Never stored, it goes directly into the scene as emitted luminance. break; }也就是Unlit 不需要存储完整 Slab 数据汇总逻辑增加量Complex Packed Header4 BytesShared Local Basis数量 × BasisStrideSimple Header4 BytesSimple Disney Material4 BytesSingle Header4 BytesCustom Encoding Header4 BytesFast Water Header4 BytesFast Water Data4 Bytes顶层灰度 Weight4 Bytes非顶层彩色 Weight12 BytesSlab 基础数据8 BytesEdge Color / Second Roughness / Clear Coat4 Bytes底层 SSS 或非底层 MFP4 BytesFuzz4 BytesGlint16 BytesSpecular Profile4 BytesHair Custom Data8 BytesEye Custom Data8 BytesEye 额外自定义参数每项 4 Bytes不应直接理解为 Byte Count 增量的内容下面这些通常不是直接的 UintByteSize但会改变最终结果F90 Anisotropy Shared Local Basis 数量 材质是顶层还是底层 参数混合 材质简化 Simple / Single / Complex 分类 Complex Special 路径 uint32 对齐最后还会进行类似RequestedSizeInUint DivideAndRoundUp( SubstrateMaterialRequestedSizeByte, 4u);因此最终 Byte Count 的正确理解是最终 Byte Count 公共布局 BSDF 状态 Slab 基础数据 Feature 数据块 Shared Local Basis 对齐1.bCustomEncoding表示当前 BSDF 不使用通用 Slab 编码而是使用自己的专用编码方式。典型类型Substrate Hair BSDF Substrate Eye BSDF它们的参数结构和 Slab 不一样所以不能直接按普通 Slab 处理。逻辑通常类似if (BSDFType SUBSTRATE_BSDF_TYPE_HAIR || BSDFType SUBSTRATE_BSDF_TYPE_EYE) { bCustomEncoding true; }影响不走普通 Complex Header 不走普通 Slab Payload 不按 Slab 的 Fuzz / SSS / MFP 分支计算 进入 Hair / Eye 自己的编码逻辑例如case SUBSTRATE_BSDF_TYPE_EYE: UintByteSize; UintByteSize;所以bCustomEncoding不是“增加一个 Feature”而是切换到另一套 BSDF 存储格式2.bIsFastWaterPath表示当前材质是 Single Layer Water并且可以使用专用的快速水体路径。对应Substrate SingleLayerWater BSDF它不是Slab MFP而是一个独立的特殊 BSDF。它通常走else if (bIsFastWaterPath) { // Header Data SubstrateMaterialRequestedSizeByte UintByteSize; // Data SubstrateMaterialRequestedSizeByte UintByteSize; break; }也就是Water Header4 Bytes Water Data 4 Bytes 总计 8 Bytes它会跳过普通 Slab 数据 Complex Shared Local Basis 普通 Slab Feature 分支所以它是一条专门优化过的水体编码路径。注意Substrate SingleLayerWater BSDF和Slab 连接 MFP 做 Simple Volume不是同一种路径。3、Colored Weight 使用条件Colored Weight不是由 MFP 是否为 RGB 直接决定的而主要由材质树拓扑决定。会使用或预留 Colored Weight 的情况存在 Vertical Layer 并且当前 Slab 不是顶层 Slab源码逻辑通常是bMayHaveColoredWeight !It.bIsTop;非顶层 Slab 可能受到上层材质的彩色透射 颜色吸收 Diffuse / F0 颜色影响 Light Transmittance先把所有增加byte count的代码先汇总于此if (!SubstrateMaterialComplexity.bIsSimple !SubstrateMaterialComplexity.bIsSingle !bCustomEncoding !bIsFastWaterPath) // header written later, { // Packed Header SubstrateMaterialRequestedSizeByte UintByteSize; // Shared local bases between BSDFs SubstrateMaterialRequestedSizeByte UsedSharedLocalBasesCount * SUBSTRATE_PACKED_SHAREDLOCALBASIS_STRIDE_BYTES; } switch (It.OperatorType) { case SUBSTRATE_OPERATOR_BSDF: { // we have encountered a new BSDF which directly link to a single closure evaluation SubstrateMaterialClosureCount; // From the compiler side, we can only assume the top layer has gray scale luminance weight. const bool bMayHaveColoredWeight !It.bIsTop; if (SubstrateMaterialComplexity.bIsSimple) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; // Disney material SubstrateMaterialRequestedSizeByte UintByteSize; break; // Stop here } else if (SubstrateMaterialComplexity.bIsSingle) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; } else if (bCustomEncoding) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; } else if (bIsFastWaterPath) { // Header Data SubstrateMaterialRequestedSizeByte UintByteSize; // Data SubstrateMaterialRequestedSizeByte UintByteSize; break; // Stop here } else if (bMayHaveColoredWeight) { // BSDF state SubstrateMaterialRequestedSizeByte UintByteSize; // Color weight SubstrateMaterialRequestedSizeByte UintByteSize; // Light transmittance weight SubstrateMaterialRequestedSizeByte UintByteSize; } else { // BSDF state with gray scale weight SubstrateMaterialRequestedSizeByte UintByteSize; } switch (It.BSDFType) { case SUBSTRATE_BSDF_TYPE_SLAB: { // Compute values closer to the reality for HasSSS and IsSimpleVolume, now that we know that we know the topology of the material. const bool bIsSimpleVolume !It.bIsBottom It.Has(ESubstrateBsdfFeature::MFPPluggedIn); const bool bHasSSS It.bIsBottom It.Has(ESubstrateBsdfFeature::SSS); SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; // When using blendable GBuffer, do not account for featuress byte requests. as downcasting is done SubstrateExport(). // Otherwise this would cause material to not compile as we do not demote feature during simplification. if (Substrate::IsSubstrateBlendableGBufferEnabled(ShaderPlatform)) { break; } if (It.Has(ESubstrateBsdfFeature::EdgeColor | ESubstrateBsdfFeature::SecondRoughnessOrSimpleClearCoat)) { SubstrateMaterialRequestedSizeByte UintByteSize; } if (bHasSSS || bIsSimpleVolume) { SubstrateMaterialRequestedSizeByte UintByteSize; } if (It.Has(ESubstrateBsdfFeature::Fuzz)) { SubstrateMaterialRequestedSizeByte UintByteSize; } if (It.Has(ESubstrateBsdfFeature::Glint) Substrate::IsGlintEnabled(ShaderPlatform)) { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; } if (It.Has(ESubstrateBsdfFeature::SpecularProfile)) { SubstrateMaterialRequestedSizeByte UintByteSize; } if (It.Has(ESubstrateBsdfFeature::Eye)) { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; } if (It.Has(ESubstrateBsdfFeature::Hair)) { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; } break; } case SUBSTRATE_BSDF_TYPE_HAIR: { // Custom encoding SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; break; } case SUBSTRATE_BSDF_TYPE_EYE: { // Custom encoding SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; break; }我来计算一下Glint的byte count首先如图一所示Glint属于Complex Special这里是要进去的所以是4 4 8然后如图二它属于else层级的只加48412如图三它属于slab所以是6*4 24 然后24 12等于36计算正确再来计算只连接anisotropy消耗多少byte count首先连接了anisotropy了过后complexity就属于complex了那么这里就会2*48的byte count消耗了这里不属于任何一个标识直接加4目前8412byte count消耗最后只有基础slab数据的开销2*48总消耗812 20byte count消耗再来看为什么glint anisotropy不增加各向异性让我们来看为什么anisotropy glint还是36呢首先anisotroyp glint还是 complex special一样要计算这里header的消耗 2*48老样子走else不涉及任何的分支标识最后这里还是只进入这几个标识6*4 24 总共2488其实最核心的就是因为glint和glintanisotropy的slab类型相同complexity都是complex special新增的anisotropy没有对应feature增加的byte count的开销所以这两个类型最终的总开销是相同的当 Glint 已经把材质推入 Complex Special并且 Shared Local Basis 数量没有增加时再加入 Anisotropy 不会带来额外的 Byte Count它只改变局部 Basis 的类型和内容。最后来一个复合类型的首先上下层覆合关系是complex的所以里面2*48的byte count消耗上面的SubstrateMaterialComplexity是整个材质的MaterialComplexity而不是单个slab的complextiy然后上层slab的operator用的else4下层属于colorweight的3*412最后首先两个slab都要有基础slab信息4*416然后上层slab强行变成了issimplevolume所以再来个4总共计算byte count841216444主要第一看tree header和local basis然后看operator最后看slab这里特别注意这个usesharedlocalbasescount和sharedlocalbasescount没关系别被坑了这里是operator有一个类型是bsdf的那就1就是说有多少没被优化的bsdf slab板实际SharedLocalBasesCount会影响视口里的Memory Transactions但不会影响材质编辑器显示的Byte Count。原因是两处使用了不同的数量。材质编辑器 Byte Count计算 Byte Count 时Normal/Tangent 还没有完成编译无法进行真正的 Basis 去重因此源码直接使用UsedSharedLocalBasesCount SubstrateMaterialEffectiveClosureCount;也就是保守假设一个有效 Closure 一个 Local Basis所以只要 Closure Count 不变实际 Local Bases 从 3 合并为 1编辑器 Byte Count 仍然不变。视口 Memory Transactions最终 Shader 编译完成后UE 已经能够比较 Normal/Tangent 的代码哈希并得到FinalUsedSharedLocalBasesCount运行时 Header 保存的是这个实际数量HEADER_SETSHAREDLOCALBASESCOUNT( Out, InHeader.SharedLocalBases.Count);视口再根据实际数量写入和读取 BasisHeader Normals Packed Header 4 Bytes SharedLocalBasesCount × 4 Bytes因此实际 Local BasesHeader Normals14 1×4 8 Bytes24 2×4 12 Bytes34 3×4 16 Bytes每合并一个 Local Basis实际Memory Transactions就减少4 Bytes / pixel前提是其他 BSDF 数据和可见 Closure 没有变化。所以性能规划里建议明确区分Editor Byte Count 编译期保守预算用于预算检查和材质简化 Viewport Memory Transactions 当前像素实际读取的 Substrate 数据量 Closure Count 实际需要求值的 BSDF 数量 SharedLocalBasesCount 实际唯一 Normal/Tangent Basis 数量你的核心结论完全正确复用相同 Normal/Tangent 是有效优化只是 UE5.7 材质编辑器的 Byte Count 暂时没有体现这项收益收益会在最终 Shader 和视口实际 Memory Transactions 中体现出来。右侧Memory Transactions - Total 44就是当前选中像素实际读取了 44 Bytes。源码用的是SubstrateAddressing.ReadBytes这张图里总差值是材质编辑器 Byte Count 76 Memory Transactions 60 总差值 16 Bytes其中Shared Local Basis 合并节省 8 Bytes 两个非顶层灰度权重节省 8 Bytes灰度 Weight 被压进BSDF.State的 10-bit 字段不需要单独存储 Color Weight。
企业数字化 ERP 产品动态
相关推荐
在 LangChain 中为 Agent 注册工具 在 LangChain 架构体系中,工具(Tools)是 Agent 突破大模型自身预训练知识时空限制、与真实外部世界(如 API、数据库、计算器、操作系统环境)产生交互与副作用的唯一物理桥梁。 所谓“注册工具”,本质上是遵… · 2026/9/24 17:46:25
auditpolmsg.dll丢失不用慌:SFC+DISM官方修复完整指南 遇到报错弹窗“找不到 auditpolmsg.dll”这种提示,先别急着去搜索“dll文件丢失免费下载”,因为我见过太多人因为这一步操作把系统搞得更糟。这个文件名对多数人来说很陌生,但它在 Windows 系统里承担的实际作用,以及它消失背后的… · 2026/9/24 19:19:51
2026年AI后台代理工程化实践:主流工具横评与配置调优指南 1. 为什么2026年还要重新审视AI后台代理2026年开年到现在,我陆续把手上三个项目的后台开发流程做了一轮重构,核心动作只有一个:把AI后台代理从"偶尔用用的辅助工具"升级成"日常开发的基础设施"。这个转变不是跟风&#x… · 2026/9/24 19:19:51
VSCode配置C/C++环境:MinGW方案从入门到调试详解 开始动手前,先说明一下:这篇文章不是来教你怎么“点几个按钮就能跑代码”的,而是想把 Windows 下用 Visual Studio Code 配置 C/C 环境(minGW 方案)这件事从头到尾掰开揉碎讲清楚。我当年第一次上手时,光是… · 2026/9/24 19:19:51
微服务共享库版本漂移引发枚举反序列化500故障排查与根治 上周五下午,我正在处理另一个需求,群里突然有人 我:订单详情接口开始出现 500,而且不是百分百复现,是“偶尔冒一个”。第一反应是看监控,错误率不高,但集中在某个接口上。翻日志时看到异常栈里… · 2026/9/24 19:19:51
JMeter接口加密参数实战:从sign签名到国密算法全解析 这周最烦的一件事,是压测环境里所有请求突然开始报 sign 校验失败。开发那边给的说法很统一:安全要求,所有接口的请求参数必须带上加密签名。Jmeter 脚本里原来的参数直接暴露在请求里,现在必须把加密参数动态生成、动态塞进请求&… · 2026/9/24 19:19:38
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44