<div style="padding:15px 15px 2px 20px; border-radius:2px; margin-bottom:20px; border:1px solid #ddd">

<callout>
<p style="margin-bottom:0.75em"><b>Licensing</b></p>!!
将 Spine 官方运行时整合到自建应用程序中[需要持有 Spine 许可证](/git/spine-runtimes/spine-unity#licensing).
</callout>

<div style="font-size: 1.6em; margin: 0 0 .5em;">spine-unity 运行时文档</div>!!
* [安装运行时](/spine-unity-installation)
* [示例](/spine-unity-examples)
* [资产](/spine-unity-assets)
* [主要组件](/spine-unity-main-components)
* [工具组件](/spine-unity-utility-components)
* [渲染](/spine-unity-rendering)
* [时间轴扩展 UPM 软件包](/spine-unity-timeline)
* [懒加载扩展 UPM 软件包](/spine-unity-on-demand-loading)
* [常见问题(FAQ)](/spine-unity-faq)
</div>!!

# 渲染

## Materials

![](/img/spine-runtimes-guide/spine-unity/raptor-atlas-page.png)

每个atlas页中的Texture都需要自己的Material, Material会在导入skeleton时自动创建. 当使用除 `Normal` 外的槽位blend模式时, 运行时也会为每种blend模式额外创建Materials(使用PMA的 `Additive` 除外, 因为它可以用正常Material渲染). [SkeletonRenderer](/spine-unity-main-components#SkeletonRenderer组件) 会逐帧管理MeshRenderer中的Materials数组, 这些Materials取决于当前分配的附件图片和包含这些附件的AtlasAssets, 以及槽位使用了何种blend模式.
> **请注意:** 直接修改Material数组是毫无意义的, 因为后续的 `LateUpdate()` 会覆盖掉这些更改. 所以应该使用[SkeletonRendererCustomMaterials](/spine-unity-utility-components#SkeletonRendererCustomMaterials) 或 [SkeletonGraphicCustomMaterials](/spine-unity-utility-components#SkeletonGraphicCustomMaterials) 组件来替换Material. 也可以在 *_Atlas* 资产中设置Materials来更改所有实例的Materials. 但修改 *_Atlas* 资产之后, 需要在 `SkeletonRenderer` 组件的 `SkeletonData Asset` 参数处点击 `Reload` 来重新加载使用了新atlas materials的skeleton.

> **请注意:** `Spine/SkeletonGraphic*` 着色器只能在搭配 `SkeletonGraphic` 组件时才能正常渲染materials. 切勿在 `SkeletonGraphic` 组件中使用URP着色器, LWRP着色器或如 `Spine/Skeleton` 这类普通着色器. 缘由详见 [SkeletonGraphic - Material重要使用须知](/spine-unity-main-components#Material重要使用须知) 一节.

### Material切换和绘制调用

如果附件分布在了多个atlas页上或者有不同的blend模式 (这里不妨以material `A` 和material `B` 为例), 运行时会根据绘制顺序访问Materials顺序来设置Materials数组.

若附件访问顺序为:
>
> 1. 是A中的附件
> 2. 是A中的附件
> 3. 是B中的附件
> 4. 是A中的附件

则Material数组将会是:
>
> 1. Material A (满足附件1和附件2)
> 2. Material B (满足附件3)
> 3. Material A (满足附件4)

Materials数组中的每个Material都对应着一次 [绘制调用](http://docs.unity3d.com/Manual/DrawCallBatching.html). 因此大量的Material切换会削弱性能表现.

Dragon示例展示了这种有大量绘制调用的用例:
![](/img/spine-runtimes-guide/spine-unity/render_spineunity_alternatingmaterials.png)

因此建议将附件打包到尽可能少的atlas页中, 并根据绘制顺序将附件分组并入atlas页以减少无谓的material切换. 请参阅 [Spine Texture Packer: 文件夹结构](/spine-texture-packer#文件夹结构) 一节以了解如何在Spine atlas中编排atlas区域.

### 更改特定实例的Materials

> **请注意:** 直接修改 [SkeletonRenderer](/spine-unity-main-components#SkeletonRenderer组件) 中的material数组没有意义, 因为随后的 `LateUpdate()` 调用会覆盖掉它们. 如果下文中的方法都不适合你的用例, 可以用 [`SkeletonAnimation.OnMeshAndMaterialsUpdated`](/spine-unity-main-components#生命周期) 回调委托手动地逐帧替换 `MeshRenderer.Materials`. 在给 `MeshRenderer.Materials` 分配了atlas materials后, 运行时会在 `LateUpdate()` 结束时调用这个回调.

#### CustomMaterialOverride和CustomSlotMaterials

[SkeletonRenderer](/spine-unity-main-components#SkeletonRenderer组件) 可以覆盖(override)某个槽位上的materials, 或者替换掉生成的(resulting) materials.

要在运行中的某个 [SkeletonRenderer](/spine-unity-main-components#SkeletonRenderer组件) 实例上, 用新material替换掉原始material, 可以用 `SkeletonRenderer.CustomMaterialOverride` 属性:

```csharp
// Use the following code to programmatically query the original material, see note below.
// MeshRenderer.material will not work, MeshRenderer.sharedMaterial might fail in some cases.
if (originalMaterial == null)
 originalMaterial = skeletonAnimation.SkeletonDataAsset.atlasAssets[0].PrimaryMaterial;

skeletonAnimation.CustomMaterialOverride[originalMaterial] = newMaterial; // to enable the replacement.
skeletonAnimation.CustomMaterialOverride.Remove(originalMaterial); // to disable that replacement.
```

> **请注意:** 请勿使用表达式 `originalMaterial = skeletonAnimation.GetComponent!lt!MeshRenderer!gt!().material`, 因为 `.material` 返回的是主Material的一个实例拷贝而非Material本身. 同样也不建议写成 `originalMaterial = skeletonAnimation.GetComponent!lt!MeshRenderer!gt!().sharedMaterial`, 因为若还未给主Material赋值时 (例如活动帧尚无活动附件, 或者过早调用了这行代码时), 这种写法可能会返回null.

要仅替换某个槽位上的Material, 可以用 `SkeletonRenderer.CustomSlotMaterials`:

```csharp
skeletonAnimation.CustomSlotMaterials[slot] = newMaterial; // to enable the replacement.
skeletonAnimation.CustomSlotMaterials.Remove(slot); // to disable that replacement.
```

#### 保持渲染合批状态下Tint Skeletons

在skeleton实例上使用多种 `Materials` 或 `MaterialPropertyBlocks` 会破坏渲染合批(batching). 如果你不修改material的其他属性而只需tint某个skeleton实例, 则应使用 `Skeleton.R .G .B .A` 颜色属性. 为了使tinting生效, 必须在SkeletonRenderer组件的检查器中启用 [`Advanced - PMA Vertex Colors`](/spine-unity-main-components#进阶设置参数).

```csharp
public Color color = Color.white;
...
skeleton = GetComponent<SkeletonRenderer>().Skeleton;
...
skeleton.R = color.r;
skeleton.G = color.g;
skeleton.B = color.b;
skeleton.A = color.a;
```

这些skeleton颜色属性只设置顶点颜色而不改变其他material属性.

tinting某个附件时也能这么写:

```csharp
slot = skeleton.FindSlot(slotname);
...
slot.R = slotColor.r;
slot.G = slotColor.g;
slot.B = slotColor.b;
slot.A = slotColor.a;
```

> **请注意:** 如果动画会修改了附件的颜色值, 那么在 [SkeletonAnimation.UpdateComplete](/spine-unity-main-components#生命周期) 这类回调应用了动画之后, 务必记得把每个槽位的颜色值改回来.

#### MaterialPropertyBlocks

![](/img/spine-runtimes-guide/spine-unity/materialpropertyblock-demo.gif)  

使用 [Renderer.SetPropertyBlock](http://docs.unity3d.com/ScriptReference/Renderer.SetPropertyBlock.html) 可以设置某个 `MeshRenderer` 中material的属性值.

```csharp
MaterialPropertyBlock mpb = new MaterialPropertyBlock();
mpb.SetColor("_FillColor", Color.red); // "_FillColor" is a named property on the used shader.
mpb.SetFloat("_FillPhase", 1.0f); // "_FillPhase" is another named property on the used shader.
GetComponent<MeshRenderer>().SetPropertyBlock(mpb);

// to deactivate the override again:
MaterialPropertyBlock mpb = this.cachedMaterialPropertyBlock; // assuming you had cached the MaterialPropertyBlock
mpb.Clear();
GetComponent<Renderer>().SetPropertyBlock(mpb);
```

> **请注意:** 在 `MaterialPropertyBlock` 中使用的参数名 (如 `_FillColor` 或 `_FillPhase`), 要匹配着色器中的变量名. 请注意, 着色器变量名不是显示在检查器中的名称 (例如 `Fill Color` 和 `Fill Phase`). 要查看着色器的参数名称, 需要打开 `.shader` 文件 (点击material旁边的齿轮图标, 选择 `Edit Shader`) 查看最前面的 `Properties { .. }` 代码段, 其中有全部参数的列表. 在如下这行代码中, **最左边的**名字就是参数名 `_FillColor`:

```
_FillColor ("Fill Color", Color) = (1,1,1,1)
^^^^^^^^^^
```

着色器变量名通常以 `_` 字符开头, 且从不包含任何空格. 它旁边的字符串 `"Fill Color"` 就是在检查器中显示的名称.

你可以在示例场景 `Spine Examples/Other Examples/Per Instance Material Properties` 中找到更改特定实例中material属性的演示.

> **优化提示**
>
> - 使用Renderer.SetPropertyBlock设置不同的Material属性值会破坏渲染器合批(batching)操作. 当MaterialPropertyBlock的参数一致时 (例如tint颜色均置为绿色) 渲染器才会合批渲染.
> - 每当你改变或添加了MaterialPropertyBlock的属性值时, 都需要调用 `SetPropertyBlock` 来设置参数. 但你可以把MaterialPropertyBlock保存在类成员中, 如此在改变某个属性值时就无需再实例化出一个新MaterialPropertyBlock了.
> - 当需要经常更改某个属性时, 你可以使用静态方法 `Shader.PropertyToID(string)` 来缓存该属性ID(int值), 而无需每次都访问MaterialPropertyBlock中字符串重载过的setter.  

## 透明度与绘制顺序(Draw Order)

所有spine-unity着色器均使用alpha blending来清晰地绘制附件边缘的半透明过渡区域. 若没有使用alpha blending(比如设置成了固定的透明度阈值) 则边缘会产生尖锐的狗牙状轮廓, 类似于锯齿(aliasing artifacts)效果.

然而alpha blending有个老毛病, 就是无法用z-buffer进行自动深度排序. 所以运行时是从后到前渲染三角形, 然后将各部件分层绘制的. 运行时会为每个 [SkeletonRenderer](/spine-unity-main-components#SkeletonRenderer组件) 组件生成一个网格, 并按照Spine中设置的 [槽位绘制顺序](/spine-basic-concepts#插槽) 绘制三角形. 一次绘制调用便可将skeleton中排好序的单网格部件全部绘制出来.

如若网格间有交叠, spine-unity则会利用Unity中的各种渲染顺位机制来判断哪个网格应该在上面. 按照标准的spine-unity设置, 整个skeleton网格的渲染顺序由以下多个因素决定:

1. **[相机深度(Camera depth)](http://docs.unity3d.com/ScriptReference/Camera-depth.html).** *相机的多机位设置会影响渲染顺序.*
1. **Material.renderQueue.** *设置它后将覆盖着色器的 `Queue` 标签.*
1. **着色器的 `Queue` 标签.** *在 `Spine` 着色器中与其他sprites一样, 默认为 `"Transparent"` 队列.*
1. **[Sorting Group](https://docs.unity3d.com/ScriptReference/Rendering.SortingGroup.html) 组件.** *当该组件附加在MeshRenderer的GameObject或其任意父GameObject上时会影响渲染顺序.*
1. **渲染器的 [SortingLayer](https://docs.unity3d.com/ScriptReference/SortingLayer.html) 和 [图层内的SortingOrder](https://docs.unity3d.com/ScriptReference/Renderer-sortingOrder.html).**
1. **相机透视.** *可以把相机配置为 [使用平面距离(正交相机)或是透视距离(透视相机)](http://docs.unity3d.com/ScriptReference/Camera-transparencySortMode.html).*

若场景中的数个渲染器处于相同的sorting layer, 且sorting order和着色器的 `Queue` 标签也相同, 则可以通过控制与相机的相对距离来控制Spine GameObjects的渲染顺序. Unity的相机类内置了 [transparencySortMode](http://docs.unity3d.com/ScriptReference/Camera-transparencySortMode.html) 属性可供使用.

### Sorting Layer和Order in Layer

[SkeletonRenderer](/spine-unity-main-components#SkeletonRenderer组件) 组件的检查器中提供了 `Sorting Layer` 和 `Order in Layer` 两个属性, 它们本质上修改的是 `MeshRenderer` 的 [sortingLayerID](http://docs.unity3d.com/ScriptReference/Renderer-sortingLayerID.html) 和 [sortingOrder](http://docs.unity3d.com/ScriptReference/Renderer-sortingOrder.html) 属性. 这些属性储存在 `MeshRenderer` 内部而非 `SkeletonRenderer` 之中.

可用如下代码访问这些属性:

```csharp
GetComponent<MeshRenderer>().sortingOrder = 1; // Change the Order in Layer to 1. 
```

### 防止排序错误

![](/img/spine-runtimes-guide/spine-unity/incorrect-sorting.png)

在使用正交相机时, 特别容易出现使用多页atlas的skeleton其渲染排序有误的情况.
在skeleton的GameObject上添加一个 [Sorting Group](https://docs.unity3d.com/ScriptReference/Rendering.SortingGroup.html) 组件就能解决这个问题. 另一种变通之法是轻微旋转一下相机, 比如将摄像机transform旋转中的Y值置为0.001.

### 在Skeleton部件之间渲染其他对象

![](/img/spine-runtimes-guide/spine-unity/skeleton-render-separator-spineboy.png)

有时需要在角色的各部件之间显示其他的GameObjects, 例如角色爬到树上的过程中应该在树干图层之上显示一条腿, 同时在树干之下显示另一条腿.

spine-unity的方案是使用 [SkeletonRenderSeparator](/spine-unity-utility-components#SkeletonRenderSeparator) 组件, 它能将一个skeleton分割为多个部件.

### 淡入或淡出Skeleton

当降低skeleton的alpha值来实现半透明skeleton时, alpha blending会导致skeleton下层的部件意外露出. 这是一个很常见的问题, 而其成因是运行时绘制每个三角形时都会应用透明度.

![](/img/spine-runtimes-guide/spine-unity/CommonError-AlphaFadeOut-Comparison.png)

解决该问题的一个办法是使用一张临时的 [RenderTexture](https://docs.unity3d.com/Manual/class-RenderTexture.html).
先在正常的不透明度下将整个角色渲染到RenderTexture上, 然后再按所需的透明度将RenderTexture绘制到场景中. 建议使用我们提供的 [SkeletonRenderTexture](/spine-unity-utility-components#SkeletonRenderTexture) 和[SkeletonRenderTextureFadeout](/spine-unity-utility-components#SkeletonRenderTextureFadeout) 组件来实现这一功能. 在示例场景 `Spine Examples/Other Examples/RenderTexture FadeOut Transparency` 中也能找到对这两个组件使用方法的演示.

诚然这只是实现淡出效果的多种方法之一. 可能还有其他更简单的解决方案, 比如用纯色逐渐地tinting skeleton, 或逐渐减小尺寸等方法来淡出Skeleton. 你也可以从已推出的2D游戏中汲取灵感, 因为RenderTextures是一种过去很少使用且开销很大的方案.

## 着色器

![](/img/spine-runtimes-guide/spine-unity/stretchyman-only-small.png)

spine-unity运行时内置多种不同着色器. 新导入的skeleton material默认用的是 `Spine/Skeleton` 着色器. 你可以通过Material的着色器参数来选用其他着色器. 以下为Spine内置着色器列表.

> **重要提示:** **`SkeletonGraphic`** 组件只支持兼容 `CanvasRenderer` 的特殊着色器. 详情参见 [SkeletonGraphic - Material重要使用须知](/spine-unity-main-components#Material重要使用须知) 一节.

> **重要提示:** spine-unity运行时只提供了 [**内置渲染管线(Built-In Render Pipeline)**](https://docs.unity3d.com/Manual/built-in-render-pipeline.html) 着色器, 它们与 [**Universal Render Pipeline(通用渲染管线)**](https://unity.com/srp/universal-render-pipeline) 项目并不兼容. 如果你需要使用通用渲染管线, 请换用 [Spine URP着色器扩展UPM包](/spine-unity-rendering#URP着色器扩展UPM包) 中的 URP 着色器.

> **请注意:** Spine着色器尚不支持 [*延迟着色渲染路径*](https://docs.unity3d.com/Manual/RenderTech-DeferredShading.html).

1. **Spine/Skeleton** *(默认着色器)*  
 无光照(Unlit)透明着色器. 不写入深度缓冲区(z-buffer).
1. **Spine/Skeleton Graphic** *(SkeletonGraphic默认着色器)*  
 用于 [SkeletonGraphic](/spine-unity-main-components#SkeletonGraphic组件) 组件的无光照透明着色器. 不写入Z-buffer. 当该着色器搭配 `CanvasGroup` 一起使用时, 不支持 `Additive` blend模式. 若有此需求应使用 `Spine/Skeleton Graphic Tint Black` 着色器. 由于CanvasRenderer的限制, 只能使用单个texture.
1. **Spine/Skeleton Lit**  
 简单光照(Lit)透明着色器, 不支持法线贴图. 不写入z-buffer.
1. **Spine/Skeleton Lit ZWrite**  
 简单光照透明着色器, 不支持法线贴图. 写入z-buffer.
1. **Spine/Skeleton Fill**  
 可自定义颜色叠加(color overlay)的无光照透明着色器. 不写入z-buffer.
 `FillColor` 决定叠加何种颜色而 `FillPhase` 设置颜色叠加的强度.  
1. **Spine/Skeleton Tint**  
 可自定义双色tint的无光照透明着色器. 其深色tinting与浅色tinting是分开着色的, 该功能被称为 [tint black](/spine-slots#着成黑色). 不写入z-buffer.  

 `Tint Color` 会给texture的浅色着色, 而texture的深色tint使用 `Black Point` 参数. 相较于一般的多色blending, tint会使texture比其原色更亮. 当把 `Tint Color` 和 `Black Point` 置为同一个颜色时, tint结果会是一个纯色叠加(overlay). 当把 `Tint Color` 设置为黑色而 `Black Point` 设置为白色时, 则会将texture反色.

1. **Spine/Skeleton Tint Black**  
 ![](/img/spine-runtimes-guide/spine-unity/tint-black-demo.gif)

 每个Spine动画槽位均可 [tint black](/spine-slots#着成黑色) 的无光照透明着色器.
 不写入z-buffer.  
 Spine为槽位提供了 [Tint Black](/spine-slots#Tint-black) 功能, 可以在动画中tint black.  

 需要 (对tint color顶点数据) 进行单独设置:

- 应在 [SkeletonAnimation](/spine-unity-main-components#SkeletonAnimation组件) 组件检查器的 `Advanced` 面板中启用 `Tint Black`:  
 ![](/img/spine-runtimes-guide/spine-unity/skeletonanimation-inspector-tintblack.png)

1. **Spine/Skeleton Tint Black Additive**  
 每个Spine动画槽位均可 [tint black](/spine-slots#着成黑色) 的无光照透明着色器.
 使用additive blend模式.
 不写入z-buffer.
1. **Spine/SkeletonGraphic Tint Black**  
 `Spine/Skeleton Tint Black` 着色器的 [SkeletonGraphic](/spine-unity-main-components#SkeletonGraphic组件) 版本. 当搭配 `CanvasGroup` 使用时支持 `Additive` blend模式.  

 需要 (对tint color顶点数据) 进行单独设置:

 1. 应在 [SkeletonAnimation](/spine-unity-main-components#SkeletonAnimation组件) 组件检查器的 `Advanced` 面板中启用 `Tint Black`.
 2. 无论是否使用CanvasGroup, 均应将 [SkeletonGraphic](/spine-unity-main-components#SkeletonGraphic组件) 中的material设置为 `Spine/Runtime/spine-unity/Materials` 对应目录下的 `SkeletonGraphicTintBlack` material, PMA和Straight alpha两种工作流均能适配这些material.  
 ![](/img/spine-runtimes-guide/spine-unity/skeletongraphictintblack-material.png)
 3. 选中父级Canvas并在其 `Additional Shader Channels` 中启用 `TexCoord1` 和 `TexCoord2`.  
 ![](/img/spine-runtimes-guide/spine-unity/unity-canvas-texcoord1-texcoord2.png)

 在 `CanvasGroup` 中使用 `Additive` blend模式则需要进行以下设置:

 1. a) spine-unity 4.2 及更高版本: 在 [SkeletonGraphic](/spine-unity-main-components#SkeletonGraphic组件) 组件检查器的 `Advanced` 面板中启用 `CanvasGroup Compatible`.  
 b) 旧版spine-unity: 在 [SkeletonGraphic](/spine-unity-main-components#SkeletonGraphic组件) 组件检查器的 `Advanced` 面板中启用 `Canvas Group Tint Black`.
 2. 启用着色器的 `CanvasGroup Compatible`.
 1. **Spine/Sprite**  
 可配置的复杂着色器, 支持比 `Spine/Skeleton Lit` 着色器更高级的光照.
 可在示例场景 `Spine Examples/Other Examples/Sprite Shaders` 中找到关于 `Spine/Sprite/Vertex Lit` 着色器的演示.
 1. **Spine/Sprite/Unlit**  
  无光照着色器, 支持可配置的blend模式, 支持调整覆盖颜色、色调(hue)、饱和度(saturation)和亮度(brightness). 可配置是否写入z-buffer. 支持雾效(fog).
 1. **Spine/Sprite/Vertex Lit**  
  可配置的blend模式的顶点光照复杂着色器.  
  支持法线贴图(normal map)、次级反照率贴图(secondary albedo map)、金属贴图(metallic map)和自发光贴图(emission map).  
  可配置的颜色渐变(color ramp)以实现卡通渲染效果(cel-shaded look)和基于法线的边缘光照(rim lighting).  
  可配置的覆盖颜色、色调(hue)、饱和度(saturation)和亮度(brightness).  
  可配置是否写入z-buffer. 支持雾效.  
 1. **Spine/Sprite/Pixel Lit**  
  `Spine/Sprite/Vertex Lit` 着色器的像素光照(Pixel lit)版本. 它是唯一一个接受逐像素(per-pixel)实时阴影的着色器.
  这个着色器总是写入z-buffer (因为它使用 `ForwardAdd` pass所以启用了 `ZWrite`).
 1. **Spine/Special**
 1. **Spine/Special/Skeleton Grayscale**  
  灰度强度可调的无光照透明灰度渲染(grayscale rendering)着色器. 不写入z-buffer.
 1. **Spine/Special/Skeleton Ghost**  
  [SkeletonGhost](/spine-unity-utility-components#SkeletonGhost) 组件专用的特殊着色器, 用于渲染踪迹(trail).
 1. **Spine/Blend Modes**  
 Intended for slots that have blend modes `Additive`, `Multiply` and `Screen` assigned in the Spine editor. It is recommended to automatically assign blend mode materials on import via the [SkeletonData Blend Mode Materials](/spine-unity-assets#Skeleton-Data).
 1. **Spine/Blend Modes/Skeleton PMA Additive**  
  无光照透明着色器. 使用additive blend模式. 不写入z-buffer.
 1. **Spine/Blend Modes/Skeleton PMA Multiply**  
  无光照透明着色器. 使用multiply blend模式. 不写入z-buffer.
 1. **Spine/Blend Modes/Skeleton PMA Screen**  
  无光照透明着色器. 使用screen blend模式. 不写入z-buffer.
 1. **Spine/Outline**  
 上述所有着色器都提供了 `Outline` 参数, 启用它就能切换到 `Spine/Outline` 版的着色器, 它用于在skeleton的周边绘制一圈彩色轮廓.
 你可以在示例场景 `Spine Examples/Other Examples/Outline Shaders` 中找到对 `Spine/Outline` 着色器的演示.
 1. **Spine/Outline/OutlineOnly-ZWrite**
  这是一个只渲染轮廓的单pass特殊着色器. 它会写入z-buffer, 以便在有交叠的(overlapping)附件上正确实现轮廓遮蔽(outline occlusion). 该着色器也可以用于渲染包含多种材质的skeleton, 普通的轮廓着色器在这种情况下会把每个子网格的轮廓都渲染出来而不是将skeleton作为整体渲染. 此时可以用诸如 [`RenderCombinedMesh`](#rendercombinedmesh) 这样的组件重渲染组合后的skeleton网格, 并使用该着色器在skeleton下层加上轮廓.

### 后处理效果

景深(DoF, Depth of Field)等一些后处理效果要求着色器能够写入z-buffer (更准确地说是写入有深度预处理(depth pre-pass)的深度缓冲区). 某些Spine着色器在Material中提供了 `Depth Write(深度写入)` (又称`ZWrite`) 参数开关, 而另一些着色器则默认写入z-buffer. 欲了解Spine着色器对z-buffer写入功能的支持性, 请见上文中的着色器列表.

根据项目的图形设置 (或渲染管线资产设置), 仅仅让着色器写入z-buffer可能还不足以支持后处理效果, 例如将渲染管线资产设置为 `URP-HighFidelity` 时就会这样. 这时还需将Material的 `Render Queue` 参数从 `Transparent` 改为 `AlphaTest`.

若Spine的内置着色器限制了你的创作, 你也可以创建自己的修改版Spine着色器, 并将 (每个Pass块里的) `ZWrite Off` 改为 `ZWrite On`, 同时将标记`"Queue"="Transparent"`改为`"Queue"="AlphaTest"`. 当然别忘记更改着色器代码第一行的着色器名称, 以避免命名冲突.

### URP着色器扩展包

通用渲染管线(URP)着色器通过独立的UPM(Unity Package Manager)包提供. 下载和安装UPM包的指南, 请参见 [可选的UPM扩展包](/spine-unity-installation#可选的UPM扩展包) 一节; 而更新UPM扩展包的步骤与须知, 则在 [更新UPM插件包](/spine-unity-installation#更新UPM扩展包) 一节中详述.

URP着色器UPM包提供了专门为Unity通用渲染管线(URP)构建的着色器, 包括2D渲染器功能.

> **请注意:** 切勿在 `SkeletonGraphic` 组件上使用URP着色器. 缘由详见 [SkeletonGraphic - Material重要使用须知](/spine-unity-main-components#Material重要使用须知) 一节.

> **请注意:** Spine URP着色器尚未支持URP中新添加的 [*延迟渲染路径*](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@12.0/manual/rendering/deferred-rendering-path.html) 功能.

**用于2D渲染器的URP着色器** *(请勿用于URP (3D) 前向渲染器)*

1. **Universal Render Pipeline/2D/Spine/Skeleton**  
 URP 2D渲染器版的 `Spine/Skeleton` 着色器.
1. **Universal Render Pipeline/2D/Spine/Skeleton Lit**  
 URP 2D渲染器版的 `Spine/Skeleton Lit` 着色器.
1. **Universal Render Pipeline/2D/Spine/Sprite**  
 URP 2D渲染器版的 `Spine/Sprite/Vertex Lit` 及 `Pixel Lit` 着色器.

**用于3D渲染器的URP着色器** *(请勿用于 URP 2D 渲染器)*

1. **Universal Render Pipeline/Spine/Skeleton**  
 URP版的 `Spine/Skeleton` 着色器.
1. **Universal Render Pipeline/Spine/Skeleton Lit**  
 URP版的 `Spine/Skeleton Lit` 着色器. 可配置为接受逐像素实时阴影.
1. **Universal Render Pipeline/Spine/Sprite**  
 URP版的 `Spine/Sprite/Vertex Lit` 及 `Pixel Lit` 着色器. 接受逐像素实时阴影.
1. **Universal Render Pipeline/Spine/Outline/Skeleton-OutlineOnly**  
 URP版的 `Spine/Outline` 着色器. 由于URP不允许一个着色器包含多个pass, 因此为了实现outline效果, 需要为该着色器单独创建一个material. 你可以考虑使用 [`RenderExistingMesh`](/spine-unity-utility-components#RenderExistingMesh) 组件, 用法详见着色器UPM包中的示例场景 `com.esotericsoftware.spine.URP-shaders/Examples/Outline Shaders URP`. 如果你的skeleton需使用多种material, 那么可以用 [`RenderCombinedMesh`](/spine-unity-utility-components#RenderCombinedMesh) 来替代 `RenderExistingMesh` 组件.

如普通着色器一样, 也能将这些着色器分配给material, 并同时受 `Project Settings - Graphics` 中 `UniversalRenderPipelineAsset` 设置的影响.

可以在UPM包 `com.esotericsoftware.spine.URP-shaders/Examples` 目录中的示例场景里找到以下三个URP着色器演示场景:  
`3D/URP 3D Shaders.unity`, `2D/URP 2D Shaders.unity` 和 `Outline Shaders URP.unity`.

### LWRP着色器扩展包

轻量渲染管线(LWRP)着色器通过独立的UPM(Unity Package Manager)包提供. 下载和安装UPM包的指南, 请参见 [可选的UPM扩展包](/spine-unity-installation#可选的UPM扩展包) 一节; 而更新UPM扩展包的步骤与须知, 则在 [更新UPM插件包](/spine-unity-installation#更新UPM扩展包) 一节中详述.

LWRP着色器扩展包内含Unity轻量渲染管线专用的着色器.

> **请注意:** 切勿在 `SkeletonGraphic` 组件上使用LWRP着色器. 缘由详见 [SkeletonGraphic - Material重要使用须知](/spine-unity-main-components#Material重要使用须知) 一节.

1. **Lightweight Render Pipeline/Spine/Skeleton**  
 LWRP版的 `Spine/Skeleton` 着色器.
1. **Lightweight Render Pipeline/Spine/Skeleton Lit**  
 LWRP版的 `Spine/Skeleton Lit` 着色器.
1. **Lightweight Render Pipeline/Spine/Sprite**  
 LWRP版的 `Spine/Sprite/Vertex Lit` 及 `Pixel Lit` 着色器.

如普通着色器一样, 也能将这些着色器分配给material, 并同时受 `Project Settings - Graphics` 中 `LightweightRenderPipelineAsset` 设置的影响.

可以在UPM包 `com.esotericsoftware.spine.lwrp-shaders-4.2/Examples/LWRP Shaders.unity` 中找到LWRP着色器的演示场景.

### Shader Graph

目前尚未推出官方的Shader Graph Spine着色器或着色器节点. 请注意, 当你从Spine导出使用了 [Straight alpha](/spine-unity-assets#进阶操作-Premultiplied和Straight-Alpha导出) 的texture时, 也可以使用任何非Spine的着色器. 如果你想在其中复刻Spine着色器专用功能, 请参见这两篇论坛帖子: [[帖子1](/forum/Shader-Graph-with-Normal-Map-and-Diffuse-Ramp-15755)] 和 [[帖子2](/forum/Adding-normals-to-a-Shader-Graph-15077)]. 如果还有任何疑问, 欢迎随时来 [Spine论坛的Unity板块](/forum/search.php?keywords=shader+graph) 上发帖咨询.

### Amplify着色器编辑器

虽然官方没有提供Amplify着色器编辑器的着色器模板, 但用户 `Hana` 已在论坛上热心分享了 [他的模板代码](/forum/Spine-Shader-Template-for-Amplify-16267?p=71176#p71176).

### 编写你自己的着色器

第一步是了解Unity中编写自定义着色器的 [通用做法](https://docs.unity3d.com/2020.1/Documentation/Manual/ShadersOverview.html). 特别是 [顶点和片元编程教程](https://docs.unity3d.com/2020.1/Documentation/Manual/SL-ShaderPrograms.html) 是一篇很棒的概述, 理解了其中的概念后便能更容易理解spine-unity着色器的各部分在做什么.

#### 基于已有的spine-unity着色器编写

强烈建议先复制一份已有的spine-unity着色器, 然后逐步修改调整它来实现你期望的效果. 比如在着色器返回最终的颜色值之前添加你的颜色处理代码. 以下代码展示了一个简短的示例, 说明了如何实现一个添加了灰度渲染功能的 `SkeletonGraphic` 修改版着色器:

```
Properties
{
    _GrayIntensity("Intensity", Range(0, 1)) = 1 // this line was added to provide a Material property
    [..]
}
```

```
sampler2D _MainTex;
float _GrayIntensity; // this parameter was added
..
fixed4 frag (VertexOutput IN) : SV_Target
{
    ..
    color.rgb = lerp(color.rgb, dot(color.rgb, float3(0.3, 0.59, 0.11)), _GrayIntensity); // this line was added
    return color;
}
```

#### 使用非Spine着色器或可视化着色器编辑器时的附加须知

使用这类着色器前, 务必要考虑spine-unity和非Spine着色器间的以下区别:

1. 渲染任意Spine skeleton时, 必须用 `Cull Off` 设置来**禁用Backface culling(隐表面剔除)**.
2. Spine着色器通常无需法线, 因此在使用光照着色器时可能需要在组件中启用 `Advanced - Add Normals`.
3. Spine着色器通常无需切线, 因此在使用法线贴图时, 可能需要在组件中启用 `Advanced - Solve Tangents`.
4. Spine着色器默认使用Premultiply Alpha textures,  所以要么  
 a) 将atlas textures导出为 [Straight alpha](/spine-unity-assets#进阶操作-premultiplied和straight-alpha导出), 要么  
 b) 将着色器的blend模式改为PMA blend模式 `Blend One OneMinusSrcAlpha`.
5. Spine顶点色通常是PMA颜色. 当使用透明槽位或 `Additive` 槽位时, 你可以  
 a) 将着色器的blend模式改为PMA blend模式 `Blend One OneMinusSrcAlpha` 并使用PMA atlas textures, 或者
 b) 在组件中禁用 `Advanced - PMA Vertex Colors`(此后将不渲染 `Additive` 槽位).

当然, UI和非UI着色器的通用准则仍是:

1. 不要在 `SkeletonAnimation` 或 `SkeletonMecanim`上使用UI着色器.
2. 不要在 `SkeletonGraphic` 上使用非UI着色器.

#### `Spine/Skeleton` 着色器的基本解析

本节将对 [`Spine/Skeleton` 着色器](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton.shader) 进行简单解析. 在运行时导入了Spine texture atlas并据此生成了Material后, 默认就会使用该着色器.
`Spine/Skeleton` 着色器相当精简且经典, 它具有以下特点:

- Premultiply Alpha (PMA) blending
- 不写入深度缓冲区
- 无光照
- 无背面剔除(backface culling)
- 无雾效
- 通过顶点色预乘来tint texture
- 可选择使用straight alpha替换PMA textures
- 包含用于投射实时阴影的 `"ShadowCaster"` pass
- Material属性:
  - _MainTex "Main Texture"
  - _StraightAlphaInput "Straight Alpha Texture"
  - _Cutoff "Shadow alpha cutoff"
 -进阶参数:
    - _StencilRef "Stencil Reference"
    - _StencilComp "Stencil Comparison"
    - Outline 参数 (_OutlineWidth "Outline Width" 等等)

#### 着色器详解

- **Premultiply Alpha (PMA) blending**  

 ```hlsl
 Blend One OneMinusSrcAlpha
 ```

 [(Spine-Skeleton.shader:27)](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton.shader#L27)

 Blending的本质是这一句 `result_rgba = frag_output_rgba * src_factor + framebuffer_rgba * dst_factor`.  
 非标准的 `Blend One OneMinusSrcAlpha` PMA blend模式可以在单个渲染pass中既渲染 `Additive` blend模式的槽位又渲染 `Normal` blend模式的槽位. 通过将上面这行代码中的 `SrcFactor` 置为 `One` (而非 `SrcAlpha` 值) 便能实现这一效果. 于是被 `OneMinusSrcAlpha` 加权后的 `framebuffer_rgba` 值将与原始的 `frag_output_rgba` 值相加:  
  a) 对于 `Normal` blending, 片元着色器会将 `RGB` 乘以 `A`, 其中 `A` 保持不变.  
  b) 对于 `Additive` blending, `RGB` 不乘以 alpha, 而是将 `A` 置为 `0`, 最终result_rgba的计算就变为了 `result_rgba = frag_output_rgba + (1-0) * framebuffer_rgba`.  

 当在 `SkeletonRenderer` 或 `SkeletonGraphic` 组件上启用 [进阶设置 - `PMA Vertex Colors`](/spine-unity-main-components#进阶设置参数) 后, 将把 `Normal` 和 `Additive` 槽位blend模式的输出值作为顶点色隐式传入着色器:  

 ```hlsl
 struct VertexInput {
     float4 vertexColor : COLOR
 }
 ```

 [(Spine-Skeleton.shader:50)](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton.shader#L50)

 当PMA顶点色乘以采样到的PMA texture颜色时, 会自动应用槽位的 `Normal` 或 `Additive` blend模式:

 ```hlsl
 return (texColor * i.vertexColor);
 ```

 [(Spine-Skeleton.shader:74)](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton.shader#L74)

 因此在你的着色器中正确支持 `Normal` 和 `Additive` PMA blend模式需要:

 1. 将blend函数定义为 `Blend One OneMinusSrcAlpha`
 2. 将texture颜色与顶点色相乘
 3. 在组件中启用 [进阶设置 - `PMA Vertex Colors`](/spine-unity-main-components#进阶设置参数).

 > 如果你想使用基于标准blend模式 `Blend SrcAlpha OneMinusSrcAlpha` 的着色器, 且不需要 `Additive` 槽位, 则需要确保从Spine导出的是 [Straight alpha](/spine-unity-assets#进阶操作-premultiplied和straight-alpha导出`) atlas textures.

- **不写入深度缓冲区**  

 ```hlsl
 ZWrite Off
 ```

 [(Spine-Skeleton.shader:26)](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton.shader#L26)

 典型的alpha-blended 2D sprite着色器均不写入深度缓冲区. 透明物体将根据 [`Camera.transparencySortMode`](https://docs.unity3d.com/ScriptReference/Camera-transparencySortMode.html) 的设置从后向前绘制, 而不会用深度缓冲区来深度排序. `Spine/Skeleton` 与Unity的 `Sprites/Default` 着色器均有这一特性.

 > 如果你想使用带有深度写入的着色器, 请确保 `SkeletonRenderer` 或 `SkeletonGraphic` 组件中的 `Advanced - Z-Spacing` 值置为非 `0` 值, 以防止Z-Fighting现象 (特别是使用了光照之后).
 请注意, 使用深度缓冲区可能会在半透明区域的外围渲染出错误效果, 例如边缘锯齿(aliasing effects on edges).

- **无光照**  
 场景中的所有灯光均不影响 `Spine/Skeleton` 着色器, 着色器将始终以100%强度(intensity)渲染 `texColor * i.vertexColor`.

 如果需要在着色器中使用光照, 建议选择一个现有的光照着色器, 并根据需求对其修改.
 不过将 `Lighting Off` 简单地改为 `Lighting On` 并不能实现效果, 还需要在你的顶点着色器 (用于逐顶点照明(per-vertex lighting)) 或片元着色器函数 (用于逐像素照明(per-pixel lighting)) 中计算光照, 再乘以颜色强度. 要注意的是, URP, URP-2D和标准渲染管线着色器的光照计算方法各异, 所以参考着色器代码时一定要选择对应管线的着色器.

- **无背面剔除**  

 ```hlsl
 Cull Off
 ```

 [(Spine-Skeleton.shader:25)](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton.shader#L25)

 渲染Spine skeleton的唯一硬性要求是必须 **禁用backface culling(背面剔除)**, 对于2D着色器尤其如此.

 > 主流的3D着色器都有背面剔除功能. 但在部件尺寸被缩放为负值或是翻转skeleton朝向的情况下, Spine网格上的背面剔除功能会导致某些三角形消失.

- **无雾效**  
 `Spine/Skeleton` 着色器不支持雾效.

 在着色器上支持雾效需要在着色器代码中额外添加一些顶点参数和函数调用. 以下是 `UnityCG.cginc` 的代码:

 ```hlsl
 multi_compile_fog Will compile fog variants.
 UNITY_FOG_COORDS(texcoordindex) Declares the fog data interpolator.
 UNITY_TRANSFER_FOG(outputStruct,clipspacePos) Outputs fog data from the vertex shader.
 UNITY_APPLY_FOG(fogData,col) Applies fog to color "col". Automatically applies black fog when in forward-additive pass.
 Can also use UNITY_APPLY_FOG_COLOR to supply your own fog color.
 ```

 你可以查看 `Spine/Sprite/Unlit` 着色器来了解如何在你的着色器中支持雾效:

 ```hlsl
 #pragma multi_compile_fog
 ```

 [(SpritesUnlit.shader:76)](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Sprite/SpritesUnlit.shader#L76)

 ```hlsl
 UNITY_FOG_COORDS(1) // to declare it at the free attribute TEXCOORD1
 ```

 [(SpriteUnlit.cginc:L34)](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Sprite/CGIncludes/SpriteUnlit.cginc#L34)

 ```hlsl
 UNITY_TRANSFER_FOG(output,output.pos);
 ```

 [(SpriteUnlit.cginc:64)](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Sprite/CGIncludes/SpriteUnlit.cginc#L64)

- **通过顶点色预乘来tint texture**  
 详见前文中的 *Premultiply Alpha (PMA) blending* 一节.

- **可选择使用straight alpha替换PMA textures**  
 由于 `Spine/Skeleton` 着色器的blend模式恒定为PMA blending, 所以没有premultiplied alpha颜色的输入texture就得通过采样将texture转换为PMA颜色. 下面这几行代码是该功能的实现:  

 ```
 // bool Material parameter, enables the _STRAIGHT_ALPHA_INPUT shader keyword when enabled
 [Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
 ..
 // compiles the shader in two variants so that shader keywords can switch between both variants
 #pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
 ..
 // when enabled, multiply texture rgb values by the texture alpha value.
 #if defined(_STRAIGHT_ALPHA_INPUT)
 texColor.rgb *= texColor.a;
 #endif
 ```

- **包含用于投射实时阴影的 `"ShadowCaster"` pass**  
 在第二个pass中写上 [`Tags { "LightMode"="ShadowCaster" }`](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton.shader#L77-L116) 就能让这个pass被自动识别为阴影投射器(shadow caster) pass, 因为它带有 `LightMode` 标签.
 `ShadowCaster` pass不会写入RGB颜色, 但会将深度信息写入阴影缓冲区(shadow buffer). 因此它必须置为[`ZWrite On`](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton.shader#L81).
 由于(在渲染半透明物体时)无法在深度缓冲区中写入半透明深度值, 所以只能选择要么将片元写入深度缓冲区, 要么就丢弃片元 (此时将不投射阴影). 这是通过调用阈值(thresholding)函数实现的:  

 ```hlsl
 clip(texcol.a * i.uvAndAlpha.a - _Cutoff);
 ```

 [(Spine-Skeleton.shader:114)](https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton.shader#L114)

 其中 `_Cutoff` Material参数表示alpha阈值, 若 x &lt; 0 则 `clip(x)` 丢弃片元.

- **Material属性:**
  - _MainTex "Main Texture"  
  主texture.
  - _StraightAlphaInput "Straight Alpha Texture"  
  参见上文中的 *"可选择使用straight alpha替换PMA textures"* 一节.
  - _Cutoff "Shadow alpha cutoff"  
  参见上文中的 *"包含用于投射实时阴影的 `"ShadowCaster"` pass"* 一节.
  - 进阶参数:
    - _StencilRef "Stencil Reference"  
   用于遮罩控制(mask interaction).
    - _StencilComp "Stencil Comparison"  
   用于遮罩控制, 该参数会根据 `Mask Interaction` 属性值由 `SkeletonRenderer` 或 `SkeletonGraphic` 组件来决定.
    - Outline parameters (_OutlineWidth "Outline Width", etc.)  
   在切换到轮廓着色器 `Spine/Outline/Skeleton` 时使用, 普通的非轮廓着色器 `Spine/Skeleton` 不使用该参数.

[下一节: 时间轴扩展 UPM 软件包](/spine-unity-timeline)
[上一节: 工具组件](/spine-unity-utility-components)