title: Cazalis 溶解换衣系统实战记录(V3) description: 2026-05-27 V3版 — Cazalis zigai 5套衣服溶解换衣系统(含Blanchir),基于 Cazalis_FX_Modified_V3.controller created: 2026-05-10 updated: 2026-05-28 type: case-study tags: [vrchat, cazalis, dissolve, liltoon, animator, v3]
Cazalis 溶解换衣系统实战记录(V3)
[!NOTE] 本文档为 V3 版本(基于
Cazalis_FX_Modified_V3.controller),对应审计报告dissolve-transition-system-cazalis.md的修正版。 如需查看 V3 之前版本(已废弃),请参考dissolve-transition-system-cazalis-legacy.md。V3 核心变更:参数
Wardrobe_Int1→taozhuang,Layer 索引 21-24 → 18-21,Clip 路径更新为yuanpi/mox/xiuxian/ksmy。
架构概览
- Controller:
Assets/Cazalis/Animation/Animator/Cazalis_FX_Modified_V3.controller(27层,索引 0-26) - Dissolve Clips:
Assets/Cazalis/Animation/FX/yuanpi/、mox/、xiuxian/、ksmy/、Blanchir/下 - 控制参数:
taozhuang(Int, saved=true, default=0) - 5个溶解 Layer,index 18-22 + 25,Override 模式
- 1个内衣控制 Layer,index 26,Override 模式,weight=1.0
Layer 对应关系
|| Layer | 名称 | Int值 | 衣服 | |-------|------|-------|------| | 18 | 4S_Origin | 0 | 原皮(yuanpi) | | 19 | 4S_Lujian | 1 | 露肩短裙(mox) | | 20 | 4S_Casual | 2 | 休闲服(xiuxian) | | 21 | 4S_Cardigan | 3 | 开衫毛衣(ksmy) | | 22 | 4S_Wahua | 4 | 娃花 | | 25 | 4S_Blanchir | 5 | 绑带上衣(Blanchir) | | 26 | 4S_Underwear | - | 内衣统一控制层 |
注:taozhuang=4 对应娃花(现有换装菜单第5项),不在4S溶解层中。 5 | 绑带上衣(Blanchir) | | 26 | 4S_Underwear | - | 内衣统一控制层 |
注:taozhuang=4 对应娃花(现有换装菜单第5项),不在4S溶解层中。### 4 状态循环(V3 标准命名)
两条独立的判断路径:
穿上线: A_Closed → (taozhuang == val) → B_Appearing → (ON clip 播完) → C_Opened
脱下线: C_Opened → (taozhuang != val) → D_Disappearing → (OFF clip 播完) → A_Closed
⚠️ 旧版命名(已废弃):Hide/Appear/Show/Disappear → 请统一使用 V3 标准:A_Closed / B_Appearing / C_Opened / D_Disappearing
各状态含义:
- A_Closed:默认状态,保持溶解隐藏(_DissolveParams.z = 1.7, m_IsActive = 0)
- B_Appearing:条件满足时进入,播放 ON clip(_DissolveParams.z 1.7 → -0.5)
- C_Opened:ON clip 播完后进入,保持可见(_DissolveParams.z = -0.5, m_IsActive = 1)
- D_Disappearing:条件不满足时进入,播放 OFF clip(_DissolveParams.z -0.5 → 1.7)
- OFF clip 播完后 必须有 Transition 跳回 A_Closed,否则状态机卡住
stateDiagram-v2
A_Closed --> B_Appearing : taozhuang == val
B_Appearing --> C_Opened : ON clip 播放完毕 (1秒)
C_Opened --> D_Disappearing : taozhuang != val
D_Disappearing --> A_Closed : OFF clip 播放完毕 (1秒)
| 层面 | 负责什么 | 例子 |
|---|---|---|
| Animator 状态机 | 什么时候播什么动画 | A_Closed → B_Appearing 的 Transition 条件 taozhuang == val |
| 动画层 | 状态里持有的 AnimationClip | B_Appearing 状态持 ON clip,C_Opened 状态持 Show clip 等 |
| AnimationClip | 进入状态后具体怎么动 | _DissolveParams.z 从 1.7 走到 -0.5 |
对应关系:
状态机层: A_Closed → B_Appearing → C_Opened → D_Disappearing → A_Closed
↓ ↓ ↓
动画层: OFF值 ON clip Show clip OFF clip
每个状态需要配置: - 自己持有的 clip(进入后播放的内容) - 到下一个状态的 Transition(播完后怎么跳)
Transition 条件说明:
| 路径 | 条件 | 类型 |
|---|---|---|
| A_Closed → B_Appearing | taozhuang == val |
穿上线:参数匹配时显现 |
| B_Appearing → C_Opened | ON clip 播放完毕 | 播完自动跳 |
| C_Opened → D_Disappearing | taozhuang != val |
脱下线:参数不匹配时溶解 |
| D_Disappearing → A_Closed | OFF clip 播放完毕 | 播完自动跳回隐藏 |
⚠️ 遗漏 D_Disappearing → A_Closed 的 Transition 是常见错误:OFF clip 播完后状态机卡在 D_Disappearing 结束位置,再次触发切换时条件冲突导致卡顿。
材质配置标准(lilToon 溶解效果)
一、渲染模式
- 镂空(Cutout) ✅ — 使用
Hidden/lilToonCutout - 透明(Transparent) — 透明必须使用 Two Pass,对应
Hidden/lilToonTwoPassTransparentden/lilToonCutout` - 透明(Transparent) — 透明必须使用 Two Pass,对应
Hidden/lilToonTwoPassTransparent### 二、溶解设置
| 项目 | 值 | 说明 |
|---|---|---|
| 溶解方式 | UV | 不是世界坐标 |
| 形状 | 点(Point) | _DissolveParams.x = 2 |
| 坐标 X | 0 | _DissolvePos.x = 0 |
| 坐标 Y | 1 | _DissolvePos.y = 1 |
| 噪点图 | Cazalis_Reflection_Noise | Assets/Cazalis/Cazalis_Reflection_Noise.png |
| 噪点强度 | 0.3 | _DissolveNoiseStrength = 0.3 |
| 纹理颜色 | #387AE9 | RGB: (56, 122, 233) |
| 边界(完全显现) | -0.5 | _DissolveParams.z = -0.5 |
| 边界(完全溶解) | 1.7 | _DissolveParams.z = 1.7 |
| 边界默认值 | -0.5 | 默认可见,不溶解 |
| Shader Keyword | GEOM_TYPE_BRANCH_DETAIL | 必须启用 |
| 动画过渡时间 | 1.1 秒 | 2026-05-28 从1.2s改为1.1s |
三、Clip 中溶解参数动画
| Clip | 作用 | _DissolveParams.z 变化 | m_IsActive |
|---|---|---|---|
| A_Closed(OFF 值) | 保持隐藏 | 1.7 保持不变 | 自己的 SMR = 0 |
| ON(显现) | 衣服出现 | 1.7 → -0.5 | 结束时 m_IsActive = 1 |
| OFF(溶解) | 衣服消失 | -0.5 → 1.7 | 结束时 m_IsActive = 0 |
| C_Opened(Show 值) | 保持显示 | -0.5 保持不变 | 自己的 SMR = 1 |
| -0.5 → 1.7 | 结束时 m_IsActive = 0 |
||
| C_Opened(Show 值) | 保持显示 | -0.5 保持不变 | 自己的 SMR = 1 |
原皮 (8 SMR):
- Cazalis_Cloth.mat — Cutout
- Cazalis_Cloth_Alpha.mat — TwoPassTransparent
- Cazalis_Pumps.mat — Cutout
- Cazalis_Hair.mat — Cutout
- Cazalis_Underwear_Bra.mat — Cutout
- Cazalis_Underwear_Shorts.mat — Cutout
露肩短裙 (12 SMR) — Assets/choco/Seraphic Bloom Dress/Material/:
- Tex1_2_1.mat — TwoPassTransparent
- Tex2_2.mat — Cutout
- Tex3_2.mat — Cutout
- Tex4_2_1.mat — Cutout
- M.mat — Cutout
- C.mat — Gem (skip)
休闲服 (5 SMR) — Assets/AyuElla/SilentSkinPeek/Material/:
- Stripe_White.mat — TwoPassTransparent
- accessory.mat — TwoPassTransparent
开衫毛衣 (8 SMR) — Assets/Rest/Awakoi_Code/Mat/:
- Cloud.mat (UV2/Outer) — Cutout
- Cloud.mat (UV1/Tops) — Cutout
- Cloud.mat (UV1/Pants) — Cutout
- Cloud.mat (UV1/Tops_Ribbon) — Cutout
- Mono.mat (UV1/Leg_Ribbon) — Cutout
- Brown.mat (UV2/Boots) — Cutout
- Brown.mat (UV2/Frill) — TwoPassTransparent
Blanchir (3 SMR) — Assets/Cazalis/Qukumu/Blanchir/Cazalis/Data/:
- blanchir Cazalis.mat — Cutout/Transparent(单一材质覆盖3个SMR)
- 材质溶解参数: DP=(2,0,1.7,0), DissolvePos=(0,1,0,0), NoiseMask=Cazalis_Reflection_Noise, NoiseStrength=0.3, DissolveColor=(0.22,0.48,0.91,1)
s=(0,1,0,0), NoiseMask=Cazalis_Reflection_Noise, NoiseStrength=0.3, DissolveColor=(0.22,0.48,0.91,1)## V3 Clip 路径
| 衣服 | taozhuang 值 | ON Clip | OFF Clip |
|---|---|---|---|
| 原皮 | 0 | yuanpi_ON.anim |
yuanpi_OFF.anim |
| 露肩短裙 | 1 | mox_ON.anim |
mox_OFF.anim |
| 休闲服 | 2 | xiuxian_ON.anim |
xiuxian_OFF.anim |
| 开衫毛衣 | 3 | ksmy_ON.anim |
ksmy_OFF.anim |
| Blanchir | 5 | blanchir_ON.anim |
blanchir_OFF.anim |
Clip 存放路径:Assets/Cazalis/Animation/FX/yuanpi/ / mox/ / xiuxian/ / ksmy/ / Blanchir/(Blanchir大写B开头)
[!WARNING] V3 之前(旧版
Wardrobe_Int1方案)使用Origin_Dissolve_*.anim,各套装共用同一套 clip,导致切换时显示错乱。V3 必须每套衣服独立 ON/OFF clip,且 clip 内只控制自己那套的 SMR。
Blanchir 第5套换装(2026-05-27 新增)
概要
Blanchir 是 Cazalis zigai02 的第5套换装,3个 SMR(内衬/裙子/上衣),共用单一材质 blanchir Cazalis.mat。
动画层配置
- Layer 名称:
4S_Blanchir - Layer 索引: 25
- 参数:
taozhuang = 5(Int, saved=true, default=0) - Override 模式
- 状态/过渡/条件: 与现有 4 套(Origin/Lujian/Casual/Cardigan)完全一致
动画 Clip
| Clip | 路径 | 作用 |
|---|---|---|
Blanchir_ON.anim |
Assets/Cazalis/Animation/FX/ |
溶解显现,_DissolveParams.z 1.7 → -0.5 |
Blanchir_OFF.anim |
Assets/Cazalis/Animation/FX/ |
溶解消失,_DissolveParams.z -0.5 → 1.7 |
Blanchir_Hide.anim |
Assets/Cazalis/Animation/FX/ |
A_Closed 状态隐藏,维持 _DissolveParams.z = 1.7 |
注:Clip 仅包含材质溶解参数和
m_IsActive曲线,无骨骼位置/旋转曲线。
菜单配置
- 菜单名: 绑带上衣(原英文名 Blanchir 已改名)
- 图标:
Assets/Auto_Iron/blanchir.png(GUID=5db9f00dca0ac3240bf9abf2ffac4837, 512×512) - taozhuang 值: 5
Assets/Auto_Iron/blanchir.png(GUID=5db9f00dca0ac3240bf9abf2ffac4837, 512×512) - taozhuang 值: 5### Boots ShapeChanger
Blanchir_Cazalis/Boots 已补全 ModularAvatarShapeChanger 组件:
- 3个 blendshape:Shrink_Toe=100, Shrink_Foot=100, Shrink_Ankle=100
- referencePath="Body_Base",ChangeType=Set
- 配置与 Cloud/Boots 完全一致
材质溶解参数
| 属性 | 值 | 说明 |
|---|---|---|
_DissolveParams |
(2, 0, 1.7, 0) |
x=2=Point模式, z=1.7=完全溶解 |
_DissolvePos |
(0, 1, 0, 0) |
从头顶溶解 |
_DissolveNoiseMask |
Cazalis_Reflection_Noise |
项目统一噪点图 |
_DissolveNoiseStrength |
0.3 |
噪点强度 |
_DissolveColor |
(0.22, 0.48, 0.91, 1) |
溶解边缘蓝色光晕 (#387AE9) |
Blanchir Prefab 配置
- Prefab:
Blanchir_Cazalis.prefab(MA 预配置版) - 根缩放: 建议 1.04(匹配 zigai 体型)
- 骨骼合并:
Armature.1带有ModularAvatarMergeArmature组件,MA 构建时自动合并 - 无鞋子部件:
Foot_Heel = 0,符合规则
A_Closed 配置说明
⚠️ 文档勘误:原文档错误标注 A_Closed 状态不能设
writeDefaultValues=true。正确做法有两种:
- 理论推荐:
writeDefaultValues=true+ 搭配 Hide clip(Hide clip 的 w 值建议 0.1,w=0 会导致溶解边缘锯齿)- Cazalis 实际做法: 所有 5 套 4S 层的 A_Closed 都用 OFF clip +
writeDefaultValues=false,在 VRChat 中验证正常工作Blanchir 新层按现有 4 套做法配置以保持一致。
内衣统一控制层 — Layer 26 (4S_Underwear)(2026-05-28 新增)
问题背景
原皮和抹胸切换时 Shorts(内裤)不显示。根因:多层 Override 冲突 — 5个衣服层都是 Override 模式,每个层的 OFF clip 都写了 Shorts/Bra 的隐藏值。当原皮/抹胸层用 ON clip 显示 Shorts 时,其他未激活层的 OFF clip 在后面覆盖了 ON clip 的值。
修复方案
- 从所有10个 ON/OFF clip 移除 Shorts/Bra 曲线(40条),消除多层覆盖冲突
- 新建 Layer 26
4S_Underwear专用层统一管理内衣显隐 - 修复 Layer 26 的 defaultWeight=0→1.0(新建层默认 weight=0 不生效) 突
- 新建 Layer 26
4S_Underwear专用层统一管理内衣显隐 - 修复 Layer 26 的 defaultWeight=0→1.0(新建层默认 weight=0 不生效)### Layer 26 架构
2状态:
- Hide:Shorts m_IsActive=0 + _DissolveParams.z=1.7,Bra m_IsActive=0 + z=1.7
- ShowShorts:Shorts m_IsActive=1 + z=-0.5,Bra m_IsActive=0 + z=1.7
6个过渡条件:
- Hide → ShowShorts:taozhuang == 0(原皮)/ taozhuang == 1(抹胸)/ taozhuang == 5(绑带上衣)
- ShowShorts → Hide:taozhuang == 2(休闲)/ taozhuang == 3(开衫)/ taozhuang == 4(全脱)
配置: - blendingMode = Override - defaultWeight = 1.0(⚠️ 必须手动设,新建层默认为0) - 过渡时间 = 1.1s(与衣服溶解同步)
显隐对照表
| taozhuang | 衣服 | Shorts | Bra | Layer 26 状态 |
|---|---|---|---|---|
| 0 | 原皮 | ✅ | ❌ | ShowShorts |
| 1 | 露肩短裙 | ✅ | ❌ | ShowShorts |
| 2 | 休闲服 | ❌ | ❌ | Hide |
| 3 | 开衫毛衣 | ❌ | ❌ | Hide |
| 4 | 全脱 | ❌ | ❌ | Hide |
| 5 | 绑带上衣 | ✅ | ❌ | ShowShorts |
动画文件
| Clip | 路径 | 内容 |
|---|---|---|
Underwear_Hide.anim |
Assets/Cazalis/Animation/FX/ |
Shorts a=0 z=1.7, Bra a=0 z=1.7 |
Underwear_ShowShorts.anim |
Assets/Cazalis/Animation/FX/ |
Shorts a=1 z=-0.5, Bra a=0 z=1.7 |
| Underwear_ShowShorts.anim | Assets/Cazalis/Animation/FX/ | Shorts a=1 z=-0.5, Bra a=0 z=1.7 |### ⚠️ Layer defaultWeight 陷阱
通过 C# API 创建 AnimatorControllerLayer 时,defaultWeight 默认为 0,层不会生效。必须手动设为 1.0。
修复方法:
var newLayer = new AnimatorControllerLayer();
newLayer.name = l26.name;
newLayer.stateMachine = l26.stateMachine;
newLayer.defaultWeight = 1f;
newLayer.blendingMode = AnimatorLayerBlendingMode.Override;
var layers = fx.layers;
layers[26] = newLayer;
fx.layers = layers;
EditorUtility.SetDirty(fx);
AssetDatabase.SaveAssetIfDirty(fx);
过渡时间调整(2026-05-27)
5 个 4S 换装层共 10 条溶解过渡(每层出现 + 消失各 1 条)时长统一调整为 1.1 秒,其余非溶解过渡保持 0 秒不变。Layer 26 (4S_Underwear) 的过渡也使用 1.1 秒。
调整历程:1s → 1.5s → 1.2s → 最终 1.1s。 1.1 秒,其余非溶解过渡保持 0 秒*不变。Layer 26 (4S_Underwear) 的过渡也使用 1.1 秒。
调整历程:1s → 1.5s → 1.2s → 最终 1.1s。## 失败教训
-
材质写不入问题: 对
choco/、AyuElla/、Rest/目录下的材质调用mat.SetFloat("_DissolveParams.z", 1.7f)无效(缓存问题)。必须直接编辑 .mat 文件内容然后AssetDatabase.Refresh()。 -
不能共用 dissolve clip: 一开始所有衣服用了同一个
Origin_Dissolve_ON.anim,但该 clip 内写死了原皮的 m_IsActive 状态,导致切到其他衣服时显示错乱。每套衣服必须有自己的 ON/OFF/Show clip,只控制自己那套的 SMR。 -
lilToon 渲染模式: 普通
lilToonshader 是不透明的,必须换到Hidden/lilToonCutout或Hidden/lilToonTwoPassTransparent变体。不能只靠_TransparentMode参数。 -
切换卡顿(未解决): 快速切换衣服时有时会卡住。怀疑多个 dissolve layer 同时播放导致状态机冲突。可能的解决方向:减少同时激活的 dissolve layer,或加保护逻辑。
-
Hide clip w 值: Hide clip 的
w值(_DissolveParams 的 a 分量)不应设为 0,最佳实践为 0.1。w=0 会导致溶解边缘出现锯齿。 -
多层 Override 冲突(2026-05-28 已解决): 多个 Override 层同时写同一属性时,后面的层会覆盖前面的值。原本5个衣服层的 OFF clip 都写了 Shorts/Bra 的隐藏值,导致原皮/抹胸层的 ON clip 显示 Shorts 时被其他层覆盖。解决方案:将冲突属性(Shorts/Bra)提取到专用 Layer 26 (4S_Underwear) 统一管理,不在多个层中重复写入同一属性。
-
新建 Layer 的 defaultWeight=0 陷阱(2026-05-28 发现): 通过 C# API 创建 AnimatorControllerLayer 时 defaultWeight 默认为 0,层不会生效。必须手动创建副本设 defaultWeight=1.0 再替换回 layers 数组。直接修改
layers[idx].defaultWeight不生效,因为layers[idx]返回的是副本。 创建副本设 defaultWeight=1.0 再替换回 layers 数组。直接修改layers[idx].defaultWeight不生效,因为layers[idx]返回的是副本。8. Skill 模板 bug(已修复): 共修复 14 个 VRChat 溶解相关 skill 的 bug,严重问题包括: cazalis-dissolve-system-v3: 错误标注 A_Closed 不能设writeDefaultValues=true,正确应为wd=true+ Hide clipliltoon-dissolve-material-batch: YAML 中_DissolveParams的 a 值误设为 0,应改为 0.1vrchat-4state-toggle-dissolve-system:hideClip未定义 bug,导致编译失败vrchat-dissolve-transition-system: AnimatorConditionMode 补全等修复 em:hideClip` 未定义 bug,导致编译失败-
vrchat-dissolve-transition-system: AnimatorConditionMode 补全等修复## 项目路径 -
项目根:
E:/ALCOM/project/Cazalis - V3 Controller:
Assets/Cazalis/Animation/Animator/Cazalis_FX_Modified_V3.controller - Dissolve Clips:
Assets/Cazalis/Animation/FX/yuanpi/、mox/、xiuxian/、ksmy/ - 材质分散在 4 个目录:
Assets/Cazalis/Material/— 原皮Assets/choco/Seraphic Bloom Dress/Material/— 露肩短裙Assets/AyuElla/SilentSkinPeek/Material/— 休闲服Assets/Rest/Awakoi_Code/Mat/— 开衫毛衣Assets/Cazalis/Qukumu/Blanchir/Cazalis/Data/— Blanchir