mirror of
https://github.com/xszyou/Fay.git
synced 2026-08-28 23:00:40 +08:00
1.数字人接口增加数字人指定动作、表情等输出;
2.更换群二维码。
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
code,behavior,affect,intensity,priority,sentimentHint,keywords
|
||||
greeting.hello,nod,smile,0.35,60,0.4,你好|您好|hello|hi|哈喽
|
||||
greeting.welcome,invite,warm,0.78,80,0.9,欢迎光临|欢迎回来|欢迎来到|热烈欢迎|欢迎
|
||||
farewell.goodbye,wave,smile,0.62,72,0.2,再见|拜拜|bye|goodbye|下次见
|
||||
dialogue.confirm,nod,smile,0.42,58,0.3,是的|对的|没错|当然|收到|明白|了解|可以
|
||||
dialogue.reject,reject,serious,0.58,70,-0.6,不是|不对|不行|不可以|不能|no
|
||||
dialogue.think,think,neutral,0.44,74,0.0,让我想想|想一想|考虑一下|这个嘛|容我想想
|
||||
dialogue.question,question,curious,0.52,55,0.0,为什么|怎么回事|真的吗|是什么|怎么
|
||||
dialogue.explain,explain,neutral,0.56,63,0.1,也就是说|换句话说|意思是|其实|具体来说|比如|例如
|
||||
dialogue.recommend,recommend,smile,0.63,66,0.5,建议|推荐|你可以试试|最好|我建议
|
||||
dialogue.summary,summary,neutral,0.4,57,0.1,总之|综上|所以|最后|总结一下
|
||||
guidance.invite,invite,warm,0.74,82,0.7,请进|这边请|跟我来|请坐|往这边
|
||||
guidance.wait,wait,neutral,0.26,65,0.0,稍等|等一下|请稍等|稍后|马上
|
||||
guidance.remind,remind,neutral,0.48,62,0.1,提醒|记得|别忘了|告诉你
|
||||
guidance.warn,warn,serious,0.8,84,-0.4,注意|小心|警告|注意安全
|
||||
social.thanks,thanks,warm,0.46,61,0.4,谢谢|感谢|多谢|thank you
|
||||
social.apology,apology,sorry,0.6,71,-0.3,抱歉|对不起|不好意思|请原谅
|
||||
social.care,care,warm,0.5,64,0.2,还好吗|没事吧|别担心|放心
|
||||
emotion.celebrate,celebrate,excited,0.95,92,1.6,太好了|成功|完成了|真棒|厉害
|
||||
emotion.surprise,surprise,surprised,0.88,86,1.0,天啊|竟然|不会吧|omg|真的假的
|
||||
emotion.sad,sad,sad,0.82,83,-1.2,难过|可惜|遗憾|糟糕|失败了|伤心
|
||||
|
@@ -0,0 +1,93 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
from dataclasses import dataclass
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
|
||||
RULES_PATH = Path(__file__).resolve().parents[1] / "config" / "action_rules.csv"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ActionRule:
|
||||
code: str
|
||||
behavior: str
|
||||
affect: str
|
||||
intensity: float
|
||||
priority: int
|
||||
sentiment_hint: float
|
||||
keywords: Tuple[str, ...]
|
||||
|
||||
|
||||
def _normalize_text(text: str) -> str:
|
||||
return (text or "").strip().lower()
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def load_action_rules() -> Tuple[ActionRule, ...]:
|
||||
try:
|
||||
with RULES_PATH.open("r", encoding="utf-8", newline="") as file:
|
||||
reader = csv.DictReader(file)
|
||||
built_rules: List[ActionRule] = []
|
||||
for row in reader:
|
||||
try:
|
||||
keywords = tuple(
|
||||
k.strip() for k in row["keywords"].split("|") if k.strip()
|
||||
)
|
||||
built_rules.append(ActionRule(
|
||||
code=row["code"],
|
||||
behavior=row["behavior"],
|
||||
affect=row["affect"],
|
||||
intensity=float(row.get("intensity", 0.5)),
|
||||
priority=int(row.get("priority", 0)),
|
||||
sentiment_hint=float(row.get("sentimentHint", 0.0)),
|
||||
keywords=keywords,
|
||||
))
|
||||
except (KeyError, TypeError, ValueError):
|
||||
continue
|
||||
except OSError:
|
||||
return ()
|
||||
|
||||
return tuple(built_rules)
|
||||
|
||||
|
||||
def resolve_action_signal(text: str) -> Optional[Dict[str, object]]:
|
||||
normalized = _normalize_text(text)
|
||||
if not normalized:
|
||||
return None
|
||||
|
||||
best_rule: Optional[ActionRule] = None
|
||||
best_matches: List[str] = []
|
||||
best_score = (-1, -1, -1)
|
||||
|
||||
for rule in load_action_rules():
|
||||
matched_keywords = [
|
||||
keyword for keyword in rule.keywords if keyword.lower() in normalized
|
||||
]
|
||||
if not matched_keywords:
|
||||
continue
|
||||
|
||||
score = (
|
||||
len(matched_keywords),
|
||||
max(len(keyword) for keyword in matched_keywords),
|
||||
rule.priority,
|
||||
)
|
||||
if score > best_score:
|
||||
best_rule = rule
|
||||
best_matches = matched_keywords
|
||||
best_score = score
|
||||
|
||||
if best_rule is None:
|
||||
return None
|
||||
|
||||
return {
|
||||
"code": best_rule.code,
|
||||
"behavior": best_rule.behavior,
|
||||
"affect": best_rule.affect,
|
||||
"intensity": best_rule.intensity,
|
||||
"priority": best_rule.priority,
|
||||
"matchedKeywords": best_matches,
|
||||
"sentimentHint": best_rule.sentiment_hint,
|
||||
}
|
||||
+1
-1
@@ -47,7 +47,7 @@ import numpy as np
|
||||
|
||||
from ai_module import baidu_emotion
|
||||
|
||||
from core.live2d_action_standard import resolve_action_signal
|
||||
from core.action_signal import resolve_action_signal
|
||||
|
||||
from core import wsa_server
|
||||
|
||||
|
||||
+18
-18
@@ -37,16 +37,16 @@ class MyServer:
|
||||
output_setting = data.get("Output")
|
||||
except json.JSONDecodeError:
|
||||
pass # Ignore invalid JSON messages
|
||||
if username is not None or output_setting is not None:
|
||||
remote_address = websocket.remote_address
|
||||
unique_id = f"{remote_address[0]}:{remote_address[1]}"
|
||||
async with self.lock:
|
||||
for i in range(len(self.__clients)):
|
||||
if self.__clients[i]["id"] == unique_id:
|
||||
if username is not None:
|
||||
self.__clients[i]["username"] = username
|
||||
if output_setting is not None:
|
||||
self.__clients[i]["output"] = output_setting
|
||||
if username is not None or output_setting is not None:
|
||||
remote_address = websocket.remote_address
|
||||
unique_id = f"{remote_address[0]}:{remote_address[1]}"
|
||||
async with self.lock:
|
||||
for i in range(len(self.__clients)):
|
||||
if self.__clients[i]["id"] == unique_id:
|
||||
if username is not None:
|
||||
self.__clients[i]["username"] = username
|
||||
if output_setting is not None:
|
||||
self.__clients[i]["output"] = output_setting
|
||||
await self.__consumer(message)
|
||||
except websockets.exceptions.ConnectionClosedError as e:
|
||||
# 从客户端列表中移除已断开的连接
|
||||
@@ -195,17 +195,17 @@ class MyServer:
|
||||
if self.__server:
|
||||
util.log(1, 'server already exist')
|
||||
return
|
||||
self.__server = websockets.serve(self.__handler, self.__host, self.__port)
|
||||
self.__server = websockets.serve(self.__handler, self.__host, self.__port, ping_interval=10, ping_timeout=5)
|
||||
asyncio.get_event_loop().run_until_complete(self.__server)
|
||||
asyncio.get_event_loop().run_forever()
|
||||
|
||||
# 往要发送的命令列表中,添加命令
|
||||
def add_cmd(self, content):
|
||||
if not self.__running:
|
||||
return
|
||||
# keep unicode (emoji/中文) intact for websocket consumers
|
||||
jsonStr = json.dumps(content, ensure_ascii=False)
|
||||
self.__listCmd.append(jsonStr)
|
||||
def add_cmd(self, content):
|
||||
if not self.__running:
|
||||
return
|
||||
# keep unicode (emoji/中文) intact for websocket consumers
|
||||
jsonStr = json.dumps(content, ensure_ascii=False)
|
||||
self.__listCmd.append(jsonStr)
|
||||
# util.log('命令 {}'.format(content))
|
||||
|
||||
# 开启服务
|
||||
@@ -314,4 +314,4 @@ def get_web_instance() -> MyServer:
|
||||
|
||||
if __name__ == '__main__':
|
||||
testServer = TestServer(host='0.0.0.0', port=10000)
|
||||
testServer.start_server()
|
||||
testServer.start_server()
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
# Fay 侧通用动作语义改造说明
|
||||
|
||||
## 目标
|
||||
|
||||
让 Fay 输出“通用动作语义”,而不是输出某个渲染引擎或某个模型的具体动作编号。
|
||||
|
||||
这样 Fay 可以同时服务于:
|
||||
|
||||
- Live2D 数字人
|
||||
- 3D 数字人
|
||||
- Unity / Unreal 角色
|
||||
- 机器人 / 硬件角色
|
||||
- 仅语音客户端
|
||||
|
||||
原则:
|
||||
|
||||
1. 保持原版 Fay WebSocket 音频接口结构不变。
|
||||
2. 在原版 `Data` 中增加两个可选字段:`Action` 和 `Sentiment`。
|
||||
3. Fay 不输出 `TapBody`、`MotionNo`、`F01` 这类具体实现细节。
|
||||
4. 各前端或驱动端通过自己的配置表完成映射。
|
||||
|
||||
---
|
||||
|
||||
## 原版接口
|
||||
|
||||
原版接口如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"Topic": "human",
|
||||
"Data": {
|
||||
"Key": "audio",
|
||||
"Text": "这边请",
|
||||
"IsFirst": 1,
|
||||
"IsEnd": 0,
|
||||
"Lips": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 建议改造方式
|
||||
|
||||
在原版 `Data` 中增加可选字段 `Action` 和 `Sentiment`:
|
||||
|
||||
```json
|
||||
{
|
||||
"Topic": "human",
|
||||
"Data": {
|
||||
"Key": "audio",
|
||||
"Text": "这边请",
|
||||
"IsFirst": 1,
|
||||
"IsEnd": 0,
|
||||
"Lips": [],
|
||||
"Sentiment": 0.7,
|
||||
"Action": {
|
||||
"code": "guidance.invite",
|
||||
"behavior": "invite",
|
||||
"affect": "warm",
|
||||
"intensity": 0.74,
|
||||
"priority": 82
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 不强制增加 `ActionSchema`
|
||||
- 第一版先保证字段语义稳定即可
|
||||
- 如果未来需要版本控制,可以后续再补 `Action.version`
|
||||
- `Action` 和 `Sentiment` 建议同时存在,但都应为可选字段
|
||||
|
||||
---
|
||||
|
||||
## 为什么需要“动作语义锚点”
|
||||
|
||||
这里的“动作语义锚点”,建议用 `behavior` 表示。
|
||||
|
||||
它的作用不是描述某个具体模型动作,而是给所有前端一个统一的“动作类别坐标”。
|
||||
|
||||
### 它解决的问题
|
||||
|
||||
| 问题 | 没有语义锚点时 | 有语义锚点时 |
|
||||
| --- | --- | --- |
|
||||
| Fay 与模型耦合 | Fay 需要知道 `TapBody`、`MotionNo=21` | Fay 只说 `behavior=invite` |
|
||||
| 多端复用困难 | Live2D、3D、机器人都要单独写逻辑 | 各端都只做 `behavior -> 本地动作` 映射 |
|
||||
| 话术很多但动作有限 | 每种话术都要单独适配 | 多个话术可归一到同一个锚点 |
|
||||
| 后续维护成本高 | 每换模型都要改 Fay | 只改前端配置表 |
|
||||
|
||||
### 一个简单例子
|
||||
|
||||
这些话术:
|
||||
|
||||
- “这边请”
|
||||
- “请进”
|
||||
- “跟我来”
|
||||
- “请往这边走”
|
||||
|
||||
在 Fay 侧都可以归一成:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "guidance.invite",
|
||||
"behavior": "invite",
|
||||
"affect": "warm"
|
||||
}
|
||||
```
|
||||
|
||||
然后不同终端各自解释:
|
||||
|
||||
- Live2D:映射到某个引导动作
|
||||
- 3D:映射到某个招手/邀请动画
|
||||
- 机器人:映射到某个手臂引导动作
|
||||
- 纯语音:忽略动作,只保留语气
|
||||
|
||||
所以:
|
||||
|
||||
- `code` 更偏业务/场景语义
|
||||
- `behavior` 更偏“身体动作类别”
|
||||
- `affect` 更偏“表现气质/情绪风格”
|
||||
|
||||
---
|
||||
|
||||
## 推荐字段定义
|
||||
|
||||
| 字段 | 类型 | 是否必填 | 含义 |
|
||||
| --- | --- | --- | --- |
|
||||
| `Sentiment` | number | 否 | 连续情绪值,建议范围 `-2 ~ 2` 或 `-1 ~ 1`,由接收端自行约定 |
|
||||
| `Action.code` | string | 是 | 标准动作语义 ID,稳定主键 |
|
||||
| `Action.behavior` | string | 否 | 动作语义锚点,描述身体动作类别 |
|
||||
| `Action.affect` | string | 否 | 表现语义锚点,描述表情/语气/情绪风格 |
|
||||
| `Action.intensity` | number | 否 | 强度,建议范围 `0 ~ 1` |
|
||||
| `Action.priority` | number | 否 | 优先级,冲突时用于决策 |
|
||||
|
||||
---
|
||||
|
||||
## 字段职责建议
|
||||
|
||||
### `Sentiment`
|
||||
|
||||
用于表达”这段话整体情绪倾向”。
|
||||
|
||||
它和 `Action` 不冲突,职责不同:
|
||||
|
||||
- `Action` 决定”做什么”
|
||||
- `Sentiment` 决定”情绪偏什么方向”
|
||||
|
||||
推荐用途:
|
||||
|
||||
- 当 `Action` 存在时,作为微调信号
|
||||
- 当 `Action` 不存在时,作为回退信号
|
||||
- 可同时用于表情、语气、动作幅度、TTS 风格
|
||||
|
||||
#### `Sentiment` 计算方式
|
||||
|
||||
Fay 当前有两种计算方式,按优先级依次尝试:
|
||||
|
||||
**方式一:百度情感分析 API(优先)**
|
||||
|
||||
如果配置了 `baidu_emotion_api_key` 和 `baidu_emotion_secret_key`,调用百度 NLP 的 `sentiment_classify` 接口(`ai_module/baidu_emotion.py`),返回百度原始 sentiment 值(0=消极 / 1=中性 / 2=积极)。
|
||||
|
||||
**方式二:本地关键词匹配(兜底)**
|
||||
|
||||
未配置百度 API 或调用失败时,走 `fay_core.py` 中的 `__analyze_sentiment_by_keywords()` 方法,逻辑如下:
|
||||
|
||||
1. 维护 4 个关键词列表,每个对应一个权重:
|
||||
|
||||
| 列表 | 示例关键词 | 权重 |
|
||||
| --- | --- | --- |
|
||||
| 非常积极 | 开心、太好了、完美、激动 | +2 |
|
||||
| 轻微积极 | 好、可以、没问题、欢迎 | +1 |
|
||||
| 轻微消极 | 不好、失望、烦、担心 | -1 |
|
||||
| 非常消极 | 痛苦、绝望、崩溃、气死 | -2 |
|
||||
|
||||
2. 统计文本中匹配的关键词数量,加权求和
|
||||
3. 标点修正:`?`/`!`/`~` 加 0.3,`...`/`。。.` 减 0.3
|
||||
4. 最终 clamp 到 `[-2, +2]` 范围
|
||||
|
||||
注意:此计算与 `Action` 中的 `sentimentHint` 字段完全独立。`sentimentHint` 是动作规则自身的情感标注,用于辅助关键词匹配;`Sentiment` 是对整句话的情感极性评估。
|
||||
|
||||
### `code`
|
||||
|
||||
用于表达“这次动作在业务上的语义是什么”。
|
||||
|
||||
例如:
|
||||
|
||||
- `greeting.hello`
|
||||
- `guidance.invite`
|
||||
- `dialogue.explain`
|
||||
- `social.thanks`
|
||||
|
||||
### `behavior`
|
||||
|
||||
用于表达“身体动作属于哪一类”。
|
||||
|
||||
例如:
|
||||
|
||||
- `nod`
|
||||
- `invite`
|
||||
- `wave`
|
||||
- `think`
|
||||
- `warn`
|
||||
|
||||
它是动作语义锚点。
|
||||
|
||||
### `affect`
|
||||
|
||||
用于表达“整体表现风格是什么”。
|
||||
|
||||
例如:
|
||||
|
||||
- `smile`
|
||||
- `warm`
|
||||
- `neutral`
|
||||
- `serious`
|
||||
- `sad`
|
||||
- `excited`
|
||||
|
||||
这个字段比 `expression` 更通用,因为它不只适用于 Live2D 表情,也可以映射到:
|
||||
|
||||
- 3D 面部表情
|
||||
- TTS 语气风格
|
||||
- 机器人灯光/屏幕表情
|
||||
|
||||
### `intensity`
|
||||
|
||||
用于表达“动作做得轻一点还是重一点”。
|
||||
|
||||
例如:
|
||||
|
||||
- `0.2` 轻微点头
|
||||
- `0.8` 明显邀请
|
||||
- `0.95` 强烈庆祝
|
||||
|
||||
---
|
||||
|
||||
## Fay 侧只负责什么
|
||||
|
||||
Fay 侧只负责:
|
||||
|
||||
```text
|
||||
文本 / 意图 / 场景 -> Action
|
||||
文本 / 情绪分析 -> Sentiment
|
||||
```
|
||||
|
||||
Fay 不负责:
|
||||
|
||||
```text
|
||||
Action -> Live2D动作编号
|
||||
Action -> 3D动画名
|
||||
Action -> 机器人舵机动作
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 不建议 Fay 输出的字段
|
||||
|
||||
| 不建议由 Fay 输出 | 原因 |
|
||||
| --- | --- |
|
||||
| `MotionGroup` | 属于 конкретe 模型动作组 |
|
||||
| `MotionNo` | 属于 конкретe 模型动作编号 |
|
||||
| `TapBody` | 属于 конкретe Live2D 工程命名 |
|
||||
| `F01/F02/F03` | 属于 конкретe 模型表情资源名 |
|
||||
|
||||
这些都应该由前端或驱动端自行配置。
|
||||
|
||||
---
|
||||
|
||||
## 推荐可配置映射表
|
||||
|
||||
Fay 侧维护一张 CSV 配置表(`config/action_rules.csv`),可直接用 Excel 打开编辑:
|
||||
|
||||
```text
|
||||
关键词/意图 -> code/behavior/affect/intensity/priority/sentimentHint
|
||||
```
|
||||
|
||||
CSV 格式说明:
|
||||
- 第一行为表头:`code,behavior,affect,intensity,priority,sentimentHint,keywords`
|
||||
- `keywords` 列中多个关键词用 `|` 分隔(避免与 CSV 逗号冲突)
|
||||
- 文件编码为 UTF-8
|
||||
|
||||
不要把这张表写死在代码里。
|
||||
|
||||
### 当前规则示例
|
||||
|
||||
| 关键词或意图示例 | `code` | `behavior` | `affect` | `intensity` | `priority` |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 你好、您好、hello、hi | `greeting.hello` | `nod` | `smile` | `0.35` | `60` |
|
||||
| 欢迎、欢迎光临、欢迎来到 | `greeting.welcome` | `invite` | `warm` | `0.78` | `80` |
|
||||
| 再见、拜拜、goodbye | `farewell.goodbye` | `wave` | `smile` | `0.62` | `72` |
|
||||
| 是的、没错、收到、明白 | `dialogue.confirm` | `nod` | `smile` | `0.42` | `58` |
|
||||
| 不是、不行、不能、no | `dialogue.reject` | `reject` | `serious` | `0.58` | `70` |
|
||||
| 让我想想、考虑一下 | `dialogue.think` | `think` | `neutral` | `0.44` | `74` |
|
||||
| 为什么、怎么回事、真的吗 | `dialogue.question` | `question` | `curious` | `0.52` | `55` |
|
||||
| 也就是说、其实、比如 | `dialogue.explain` | `explain` | `neutral` | `0.56` | `63` |
|
||||
| 建议、推荐、你可以试试 | `dialogue.recommend` | `recommend` | `smile` | `0.63` | `66` |
|
||||
| 总之、综上、最后总结 | `dialogue.summary` | `summary` | `neutral` | `0.40` | `57` |
|
||||
| 请进、这边请、跟我来 | `guidance.invite` | `invite` | `warm` | `0.74` | `82` |
|
||||
| 稍等、等一下、请稍等 | `guidance.wait` | `wait` | `neutral` | `0.26` | `65` |
|
||||
| 提醒、别忘了、记得 | `guidance.remind` | `remind` | `neutral` | `0.48` | `62` |
|
||||
| 注意、小心、警告 | `guidance.warn` | `warn` | `serious` | `0.80` | `84` |
|
||||
| 谢谢、感谢、多谢 | `social.thanks` | `thanks` | `warm` | `0.46` | `61` |
|
||||
| 抱歉、对不起、不好意思 | `social.apology` | `apology` | `sorry` | `0.60` | `71` |
|
||||
| 还好吗、没事吧、别担心 | `social.care` | `care` | `warm` | `0.50` | `64` |
|
||||
| 太好了、成功了、真棒 | `emotion.celebrate` | `celebrate` | `excited` | `0.95` | `92` |
|
||||
| 天啊、竟然、不会吧 | `emotion.surprise` | `surprise` | `surprised` | `0.88` | `86` |
|
||||
| 难过、可惜、遗憾、糟糕 | `emotion.sad` | `sad` | `sad` | `0.82` | `83` |
|
||||
|
||||
说明:
|
||||
|
||||
- 这张表本身就建议“带上表情风格”,也就是 `affect`
|
||||
- 这样前端不需要只靠 `Sentiment` 猜表情
|
||||
- `Sentiment` 仍然保留,用于微调和回退
|
||||
|
||||
---
|
||||
|
||||
## 各端如何消费 `Action`
|
||||
|
||||
### Live2D
|
||||
|
||||
```text
|
||||
behavior -> motion
|
||||
affect -> expression
|
||||
intensity -> 候选动作强度选择
|
||||
sentiment -> 表情/动作幅度微调或回退
|
||||
```
|
||||
|
||||
### 3D 数字人
|
||||
|
||||
```text
|
||||
behavior -> 动画状态机
|
||||
affect -> 面部表情 / blendshape
|
||||
intensity -> 动作幅度 / 播放权重
|
||||
sentiment -> 情绪层混合权重
|
||||
```
|
||||
|
||||
### 机器人
|
||||
|
||||
```text
|
||||
behavior -> 舵机动作模板
|
||||
affect -> 灯光 / 屏幕表情 / 语音风格
|
||||
intensity -> 动作幅度 / 执行力度
|
||||
sentiment -> 情绪表现微调
|
||||
```
|
||||
|
||||
### 纯语音客户端
|
||||
|
||||
```text
|
||||
behavior -> 可忽略
|
||||
affect -> TTS 风格 / 音色 / 情绪
|
||||
intensity -> 情绪强度
|
||||
sentiment -> 连续情绪控制
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 最小结论
|
||||
|
||||
Fay 侧建议从:
|
||||
|
||||
```json
|
||||
{
|
||||
"Topic": "human",
|
||||
"Data": {
|
||||
"Key": "audio",
|
||||
"Text": "这边请",
|
||||
"IsFirst": 1,
|
||||
"IsEnd": 0,
|
||||
"Lips": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
升级为:
|
||||
|
||||
```json
|
||||
{
|
||||
"Topic": "human",
|
||||
"Data": {
|
||||
"Key": "audio",
|
||||
"Text": "这边请",
|
||||
"IsFirst": 1,
|
||||
"IsEnd": 0,
|
||||
"Lips": [],
|
||||
"Sentiment": 0.7,
|
||||
"Action": {
|
||||
"code": "guidance.invite",
|
||||
"behavior": "invite",
|
||||
"affect": "warm",
|
||||
"intensity": 0.74,
|
||||
"priority": 82
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
即可。
|
||||
|
||||
后续具体怎么动,由各前端或驱动端通过配置表自己决定。
|
||||
@@ -0,0 +1,283 @@
|
||||
# Live2D 模型制作要求
|
||||
|
||||
本文档用于指导外包人员制作 Live2D 模型,确保交付后能直接对接我方的数字人系统。
|
||||
|
||||
---
|
||||
|
||||
## 一、技术规格
|
||||
|
||||
| 项目 | 要求 |
|
||||
| --- | --- |
|
||||
| Cubism 版本 | **Cubism 5**(SDK for Web 5-r.4) |
|
||||
| model3.json 版本 | Version: 3 |
|
||||
| 贴图尺寸 | 2048×2048,张数不限 |
|
||||
| 交付格式 | 完整的 Cubism 导出目录(含 .moc3、贴图、motion3.json、exp3.json、physics3.json、pose3.json 等) |
|
||||
| 渲染目标 | 浏览器端 WebGL(Cubism SDK for Web) |
|
||||
|
||||
---
|
||||
|
||||
## 二、必需参数(Parameters)
|
||||
|
||||
模型必须包含以下标准参数,参数 ID 请严格使用 Cubism 默认命名:
|
||||
|
||||
### 口型同步(必需)
|
||||
|
||||
| 参数 ID | 用途 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `ParamMouthOpenY` | 嘴巴张合 | **最关键参数**,程序通过此参数驱动实时口型同步 |
|
||||
|
||||
程序会在 `model3.json` 的 `Groups` 中查找 `LipSync` 组:
|
||||
|
||||
```json
|
||||
{
|
||||
"Target": "Parameter",
|
||||
"Name": "LipSync",
|
||||
"Ids": ["ParamMouthOpenY"]
|
||||
}
|
||||
```
|
||||
|
||||
### 眨眼(必需)
|
||||
|
||||
| 参数 ID | 用途 |
|
||||
| --- | --- |
|
||||
| `ParamEyeLOpen` | 左眼开合 |
|
||||
| `ParamEyeROpen` | 右眼开合 |
|
||||
|
||||
程序会在 `Groups` 中查找 `EyeBlink` 组:
|
||||
|
||||
```json
|
||||
{
|
||||
"Target": "Parameter",
|
||||
"Name": "EyeBlink",
|
||||
"Ids": ["ParamEyeLOpen", "ParamEyeROpen"]
|
||||
}
|
||||
```
|
||||
|
||||
### 头部与身体跟随(必需)
|
||||
|
||||
| 参数 ID | 用途 |
|
||||
| --- | --- |
|
||||
| `ParamAngleX` | 头部左右旋转 |
|
||||
| `ParamAngleY` | 头部上下旋转 |
|
||||
| `ParamAngleZ` | 头部倾斜 |
|
||||
| `ParamBodyAngleX` | 身体左右摇摆 |
|
||||
| `ParamEyeBallX` | 眼球水平跟随 |
|
||||
| `ParamEyeBallY` | 眼球垂直跟随 |
|
||||
|
||||
这些参数用于呼吸动效和鼠标/视线跟随,程序会自动驱动。
|
||||
|
||||
---
|
||||
|
||||
## 三、口型同步(LipSync)要求
|
||||
|
||||
程序会通过 `ParamMouthOpenY` 参数实时驱动嘴巴开合,模拟说话口型。该参数的值范围是 **0(闭嘴)~ 1(张嘴最大)**。
|
||||
|
||||
### 对嘴部建模的要求
|
||||
|
||||
- **`ParamMouthOpenY` = 0 时**:嘴巴完全闭合,自然状态
|
||||
- **`ParamMouthOpenY` = 1 时**:嘴巴张到最大
|
||||
- 中间值(0.2~0.8)应有平滑的过渡,不能出现跳变或形变异常
|
||||
- 嘴部形变需要在 0~1 全范围内看起来自然,因为程序会输出各种中间值
|
||||
|
||||
### 建模细节建议
|
||||
|
||||
程序内部使用 OVR LipSync 的 15 种 viseme(口型音素),每种映射到不同的开合度:
|
||||
|
||||
| 开合度范围 | 对应口型场景 | 对应 viseme 示例 |
|
||||
| --- | --- | --- |
|
||||
| 0.0 | 静音 / 闭嘴 | sil |
|
||||
| 0.2~0.3 | 轻微张嘴(双唇音、唇齿音) | PP, FF, nn |
|
||||
| 0.4~0.5 | 中度张嘴(齿音、舌音) | TH, DD, CH, SS, ih |
|
||||
| 0.6~0.7 | 较大张嘴(元音、圆唇音) | kk, E, oh |
|
||||
| 0.8~0.9 | 大张嘴(低元音、圆唇突出) | aa, ou |
|
||||
|
||||
请确保这些开合度值下嘴型看起来合理自然。
|
||||
|
||||
### 重要约束
|
||||
|
||||
- 动作(motion)和表情(expression)中**都不要**对 `ParamMouthOpenY` 设置关键帧,否则会与程序的实时口型驱动冲突
|
||||
- 如果嘴部有多个参数(如 `ParamMouthForm` 控制嘴型宽窄),可以保留,但 `ParamMouthOpenY` 必须留给程序独占
|
||||
|
||||
---
|
||||
|
||||
## 四、动作(Motions)要求
|
||||
|
||||
### 动作组结构
|
||||
|
||||
模型必须包含以下两个动作组:
|
||||
|
||||
| 动作组名称 | 用途 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `Idle` | 待机动作 | 无对话时循环播放,至少 1 个 |
|
||||
| `TapBody` | 语义动作 | 对话时由程序调用,按编号索引触发 |
|
||||
|
||||
### TapBody 动作清单
|
||||
|
||||
`TapBody` 组中的动作按**数组下标**(从 0 开始)索引触发。程序会根据后端传来的语义信号选择对应编号的动作。
|
||||
|
||||
需要制作以下 **18 类语义动作**,每类至少 1 个,推荐关键类别提供 2 个变体(用于强度区分):
|
||||
|
||||
| 编号 | 语义(behavior) | 动作描述 | 建议变体数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | nod(点头) | 轻微点头表示肯定 | 2(轻/重) |
|
||||
| 2 | invite(邀请) | 伸手引导方向 | 1 |
|
||||
| 3 | wave(挥手) | 挥手打招呼或告别 | 1 |
|
||||
| 4 | reject(拒绝) | 摇头或摆手表示否定 | 2(轻/重) |
|
||||
| 5 | think(思考) | 手托下巴、目光偏移 | 2(轻/重) |
|
||||
| 6 | question(疑问) | 歪头、挑眉表示好奇 | 1 |
|
||||
| 7 | explain(解释) | 双手摊开或单手比划说明 | 2(简述/详述) |
|
||||
| 8 | recommend(推荐) | 单手指向或展示推荐姿态 | 1 |
|
||||
| 9 | summary(总结) | 双手合拢或归拢手势 | 1 |
|
||||
| 10 | wait(等待) | 双手交叠、微微前倾 | 1 |
|
||||
| 11 | remind(提醒) | 食指轻点或举手提示 | 1 |
|
||||
| 12 | warn(警告) | 严肃摆手或交叉手势 | 1 |
|
||||
| 13 | thanks(感谢) | 鞠躬或双手合十 | 2(轻/重) |
|
||||
| 14 | apology(道歉) | 鞠躬或低头表示歉意 | 1 |
|
||||
| 15 | care(关心) | 微微前倾、温和手势 | 1 |
|
||||
| 16 | celebrate(庆祝) | 举手欢呼、拍手 | 2(轻/重) |
|
||||
| 17 | surprise(惊讶) | 后仰、手捂嘴或张大眼 | 1 |
|
||||
| 18 | sad(悲伤) | 低头、肩膀下沉 | 2(轻/重) |
|
||||
|
||||
**关于编号与变体的说明:**
|
||||
|
||||
- TapBody 组中的动作按数组顺序排列,程序通过下标调用
|
||||
- 有 2 个变体的类别:下标靠前的是**轻度版本**(intensity 低时使用),下标靠后的是**强度版本**(intensity 高时使用)
|
||||
- 最终动作总数取决于变体数量,预计 **18~26 个**
|
||||
- 交付时请提供一份**动作编号与语义的对照表**,方便我方配置映射
|
||||
|
||||
### 动作制作要求
|
||||
|
||||
- 每个动作 `FadeInTime` 和 `FadeOutTime` 建议设为 `0.5` 秒
|
||||
- 动作时长建议 2~4 秒,不要过长(对话过程中会频繁切换)
|
||||
- 动作结束后应能自然过渡回待机姿态
|
||||
- **动作中不要包含嘴部参数的关键帧**,口型由程序实时驱动,动作中写入嘴部数据会产生冲突
|
||||
|
||||
---
|
||||
|
||||
## 五、表情(Expressions)要求
|
||||
|
||||
表情用于控制面部细节(眉毛、眼睛、嘴角等),与动作独立叠加。
|
||||
|
||||
### 必需表情清单
|
||||
|
||||
至少制作以下 **6 个表情**,表情名称请严格按下表命名:
|
||||
|
||||
| 表情名称 | 语义(affect) | 面部特征描述 |
|
||||
| --- | --- | --- |
|
||||
| `F01` | 默认 / 微笑(smile, warm, neutral) | 自然微笑,嘴角上扬,眉毛放松。这是最常用的表情 |
|
||||
| `F02` | 严肃(serious) | 眉毛微蹙,嘴角平或略向下,目光坚定 |
|
||||
| `F03` | 悲伤 / 歉意(sad, sorry) | 眉毛下垂,眼睛微眯,嘴角下拉 |
|
||||
| `F04` | 好奇 / 惊讶 / 兴奋(curious, surprised, excited) | 眉毛上挑,眼睛睁大 |
|
||||
| `F05` | 害羞 / 不好意思 | 脸红、目光偏移、嘴角含笑(可选,备用) |
|
||||
| `F06` | 得意 / 自信 | 眉毛上扬、嘴角上翘、目光明亮(可选,备用) |
|
||||
|
||||
**说明:**
|
||||
|
||||
- `F01`~`F04` 为**必需**,程序核心逻辑依赖这 4 个表情
|
||||
- `F05`~`F06` 及更多为**可选**,交付后我方可扩展映射
|
||||
- 如果能做更多差异化表情更好,但命名请保持 `F01`、`F02`... 的编号格式
|
||||
- 表情文件格式为 `.exp3.json`,放在 `expressions/` 目录下
|
||||
|
||||
### 表情制作要求
|
||||
|
||||
- **表情中不要包含 `ParamMouthOpenY` 参数**,口型由程序实时驱动,表情中写入会产生冲突
|
||||
- 表情是对基础状态的差值叠加,请确保各表情之间切换自然
|
||||
- `F01`(微笑)是默认表情,对话结束后会自动恢复到此表情
|
||||
|
||||
---
|
||||
|
||||
## 六、model3.json 结构要求
|
||||
|
||||
最终交付的 `model3.json` 需要包含以下结构(以示例名 `MyModel` 为例):
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": 3,
|
||||
"FileReferences": {
|
||||
"Moc": "MyModel.moc3",
|
||||
"Textures": ["MyModel.2048/texture_00.png"],
|
||||
"Physics": "MyModel.physics3.json",
|
||||
"Pose": "MyModel.pose3.json",
|
||||
"Expressions": [
|
||||
{ "Name": "F01", "File": "expressions/F01.exp3.json" },
|
||||
{ "Name": "F02", "File": "expressions/F02.exp3.json" },
|
||||
{ "Name": "F03", "File": "expressions/F03.exp3.json" },
|
||||
{ "Name": "F04", "File": "expressions/F04.exp3.json" }
|
||||
],
|
||||
"Motions": {
|
||||
"Idle": [
|
||||
{ "File": "motions/idle.motion3.json", "FadeInTime": 0.5, "FadeOutTime": 0.5 }
|
||||
],
|
||||
"TapBody": [
|
||||
{ "File": "motions/m01_nod_light.motion3.json", "FadeInTime": 0.5, "FadeOutTime": 0.5 },
|
||||
{ "File": "motions/m02_nod_heavy.motion3.json", "FadeInTime": 0.5, "FadeOutTime": 0.5 },
|
||||
"... 按照对照表顺序排列所有动作"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Groups": [
|
||||
{
|
||||
"Target": "Parameter",
|
||||
"Name": "EyeBlink",
|
||||
"Ids": ["ParamEyeLOpen", "ParamEyeROpen"]
|
||||
},
|
||||
{
|
||||
"Target": "Parameter",
|
||||
"Name": "LipSync",
|
||||
"Ids": ["ParamMouthOpenY"]
|
||||
}
|
||||
],
|
||||
"HitAreas": [
|
||||
{ "Id": "HitArea", "Name": "Head" },
|
||||
{ "Id": "HitArea2", "Name": "Body" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、目录结构示例
|
||||
|
||||
```
|
||||
MyModel/
|
||||
├── MyModel.model3.json # 模型配置(入口文件)
|
||||
├── MyModel.moc3 # 模型数据
|
||||
├── MyModel.physics3.json # 物理演算
|
||||
├── MyModel.pose3.json # 姿态配置(可选)
|
||||
├── MyModel.cdi3.json # 显示信息(可选)
|
||||
├── MyModel.2048/ # 贴图目录
|
||||
│ ├── texture_00.png
|
||||
│ └── texture_01.png # 如有多张
|
||||
├── expressions/ # 表情文件
|
||||
│ ├── F01.exp3.json
|
||||
│ ├── F02.exp3.json
|
||||
│ ├── F03.exp3.json
|
||||
│ └── F04.exp3.json
|
||||
└── motions/ # 动作文件
|
||||
├── idle.motion3.json
|
||||
├── m01_nod_light.motion3.json
|
||||
├── m02_nod_heavy.motion3.json
|
||||
└── ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、交付清单
|
||||
|
||||
| 交付物 | 必需 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 完整模型导出目录 | 是 | 包含上述所有文件 |
|
||||
| 动作编号对照表 | 是 | 说明 TapBody 中每个下标对应的语义和动作描述 |
|
||||
| Cubism Editor 工程源文件(.cmo3) | 推荐 | 方便后续调整 |
|
||||
| 动作源文件(.can3) | 推荐 | 方便后续调整动作 |
|
||||
|
||||
---
|
||||
|
||||
## 九、重要注意事项
|
||||
|
||||
1. **口型参数独占**:`ParamMouthOpenY` 由程序实时驱动口型同步,动作和表情中**都不要**对此参数设置关键帧
|
||||
2. **表情命名必须匹配**:表情名称必须是 `F01`、`F02`... 格式,程序按此名称调用
|
||||
3. **动作组命名必须匹配**:待机组必须叫 `Idle`,语义动作组必须叫 `TapBody`
|
||||
4. **TapBody 的顺序很重要**:程序按数组下标索引动作,请务必按对照表排列顺序
|
||||
5. **动作不宜过长**:对话场景中动作切换频繁,单个动作建议 2~4 秒
|
||||
6. **Groups 配置不能遗漏**:`EyeBlink` 和 `LipSync` 两个 Group 必须在 model3.json 中正确声明
|
||||
@@ -0,0 +1,187 @@
|
||||
# Live2D 模型制作要求(简化版)
|
||||
|
||||
本文档用于指导外包制作 Live2D 数字人模型。本版为最小可用版本,仅满足基础对话场景。
|
||||
|
||||
---
|
||||
|
||||
## 一、技术规格
|
||||
|
||||
| 项目 | 要求 |
|
||||
| -------------- | --------------------------------------------------- |
|
||||
| Cubism 版本 | **Cubism 5** |
|
||||
| model3.json 版本 | Version: 3 |
|
||||
| 贴图尺寸 | 2048×2048 |
|
||||
| 渲染目标 | 浏览器端 WebGL |
|
||||
| 交付格式 | 完整 Cubism 导出目录(.moc3、贴图、motion3.json、physics3.json) |
|
||||
|
||||
---
|
||||
|
||||
## 二、必需参数(Parameters)
|
||||
|
||||
参数 ID 请严格使用 Cubism 默认命名。
|
||||
|
||||
| 参数 ID | 用途 | 说明 |
|
||||
| ----------------- | ---- | ---------------- |
|
||||
| `ParamMouthOpenY` | 嘴巴张合 | 程序实时驱动口型,**最关键** |
|
||||
| `ParamEyeLOpen` | 左眼开合 | 程序驱动自动眨眼 |
|
||||
| `ParamEyeROpen` | 右眼开合 | 程序驱动自动眨眼 |
|
||||
| `ParamAngleX` | 头部左右 | 呼吸动效 + 视线跟随 |
|
||||
| `ParamAngleY` | 头部上下 | 同上 |
|
||||
| `ParamAngleZ` | 头部倾斜 | 同上 |
|
||||
| `ParamBodyAngleX` | 身体摇摆 | 呼吸动效 |
|
||||
| `ParamEyeBallX` | 眼球水平 | 视线跟随 |
|
||||
| `ParamEyeBallY` | 眼球垂直 | 视线跟随 |
|
||||
|
||||
---
|
||||
|
||||
## 三、口型同步(LipSync)要求
|
||||
|
||||
程序会通过 `ParamMouthOpenY` 参数实时驱动嘴巴开合,模拟说话口型。该参数的值范围是 **0(闭嘴)~ 1(张嘴最大)**。
|
||||
|
||||
### 对嘴部建模的要求
|
||||
|
||||
- **`ParamMouthOpenY` = 0 时**:嘴巴完全闭合,自然状态
|
||||
- **`ParamMouthOpenY` = 1 时**:嘴巴张到最大
|
||||
- 中间值(0.2~0.8)应有平滑的过渡,不能出现跳变或形变异常
|
||||
- 嘴部形变需要在 0~1 全范围内看起来自然,因为程序会输出各种中间值
|
||||
|
||||
### 建模细节建议
|
||||
|
||||
程序内部会产生以下几种典型开合度,请确保这些值下嘴型看起来合理:
|
||||
|
||||
| 开合度范围 | 对应口型场景 |
|
||||
| --- | --- |
|
||||
| 0.0 | 静音 / 闭嘴 |
|
||||
| 0.2~0.3 | 轻微张嘴(双唇音 p/b、唇齿音 f) |
|
||||
| 0.4~0.5 | 中度张嘴(齿音 d/t、舌音 ch/s) |
|
||||
| 0.6~0.7 | 较大张嘴(元音 e、圆唇音 o) |
|
||||
| 0.8~0.9 | 大张嘴(低元音 a、圆唇突出 ou) |
|
||||
|
||||
### 重要约束
|
||||
|
||||
- 动作(motion)和表情(expression)中**都不要**对 `ParamMouthOpenY` 设置关键帧,否则会与程序的实时口型驱动冲突
|
||||
- 如果嘴部有多个参数(如 `ParamMouthForm` 控制嘴型宽窄),可以保留,但 `ParamMouthOpenY` 必须留给程序独占
|
||||
|
||||
---
|
||||
|
||||
## 四、动作(Motions)要求
|
||||
|
||||
不需要表情文件,不需要情绪系统。只需要以下动作:
|
||||
|
||||
### 动作组结构
|
||||
|
||||
| 动作组名称 | 用途 |
|
||||
| --------- | -------------- |
|
||||
| `Idle` | 待机,无对话时循环播放 |
|
||||
| `TapBody` | 对话动作,程序按数组下标调用 |
|
||||
|
||||
### Idle 组(至少 1 个)
|
||||
|
||||
自然站立的待机循环动画,有轻微呼吸感。
|
||||
|
||||
### TapBody 组(共 5 个动作)
|
||||
|
||||
程序按 **数组下标(从 0 开始)** 调用,顺序必须严格按下表排列:
|
||||
|
||||
| 下标 | 动作名称 | 动作描述 | 使用场景 |
|
||||
| --- | ---- | ---------------------- | ----------- |
|
||||
| 0 | 聆听 | 微微前倾、略点头,表现出认真听对方说话的姿态 | 用户正在输入/说话时 |
|
||||
| 1 | 说话 | 自然的说话姿态,可有轻微手势,身体略有律动 | AI 回复时的默认动作 |
|
||||
| 2 | 思考 | 手托下巴或目光微偏上方,表现在想问题 | AI 正在生成回复时 |
|
||||
| 3 | 等待 | 双手自然交叠或轻微晃动,耐心等待的姿态 | 空闲等待用户操作时 |
|
||||
| 4 | 打招呼 | 挥手或点头致意 | 对话开始时的问候 |
|
||||
|
||||
### 动作制作要求
|
||||
|
||||
- `FadeInTime` 和 `FadeOutTime` 设为 `0.5` 秒
|
||||
- 动作时长 **2~4 秒**
|
||||
- 动作结束后能自然过渡回待机姿态
|
||||
- **动作中不要包含 `ParamMouthOpenY` 的关键帧**,口型由程序实时驱动,写入会冲突
|
||||
|
||||
---
|
||||
|
||||
## 五、model3.json 结构要求
|
||||
|
||||
以 `MyModel` 为例:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": 3,
|
||||
"FileReferences": {
|
||||
"Moc": "MyModel.moc3",
|
||||
"Textures": ["MyModel.2048/texture_00.png"],
|
||||
"Physics": "MyModel.physics3.json",
|
||||
"Motions": {
|
||||
"Idle": [
|
||||
{ "File": "motions/idle.motion3.json", "FadeInTime": 0.5, "FadeOutTime": 0.5 }
|
||||
],
|
||||
"TapBody": [
|
||||
{ "File": "motions/listen.motion3.json", "FadeInTime": 0.5, "FadeOutTime": 0.5 },
|
||||
{ "File": "motions/speak.motion3.json", "FadeInTime": 0.5, "FadeOutTime": 0.5 },
|
||||
{ "File": "motions/think.motion3.json", "FadeInTime": 0.5, "FadeOutTime": 0.5 },
|
||||
{ "File": "motions/wait.motion3.json", "FadeInTime": 0.5, "FadeOutTime": 0.5 },
|
||||
{ "File": "motions/greet.motion3.json", "FadeInTime": 0.5, "FadeOutTime": 0.5 }
|
||||
]
|
||||
}
|
||||
},
|
||||
"Groups": [
|
||||
{
|
||||
"Target": "Parameter",
|
||||
"Name": "EyeBlink",
|
||||
"Ids": ["ParamEyeLOpen", "ParamEyeROpen"]
|
||||
},
|
||||
{
|
||||
"Target": "Parameter",
|
||||
"Name": "LipSync",
|
||||
"Ids": ["ParamMouthOpenY"]
|
||||
}
|
||||
],
|
||||
"HitAreas": [
|
||||
{ "Id": "HitArea", "Name": "Body" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**关键点:**
|
||||
|
||||
- `Groups` 中 `EyeBlink` 和 `LipSync` 必须声明,程序依赖这两个组实现自动眨眼和口型同步
|
||||
- `TapBody` 数组中的顺序必须是:聆听、说话、思考、等待、打招呼
|
||||
|
||||
---
|
||||
|
||||
## 六、目录结构
|
||||
|
||||
```
|
||||
MyModel/
|
||||
├── MyModel.model3.json
|
||||
├── MyModel.moc3
|
||||
├── MyModel.physics3.json
|
||||
├── MyModel.2048/
|
||||
│ └── texture_00.png
|
||||
└── motions/
|
||||
├── idle.motion3.json
|
||||
├── listen.motion3.json # 下标 0 - 聆听
|
||||
├── speak.motion3.json # 下标 1 - 说话
|
||||
├── think.motion3.json # 下标 2 - 思考
|
||||
├── wait.motion3.json # 下标 3 - 等待
|
||||
└── greet.motion3.json # 下标 4 - 打招呼
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、交付清单
|
||||
|
||||
| 交付物 | 必需 |
|
||||
| -------------------------- | --- |
|
||||
| 完整模型导出目录(上述所有文件) | 是 |
|
||||
| Cubism Editor 工程源文件(.cmo3) | 推荐 |
|
||||
| 动作源文件(.can3) | 推荐 |
|
||||
|
||||
---
|
||||
|
||||
## 八、注意事项
|
||||
|
||||
1. **`ParamMouthOpenY` 不要动**:动作中不要对嘴巴参数设关键帧,程序实时驱动口型
|
||||
2. **动作组必须叫 `Idle` 和 `TapBody`**:程序按这两个名字查找
|
||||
3. **TapBody 顺序不能变**:程序按下标 0~4 调用,顺序错了动作就对不上
|
||||
4. **`EyeBlink` 和 `LipSync` Groups 不能漏**:没有这两个声明,自动眨眼和口型同步不会生效
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 176 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 174 KiB |
Reference in New Issue
Block a user