On this page
Custom Shader Integration
Read Supported Boundaries and Unsupported Operations first. Passing Build does not establish support for every visual effect.
The goal is to retain your surface appearance while applying ECSAnimator deformation in every relevant geometry pass. Adding an include alone does not complete integration.
1. Choose a route
| Requirement | Route |
|---|---|
| Common PBR, normals, metallic, clipping and object/skeletal motion vectors | Assets > Create > ECSAnimator > Lit Material |
| Custom color, UV, emission and other surface effects | Assets > Create > ECSAnimator > Lit Shader Graph Template (Preview) |
| Existing handwritten Shader with special lighting | Adapt all four geometry passes using the public contract below |
Prefer built-in Lit when its properties cover the effect. For an existing Shader Graph, create a copy from the package template, migrate Fragment nodes/properties/textures and keep the Vertex connections. Connecting the package SubGraph to an existing compatible Graph is possible but requires more setup.
For handwritten HLSL, create a project-owned adapted copy and keep the original as a reference. No automatic converter or standalone handwritten template is supplied. Transparent blending, extra outline geometry passes, Deferred, Cloth and arbitrary runtime vertex deformation are outside the current External profile.
Before migration
- External covers URP 14 Lit/Opaque/Forward Graph and handwritten opaque/cutout Shaders. Effective material renderQueue must be at most 2450.
- Source renderer-to-character-root static transforms must be identity, and Cloth must be absent. This is not merely a localPosition check. If reorganizing hierarchy, correctly adjust mesh/bind data rather than zeroing transforms.
- All renderers in one character must use the same profile, built-in or External. External motion-vector mode must be Camera Motion Only.
- Exported streams are Position, Normal, Tangent and UV0. Effects requiring vertex colors, UV1/UV2 or custom streams cannot assume those inputs survive.
- MaterialPropertyBlock, arbitrary per-instance material properties and manually populated _ECSAnimInstanceMeta are not gameplay extension points. Use supported appearance interfaces or separately validated source materials/artifacts.
Stable ECSAnimator/Lit has five passes and previous skeletal data. Lighting, depth and shadows reuse URP 14 implementations; DepthNormals still evaluates normal maps, while Motion uses current/previous bone and world transforms.
External HLSL/Graph remains Preview / NeedsVerification. It does not promise transparent, Deferred, XR, extra geometry passes, arbitrary vertex displacement or automatic third-party compatibility.
Migration note: the standalone HLSL Lit Template menu, ECSAnimatorLitTemplate.shader, color/alpha callbacks and ToWorld wrappers have been removed. Old generated templates must not include private ECSAnimatorLitPasses.hlsl. Use Graph for ordinary surface customization, or the object-space public contract below for handwritten passes. Public Graph Object float/half entry points remain.
2. Shader Graph workflow
- Choose Assets > Create > ECSAnimator > Lit Shader Graph Template (Preview) and a new file under Assets. The tool creates a connected .shadergraph and .mat without overwriting existing assets.
- Edit Fragment Base Color, Normal, Metallic, Smoothness, Emission and Occlusion. The initial material is gray; old material properties are not copied automatically. Keep Vertex Position/Normal/Tangent connected.
- Transfer texture samples, UV0 operations, color and emission incrementally. For cutout, enable Alpha Clipping and connect Alpha/Alpha Clip Threshold while remaining Opaque.
- Retain hidden _ECSAnimInstanceMeta and animation keywords, assign the material to every source renderer, and set renderer Motion to Camera.
- Save, convert/build and spawn through the generated EntityDesc (
CharacterRuntimeAsset). Rebuild after Shader/material edits. Textures remain ordinary references; derived materials remain snapshots.
An existing compatible Graph can use ECSAnimatorDeformation.shadersubgraph. It calls ECSAnimatorDeformVertexObject and returns object-space data; Graph performs ObjectToWorld once.
Retain:
- ECSANIMATOR_ENTITIES_GRAPHICS: Boolean, Multi Compile, Vertex, off by default.
- ECSANIMATOR_LOCAL_RESOLVE: Boolean, local Multi Compile, Vertex, off by default.
- ECSANIMATOR_COMPACT_HOOK_BINDING, supplied by the canonical SubGraph.
- Hidden Vector4 _ECSAnimInstanceMeta, default zero, as an ordinary UnityPerMaterial property. Do not create duplicate DOTS metadata with that name.
- UniversalForward, DepthOnly, DepthNormals and ShadowCaster passes.
The converter checks final keywords/properties, dependencies, materials and passes. It does not parse private Graph JSON or prove arbitrary node behavior.
3. Handwritten HLSL contract
Declare _ECSAnimInstanceMeta as Vector in Shader Properties and float4 in the existing shared UnityPerMaterial CBUFFER:
[HideInInspector] _ECSAnimInstanceMeta ("ECSAnimator", Vector) = (0,0,0,0)
// Inside the Shader's existing UnityPerMaterial CBUFFER:
float4 _ECSAnimInstanceMeta;
Do not redeclare the entire CBUFFER or introduce duplicate DOTS metadata. Configure each geometry pass:
#pragma target 4.5
#pragma only_renderers d3d11 vulkan metal
#pragma multi_compile_instancing
#pragma multi_compile_local_vertex _ ECSANIMATOR_COMPACT_HOOK_BINDING
#pragma multi_compile_local_vertex _ ECSANIMATOR_LOCAL_RESOLVE
#pragma multi_compile_vertex _ ECSANIMATOR_ENTITIES_GRAPHICS
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
Include Core before the canonical deformation include:
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.asterism.ecsanimator/Runtime/Product/Rendering/Shaders/ECSAnimatorDeformation.hlsl"
Retain Position/Normal/Tangent/UV0, SV_VertexID and UNITY_VERTEX_INPUT_INSTANCE_ID. This is a vertex-function fragment, not a complete Shader. vertexId comes from SV_VertexID; positionOS/normalOS/tangentOS are float3/float3/float4.
UNITY_SETUP_INSTANCE_ID(input);
ECSAnimatorDeformationContext context = ECSAnimatorBeginContext();
ECSAnimatorDeformVertex(context, vertexId,
input.positionOS, input.normalOS, input.tangentOS);
output.positionCS = context.valid
? TransformObjectToHClip(input.positionOS) : float4(2, 2, 2, 1);
Input/output stay in object space. Ordinary GameObject/keyword-off variants pass through unchanged. BeginContext does not take an instanceId: initialize Unity's instance binding first, then metadata is read from DOTS. context.valid rejects invalid bindings. Do not read private Raw buffers or write metadata yourself. Preserve tangent.w, transform world normals with the inverse transpose and multiply tangent handedness by GetOddNegativeScale().
Use exactly one pass for each required LightMode: UniversalForward, DepthOnly, DepthNormals and ShadowCaster. Deform in every pass; apply correct normals, shadow bias and consistent clipping/UVs. The optional final constant argument of ECSAnimatorDeformVertex controls direction output: 0 for position only, 1 for position/normal, default 2 includes tangent. Request only the data a pass needs. Keep binding keywords off on source materials; the converter/runtime changes private clones.
| Pass | Integration |
|---|---|
| UniversalForward | Use deformed object-space data for position, world normal/tangent and surface effects |
| DepthOnly | Deform position; match color-pass UV and Alpha Clip |
| DepthNormals | Deform position/normal; request tangent too when a normal map needs tangent space |
| ShadowCaster | Deform first, then compute shadow projection/normal bias and matching clipping; do not copy the Forward projection unchanged |
Deform each vertex once per pass. Do not combine old GPU/VAT deformation with both the package SubGraph and handwritten deformation. An ordinary URP UsePass without deformation is not a valid character shadow/depth pass. Preserve Unity instance-ID setup/transfer rules; its instanceID is not a plugin character index.
4. Validation boundaries
Earlier source-compilation checks for built-in passes used Unity 2022.3.45f1c1 / URP 14.0.11 and covered D3D, Vulkan and Metal variants. Source compilation is not target-device rendering, numerical or performance qualification.
Graph Metal cross-compilation has a known URP error involving undeclared _FOVEATED_RENDERING_NON_UNIFORM_RASTER. Removing the standalone HLSL template did not fix that issue. Validate handwritten customization on Metal or wait for the Graph issue to be resolved; stable built-in Lit remains available. Editor evidence does not replace Windows Player or mobile device results.
| Symptom | Action |
|---|---|
| Missing keyword/metadata | Generate a current Graph template or complete the handwritten contract |
| Rejected source motion mode | External uses Camera Motion Only; object/skeletal motion uses built-in Lit |
| Double-transformed position | Apply ObjectToWorld exactly once after object-space deformation |
| Shadow/depth silhouette mismatch | Match UV, alpha and cutoff in every pass |
| Source Shader/material changes have no effect | Rebuild instead of editing generated snapshots |
For External, test two-frame motion, non-origin placement/rotation, normals, shadows, clipping and destruction on the target Player. Mirror/negative-scale tests must use a supported Shader test path, not an invalid character-root Spawn scale. Characters do not use per-instance Light Probes; global ambient remains. Structural checks do not produce automatic cross-platform qualification.
5. Iterate and verify
- Establish one visibly animated character using the unmodified template/adaptation as a reference.
- Migrate one effect at a time, such as dissolve, tint or emission. Check ordinary material preview and ECS instances; keyword-off previews correctly show the undeformed mesh.
- Save source Shader/Graph/material, refresh Preview, rebuild and respawn.
- Inspect color, depth, DepthNormals and shadows together. Dissolve/cutout must agree across all four passes. Check normals during turns and at non-origin positions under directional light.
- Test Idle/Run, transitions, placement/rotation, LOD, unload and re-entry. Spawn supports finite positive uniform root scale.
- Build for the actual graphics API, retain DOTS variants and run on the target device. Record Shader/Unity/URP versions, API, diagnostics and comparison images.
Third-party passes, extra vertex inputs and runtime material changes need separate validation; template success alone does not qualify them.