EntityComponentSystemSamples 之 ShaderGraphProperties:在 ECS 实体上实现 Shader Graph 材质属性逐实例覆盖的完整指南 📅 发布时间:2026/9/16 18:45:18 👁 浏览次数: EntityComponentSystemSamples 之 ShaderGraphProperties在 ECS 实体上实现 Shader Graph 材质属性逐实例覆盖的完整指南【免费下载链接】EntityComponentSystemSamples项目地址: https://gitcode.com/GitHub_Trending/en/EntityComponentSystemSamples在 Unity ECSEntities体系中成千上万个实体共享同一份材质但往往需要各自拥有不同的颜色或材质属性。本指南以 EntityComponentSystemSamples 仓库中的GraphicsSamples/URPSamples示例场景 ShaderGraphProperties 为核心完整讲解如何在 Shader Graph 着色器上通过材质覆盖 Authoring 组件material override authoring components实现实体级per-entity的材质属性覆盖涵盖场景操作步骤、Shader Graph 属性声明配置Override Property Declaration Hybrid Per Instance以及对应的 ECS 源码级原理。读完本文你将能独立复现该示例并把自定义 Shader Graph 材质属性接入 ECS 实体的逐实例覆盖管线。示例概览场景里发生了什么ShaderGraphProperties 场景位于GraphicsSamples/URPSamples/Assets/SampleScenes/2. MaterialProperties/ShaderGraphProperties/对应的入口场景文件为ShaderGraphProperties.unity。按示例 README 的描述场景中的立方体cubes使用的是Shader Graph 的 PBR 输出对应源码资源 RGBShader_URP.shadergraph其 Master 节点为PBRMasterNodeSurface Type 为 Opaque并挂接了UniversalPBRSubShader其中一部分立方体的 MeshRenderer 上挂接了材质覆盖 Authoring 组件material override authoring components这些组件会覆盖立方体的颜色color未被覆盖的立方体则保持材质本身的默认颜色。也就是说所有立方体在烘焙后共享同一份渲染资源但通过 per-entity 的覆盖数据每个实体的颜色可以不同。这正是 DOTS 渲染中一材多用、数据驱动变体的核心思路。从资源目录结构SceneAssets可以看到示例提供了多组对比资产资产作用RGBShader_URP.shadergraph/RGBMaterial.mat基础 Shader Graph 与材质开启逐实例覆盖的对照组RGBShader2_URP.shadergraph/RGBMaterial2.mat变体 1用于对比不同属性名或声明方式的差异RGBShader3_URP.shadergraph/RGBMaterial3.mat变体 2RGBShaderNonInstanced_URP.shadergraph/RGBMaterialNonInstanced.mat关闭逐实例GPU Instancing的对比组MaterialPropertiesSettings.lighting场景光照配置同时 SubScenes 下提供CubeSubScene.unity与CubeSubSceneNoOverride.unity两个子场景分别对应带覆盖与不带覆盖的实体内容方便对照验证覆盖行为。逐步操作复现并观察逐实例颜色覆盖按照官方示例文档给出的流程可以在编辑器中完整走一遍选中覆盖目标在 Hierarchy 中选中名为CubeRed的立方体。它是挂载了覆盖组件的代表对象。查看 Authoring 组件在 Inspector 中观察Material Color组件。该组件即是文档提到的材质覆盖 Authoring 组件它把一个名为_Color的 Shader Graph 属性声明为可被 ECS 逐实体覆盖的数据。若你需要覆盖其他自定义属性可以参照 MaterialColor 脚本的写法为每一个自定义 Shader Graph 材质属性创建一个对应的组件详见下文从源码看覆盖机制。核对 Shader Graph 属性声明点击Edit打开该立方体使用的 Shader Graph选中 Color 属性节点观察其Node SettingsOverride Property Declaration处于启用状态且Shader Declaration设置为Hybrid Per Instance。这一步是整个机制成立的关键前提。验证方法很简单在 Play 模式下观察 CubeRed 与同材质其他立方体前者颜色被组件覆盖后者保持材质默认色再把 CubeSubScene 切换为 CubeSubSceneNoOverride覆盖消失颜色回归默认。通过两组子场景与RGBShaderNonInstanced_URP.shadergraph其中属性m_GPUInstanced为false的对比可以直观感受到声明方式决定覆盖是否生效。关键配置逐项解析为什么声明比值更重要逐实例覆盖是否生效不取决于材质球里的颜色值而取决于 Shader Graph 属性在序列化层面如何声明。以 RGBShader_URP.shadergraph 的m_SerializedProperties为例{ typeInfo: { fullName: UnityEditor.ShaderGraph.Internal.Vector4ShaderProperty }, JSONnodeData: {\n \m_Guid\: {...},\n \m_Name\: \Color\,\n \m_DefaultReferenceName\: \Vector4_71CD9CDD\,\n \m_OverrideReferenceName\: \_Color\,\n \m_GeneratePropertyBlock\: true,\n \m_Precision\: 0,\n \m_GPUInstanced\: true,\n \m_Hidden\: false,\n \m_Value\: { \x\: 1.0, \y\: 1.0, \z\: 1.0, \w\: 1.0 }\n} }要点如下m_OverrideReferenceName为_Color这是该属性在最终着色器代码中的统一引用名也是 ECS 侧按名寻址的关键标识m_GPUInstanced为true对应编辑器 Node Settings 中的Hybrid Per Instance声明即属性写入 GPU instancing buffer使每个实例可以携带独立的属性值m_GeneratePropertyBlock为true属性进入材质属性块Property Block体系供渲染管线读取默认值(1,1,1,1)当某实体没有覆盖数据时使用该默认值渲染。对照资源 RGBShaderNonInstanced_URP.shadergraph 中同样的 Color 属性其m_GPUInstanced为false——这正是NonInstanced的含义该变体无法参与逐实例覆盖。两组资源的差异从序列化层面直接印证了编辑器里 Override Property Declaration / Shader Declaration 两个开关的最终效果。从源码看覆盖机制[MaterialProperty]属性与生成组件为每个自定义属性写一个覆盖组件并非凭空操作仓库内已有完整范例。位于 MaterialOverridesSample 的 SceneAssets 下由 Unity 生成的覆盖组件代码揭示了标准写法using Unity.Entities; using Unity.Mathematics; namespace Unity.Rendering { [MaterialProperty(_URPMyColor)] struct URPMyColorVector4Override : IComponentData { public float4 Value; } }以及 float 型属性using Unity.Entities; namespace Unity.Rendering { [MaterialProperty(_URPMyMetallic)] struct URPMyMetallicFloatOverride : IComponentData { public float Value; } }机制要点可以总结为[MaterialProperty(属性名)]特性将 IComponentData 与着色器属性名绑定特性参数必须与 Shader Graph 属性在代码中的引用名一致本例为_URPMyColor、_URPMyMetallic字段类型决定属性类型float4 Value对应 Vector4/Color 属性float Value对应 Float 属性Unity 的 entities.graphics 包会根据组件结构与特性自动生成 GPU instancing 所需的渲染数据组件挂在 MeshRenderer 上作为 Authoring烘焙Bake后成为实体上的 ECS 组件由渲染系统读取并写入实例化 buffer。以此类推ShaderGraphProperties 示例 README 中提到的 MaterialColor 脚本就是针对_Color属性的同类实现struct MaterialColor : IComponentData { public float4 Value; }加[MaterialProperty(_Color)]。在实际使用中你也可以在 MaterialOverridesSample 的生成组件基础上改造出自己的覆盖组件。与仓库内其他材质覆盖示例的横向对照为避免只看到单一案例仓库在同一 2. MaterialProperties 目录下提供了另外两个互补示例用于理解覆盖机制的三种落地形态MaterialOverridesSample无代码覆盖。通过挂在 MeshRenderer 上的 MaterialOverride 组件引用一个 MaterialOverride asset如 URPGraphOverride.asset、URPUnlitOverride.asset来集中配置需要覆盖的属性集合覆盖 URP Lit、URP Unlit 与 Shader Graph 三类着色器适合希望以纯配置方式工作的团队URPLitProperties内置属性覆盖。针对 URP Lit 着色器提供若干现成的 URP Material Property Authoring 组件直接覆盖 color、smoothness、metallic 等内建属性无需自己定义组件ShaderGraphProperties本文主题自定义属性覆盖。面对的是 Shader Graph 中任意自定义属性需要结合[MaterialProperty]组件与 Hybrid Per Instance 声明两条腿走路。三者覆盖的是不同使用场景若目标是 URP 内建属性且不想写代码优先参考 URPLitProperties若希望纯配置驱动参考 MaterialOverridesSample若要覆盖自定义 Shader Graph 属性本文核心则沿用 ShaderGraphProperties 的组件模式。常见问题与排查要点结合场景资源与序列化数据实践中容易踩坑的点主要有三处覆盖不生效检查m_GPUInstancedShader Graph 中该属性必须开启 Override Property Declaration 且 Shader Declaration 为 Hybrid Per Instance。可以打开 RGBShader_URP.shadergraph 与 RGBShaderNonInstanced_URP.shadergraph 对照序列化字段m_GPUInstanced的差异前者true、后者false属性名对不上[MaterialProperty(...)]中的字符串必须与 Shader Graph 属性的引用名完全一致大小写敏感。确认方式是在 Shader Graph 的 Node Settings 中查看最终 Reference Name而不是显示名选错对象/子场景覆盖数据位于 Authoring 组件与子场景烘焙产物中确认观察对象处于 CubeSubScene带覆盖而非 CubeSubSceneNoOverride且 MeshRenderer 上确实挂载了对应覆盖组件。扩展阅读场景与子场景ShaderGraphProperties.unity、CubeSubScene.unity、CubeSubSceneNoOverride.unity着色器与材质资产SceneAssets 目录含 4 组 shadergraph / mat 对比对无代码覆盖方案MaterialOverridesSample 及其 URPGraphOverride.asset内建 URP 属性覆盖URPLitPropertiesHDRP 管线下的同名示例HDRPSamples/ShadergraphProperties其场景结构与操作步骤与 URP 版本一致可用于跨管线对照【免费下载链接】EntityComponentSystemSamples项目地址: https://gitcode.com/GitHub_Trending/en/EntityComponentSystemSamples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考