Files
netease-bedrock-wiki/mcguide/16-美术/6-模型和动作/04-骨骼模型的使用.md
2025-08-25 18:36:29 +08:00

343 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
front:
hard: 入门
time: 分钟
---
# 骨骼模型的使用
骨骼模型、骨骼动画的制作请参考[骨骼模型制作指南(3DMAX)](02-骨骼模型制作指南3DMAX.html)等相关文档。
通过专业的模型制作软件完成模型后导出为FBX格式然后再转换为《我的世界》中的模型格式JSON格式相应的方法请查阅[教学中心-编辑器基础-特效编辑器-入门教程01-模型导入与使用](../9-特效/10-模型导入.md#1-导入fbx模型)。
> 如需将已转换为我的世界中国版格式的骨骼模型导入至其他作品中,可以参考[骨骼模型json的导入](../9-特效/10-模型导入.md#2-导入骨骼模型json)
在将骨骼模型转换为JSON格式后我们就可以准备在游戏内使用骨骼模型了。以离线文档中 **示例/6-4 资源制作/工具和示例/fbxRes/xuenv** 资源为例先在编辑器中导入这个模型然后按下面的步骤进行。实际上下面的1~3步目前编辑器已经自动完成了需要手动操作文件时可以参考。
## 1. 填入模型路径信息
`mod_resource/models/netease_models.json`里面填入模型的骨骼,网格及动作的文件路径(目前编辑器已经自动做好这一步了),如下图所示:
<img src="./picture/anim/reg4-1.png" alt="reg4-1" style="zoom: 80%;" />
**骨骼模型动画的命名最好使用英文单词/拼音/数字/下划线组成。**
## 2. 将资源放入对应目录
将skeleton、mesh、animation资源放入`mod_resource/models`下对应的文件夹:
![reg4-3](./picture/anim/reg4-3.png)
## 3. 指定贴图
mesh文件中会有指定material如下图所示
名称对应到`mod_resource/textures/models/xuenv.png`,所以需要在此位置放置贴图资源。
<img src="./picture/anim/reg4-4.png" alt="reg4-4" style="zoom: 80%;" />
## 4. 代码调用
采用component的结构来创建模型与控制动画播放
创建模型替换原有模型
```python
modelComp = self.CreateComponent(playerId, 'Minecraft', 'model')
# 'xuenv'即为netease_models.json里配置的骨骼模型名称
modelComp.SetModel('xuenv')
```
播放动画
```python
modelComp = self.GetComponent(playerId, 'Minecraft', 'model')
# 播放动画'prepare'第二个参数设置为True表示循环播放该动画接口详细信息可以查看modAPI接口文档
modelComp.PlayAnim('prepare', True)
```
## 5. 第一人称模型
上面部分完成了第三人称视角的骨骼模型显示及动画播放当我们想在游戏中切换到第一人称也有骨骼模型与动作时需要另外做一套骨骼模型和动作如下图的steve_fp并且使动作的名称与第三人称模型的动作一致。然后把他配置为第三人称模型的“arm_model”例如
<img src="./picture/anim/reg4-2.png" alt="reg4-2" style="zoom: 50%;" />
在上图中我们可以看到steve有骨骼模型动画这些属于第三人称视角的骨骼模型动画steve中的”arm_model”属性可关联第一人称视角的骨骼模型。配置第一人称视角骨骼模型之后当玩家切换到第一人称视角时如果手上无物品或者手上物品不显示则会显示第一人称骨骼模型即这里的“steve_fp”
当播放动作时第一人称视角模型的动作会跟随第三人称模型。例如给本地玩家替换steve模型后播放walk动作那么切换到第一人称视角时显示的steve_fp模型也会播放walk动作。
如果替换的第三人称骨骼模型没配置”arm_model”字段例如上述的”xuenv”模型则在第一人称视角下手上无物品或者手上物品不显示时不会显示任何模型。
## 6. 模型贴图序列帧动画
骨骼模型动画除了支持上述在animation文件夹下放置相应的json文件这一方式外也支持另一种只是单纯贴图变化的序列帧动画
<img src="./picture/frame_anim/xuenv_frames.gif" alt="xuenv_frames" style="zoom: 80%;" />
制作这种贴图序列帧动画需要两个步骤,我们以上述的雪女为例:
1) 用图片处理工具修改`mod_resource/textures/models/xuenv.png` 路径下的这张贴图:
根据动画需要的帧数横竖方向重复铺开此例中我们以6行5列总共30帧进行制作处理每一帧中贴图需要变化的部分序列帧播放的顺序由贴图的左上角先横再竖播放到贴图的右下角。
原图:
<img src="./picture/frame_anim/xuenv.png" alt="xuenv" style="zoom: 10%;" />
处理后:
<img src="./picture/frame_anim/xuenv_frames.png" alt="xuenv_frames" style="zoom: 20%;" />
2) 修改`mod_resource/models/netease_models.json`中雪女的json配置新增frame_anim字段
<img src="./picture/frame_anim/xuenv_json.png" alt="xuenv_json" style="zoom: 100%;" />
字段中各参数意义如下:
row: 贴图动画中分了多少行此例中6行5列即6行所以此处填6
col: 贴图动画中分了多少列此例中6行5列即5列所以此处填5
frames: 帧数此例中为30帧此数字不一定为row乘col的值允许贴图留有空白假如贴图右下角留了一帧的空白区域此处可填29
time: 动画总时长此例中为5秒代表5秒内播放完30帧然后开始循环
注意:
为了节省运行时的内存,建议资源制作时尽量秉持宽高最邻近二次幂数相乘最小原则。
所谓最邻近二次幂数即数字往上寻找最靠近自己的二次幂二次幂就是124816326412825651210242048等等。
比如一张贴图为200 * 200的分辨率200的最邻近二次幂数为256则加载入内存后即占用256 * 256的大小此处为了讲解方便简化了一些计算细节读者可简单理解为内存占用与宽高成正比。
在此例中我们雪女单帧的图片大小256 * 2566行5列排放后为1280 * 1536, 宽高最邻近二次幂数分别为2048和2048所以占用内存为2048 * 2048。
但假如我们调整一下用横4竖8的方式排放宽高即为1024 * 2048此方式最高可支持 4 * 8 = 32帧宽高最邻近二次幂数分别也为1024和2048占用内存即为1024 * 2048内存占用可降低到原来的一半
最后,在代码调用时无须添加额外代码即可生效:
```python
modelComp = self.CreateComponent(playerId, 'Minecraft', 'model')
# 'xuenv'即为netease_models.json里配置的骨骼模型名称
modelComp.SetModel('xuenv')
```
## 7. 模型使用自定义材质及更多贴图
若我们想要实现更为复杂的渲染效果则可能会需要使用特殊的shader及shader所需要采样的贴图这时候我们需要修改材质并配置为使用我们的shader。我们可以在netease_models.json中为模型指定material_cpu和material字段则骨骼模型将使用指定材质进行渲染。自定义材质都需要先在资源包resource_pack/materials/entity.material中进行定义。
以雪女模型为例:
```json
"xuenv_1": {
"skeleton": "skeleton/xuenv_skeleton.json",
"mesh": "mesh/xuenv_mesh_frame.json",
"animation": {
"idle": "animation/xuenv_animation_idle.json",
"walk": "animation/xuenv_animation_idle.json",
"skill1": "animation/xuenv_animation_skill1.json",
"behited": "animation/gun_animation_idle.json",
"prepare": "animation/xuenv_animation_prepare.json",
"win": "animation/xuenv_animation_win.json"
},
// 定义自己的自定义材质
"material": "xuenv_custom",
// 自定义CPU蒙皮材质可不进行配置根据开发者的需要也可配置
"material_cpu": "xuenv_custom_cpu",
// 这个自定义材质所需要的自定义贴图最多可配置4张
"texture": {
//"tex0": "my_custom_texture0",
"tex1": "my_custom_texture1",
"tex2": "my_custom_texture2",
"tex3": "my_custom_texture3",
},
"frame_anim": {
"row": 6,
"col": 5,
"frames": 30,
"time": 5
}
}
```
`material`为GPU骨骼渲染的形式下所使用的材质会全局存储所有骨骼的变换矩阵列表顶点着色器中每个顶点只存储对应的骨骼下标在执行过程中用通过该下标从列表中查询得出变换矩阵因为会有不同的顶点共用相同的骨骼相比于后文的CPU骨骼蒙皮渲染形式使用此方式能更为节省GPU带宽。材质的配置可参考官方骨骼模型材质(data/vanilla_netease/materials/entity.material/entity_for_skeleton)。**几乎所有情况下引擎都会使用该形式的材质**。
`material_cpu`为传统的CPU骨骼蒙皮渲染形式顶点着色器中每个顶点存储自己的变换矩阵。**目前引擎只会在部分安卓设备且该设备不支持使用大顶点Uniform变量large vertex shader uniforms时才会使用的CPU骨骼蒙皮渲染其它情况下则使用GPU骨骼渲染的渲染形式**。**因此开发者如无特别需要可以不定义该材质也可以直接指定为GPU骨骼渲染材质。**如开发者有需要,则材质的配置可参考官方骨骼模型材质(data/vanilla_netease/materials/entity.material/entity_for_skeleton_cpu与GPU所使用的的材质不同的是该材质没有定义`USE_SKINNING`以及`BoneId0`.
两种材质的差别基本如图所示:
<img src="./picture/model/xuenv_material.png" alt="xuenv_material" style="zoom: 100%;" />
在上面我们为模型配置了自己的材质及shader接着如果我们的材质及shader需要有更多的自定义贴图我们就可以增加如下`texture`字段:
```json
// 这个自定义材质所需要的自定义贴图最多可配置4张
"texture": {
//"tex0": "my_custom_texture0",
"tex1": "my_custom_texture1",
"tex2": "my_custom_texture2",
"tex3": "my_custom_texture3",
},
```
关于自定义贴图我们一共可以配置且最多配置4张字段名称分别为`tex0`,`tex1`,`tex2`,`tex3`。 字段的值为贴图的路径,即`textures/models/`路径下的贴图名称,不需要后缀名。
其中`tex0`所配置的贴图会覆盖`mesh`文件中`material`字段所指定的贴图即上文第3节-指定贴图中所配置的贴图。所以一般不需要额外配置`tex0`。如果`mesh`文件中`material`字段没有配置模型贴图,则可以使用`tex0`来配置。
`tex1`,`tex2`,`tex3`,则可供开发者按需求进行配置。
在配置了以上贴图后我们则可以在我们的自定义材质中指定了的fragment shader当中通过声明`LAYOUT_BINDING`使用这些贴图:
```glsl
LAYOUT_BINDING(0) uniform sampler2D TEXTURE_0; // TEXTURE_0对应tex0, 也对应mesh文件中material字段指定的贴图
LAYOUT_BINDING(1) uniform sampler2D TEXTURE_1; // TEXTURE_1对应tex1,
LAYOUT_BINDING(2) uniform sampler2D TEXTURE_2; // TEXTURE_2对应tex2,
LAYOUT_BINDING(3) uniform sampler2D TEXTURE_3; // TEXTURE_3对应tex3,
void main(){
vec4 base_color = texture( TEXTURE_0, uv );
vec4 base_color1 = texture( TEXTURE_1, uv );
vec4 base_color2 = texture( TEXTURE_2, uv );
vec4 base_color3 = texture( TEXTURE_3, uv );
// do what you want
//...
}
```
## 8. 模型使用多个材质及贴图
2.8版本后,可以对同一个骨骼模型的不同骨骼定义不同的材质及所使用的贴图,也可以使用骨骼模型渲染屏蔽接口<a href="../../../mcdocs/1-ModAPI/接口/模型.html#SetModelPartVisible" rel="noopenner"> SetModelPartVisible </a>进行指定骨骼的渲染屏蔽。
想要对骨骼模型配置多个材质以及使用骨骼渲染屏蔽功能需要在netease_models.json中增加如下字段的配置
1`useSplitMeshes` bool类型字段决定了骨骼模型在读取时是否按照指定的骨骼来拆分生成多个mesh值默认为fasle。当设为true时读取该模型的时候将按来分`splitBonesGroup`中指定的骨骼来分别生成和存储mesh而不会按过往方法来合成一整个mesh。骨骼模型渲染时也会变成多个mesh一起渲染因此如果不是要进行骨骼模型按骨骼进行渲染屏蔽或者并没有使用到骨骼模型多材质功能时不要设置为true。
2`splitBonesGroup` 字典类型字段,可以指定拆分的骨骼名称,以及这个骨骼所使用的材质及这个材质所需要的贴图。指定后,骨骼模型将拆分为本体模型以及这些骨骼各自所组成的模型。
以雪女模型为例:
```json
"xuenv_1": {
"skeleton": "skeleton/xuenv_skeleton.json",
"mesh": "mesh/xuenv_mesh_frame.json",
"animation": {
"idle": "animation/xuenv_animation_idle.json",
"walk": "animation/xuenv_animation_idle.json",
"skill1": "animation/xuenv_animation_skill1.json",
"behited": "animation/gun_animation_idle.json",
"prepare": "animation/xuenv_animation_prepare.json",
"win": "animation/xuenv_animation_win.json"
},
// 定义本体模型的自定义材质
"material": "xuenv_custom",
"material_cpu": "xuenv_custom_cpu",
"texture": {
//"tex0": "my_custom_texture0",
"tex1": "my_custom_texture1",
"tex2": "my_custom_texture2",
"tex3": "my_custom_texture3",
},
"frame_anim": {
"row": 6,
"col": 5,
"frames": 30,
"time": 5
},
// 设置useSplitMeshes为true设置后骨骼模型将按splitBonesGroup中的骨骼来拆分渲染。
"useSplitMeshes": true,
// 指定需要分离出来的骨骼名称,指定后,骨骼模型将拆分为本体模型以及这些骨骼各自所组成的模型。
// 骨骼名称需要与该骨骼模型的"skeleton"所指定的skeleton.json文件中存在的骨骼名称一致大小写一致否则无效。
// 可以对分离出来的骨骼指定自定义材质及所使用的贴图。
"splitBonesGroup": {
"Bone007":{
// material 如果没有定义,则使用本体模型的材质
// texture 如果没有定义,则该骨骼使用骨骼模型"mesh"字段所指定的mesh.json文件中该骨骼所属的mesh中"material"字段所定义的贴图路径,如果这个也没有,则使用本体模型的贴图
},
"Bip001 Head":{
// 这个骨骼模型的本体模型使用了frame帧动画贴图如果想要这个骨骼也使用同样的帧动画贴图为了保证帧动画贴图显示正常则可以为这个骨骼的贴图tex0指定同样的帧动画贴图如下
"texture": {
"tex0": "xuenv-1"
}
// 当然也可以不用指定tex0贴图。不指定帧动画贴图的情况下如果想要这个骨骼也显示跟本体模型同样的帧动画效果则请保证mesh.json中所有包含这个骨骼的mesh的"material"字段都定义了相同的帧动画贴图。
},
// 雪女模型的身躯骨骼,我们为这个骨骼指定自定义的材质
"Bip001 Spine": {
// 自定义的材质,材质的定义在资源包/materials/entity.material中定义
"material": "xuenv_special",
// 自定义CPU蒙皮材质可根据需要配置
"material_cpu": "xuenv_special",
// 该材质里的shader所使用的到的贴图名称不需要后缀名最多为4张。贴图放置于资源包/textures/models中。
// 如只需使用1张则定义"tex0"即可使用2张则定义"tex0"及"tex1",如此类推。"tex0"对应shader中的TEXTURE_0, "tex1"对应shader中的TEXTURE_1如此类推。
"texture": {
"tex0": "special_1",
"tex1": "special_2",
"tex2": "special_3",
"tex3": "special_4"
}
}
}
}
```
骨骼的材质会按照如下顺序来优先适用:
1是否指定了`material`,如果有,则该骨骼优先适用`material`中指定的材质;
2如果没有指定`material`,则该骨骼采用与本体模型相同的材质。
骨骼的贴图会按照如下顺序来优先适用:
1是否指定了`texture`,如果有,则该骨骼会优先适用`texture`中定义的贴图至于这些贴图如何被使用则由自定义材质中的shader来决定。
2如果没有指定`texture`则该骨骼优先适用骨骼模型的mesh.json文件中该骨骼所属的mesh中"material"字段指定的贴图。
3如果以上均没有则使用本体模型的贴图。
经过如上配置后,如果在游戏中实时隐藏某个骨骼,则可以调用<a href="../../../mcdocs/1-ModAPI/接口/模型.html#SetModelPartVisible" rel="noopenner"> SetModelPartVisible </a>
```python
import mod.client.extraClientApi as clientApi
comp = clientApi.GetEngineCompFactory().CreateModel(entityId)
modelId = comp.SetModel("xuenv_1")
# 屏蔽指定骨骼
print comp.SetModelPartVisible(modelId, "Bip001 Head", False)
```
## 9.骨骼模型自定义多pass
2.8版本开放了自定义多pass的特性目前开发者可以通过对骨骼模型配置多pass材质实现更多效果。开发者仅需修改`netease_models.json`中的模型配置,将模型配置中的`material`字段配置成数组即可:
```json
{ // netease_models.json
"model_example": {
"dy_load": true,
"mesh": "mesh/model_example_mesh.json",
"skeleton": "skeleton/model_example_skeleton.json",
"material": ["entity_for_skeleton", "netease_drawline_example"] // ****多pass材质数组****
}
}
```
可以看到模型`model_example`中的`material`字段被配置为了由两个材质组成的数组,第一个材质是网易骨骼模型材质`entity_for_skeleton`第二个材质是2.8新增的内置描边材质`netease_drawline_example`
在渲染模型时,引擎会**按顺序**渲染配置中的材质数组,在这个例子中,模型`model_example`会首先使用材质`entity_for_skeleton`渲染一次,再使用材质`netease_drawline_example`渲染一次。
详细示例可参考[多pass演示描边效果](../7-材质与着色器/4-材质实战.md#自定义多pass)。
## 10.支持半透明渲染的材质
骨骼模型在默认情况下使用不透明材质进行渲染。片段着色器输出中gl_FragColor的第四个参数alpha在不透明渲染中并没有效果即gl_FragColor.a无论是什么值都不影响最终渲染效果。
开启半透明渲染之后gl_FragColor.a代表透明度范围为[0.0, 1.0], 1.0代表完全不透明0.0代表完全透明看不见。开启方式如下,只需要在材质的定义中声明如下部份即可:
<img src="./picture/model/xuenv_blend_def.png" alt="xuenv_material" style="zoom: 100%;" />
Blending代表使用混合模式进行渲染blendSrc代表使用SourceAlpha作为混合源因子, blendDst代表使用OneMinusSrcAlpha作为目标混合因子。
关于混合因子说明可看[材质配置说明](../7-材质与着色器/3-材质配置说明.md#材质属性)中关于Blend 半透明对象颜色混合的说明。
接下来我们尝试修改gl_FragColor.a进行测试修改为透明度0.5
<img src="./picture/model/xuenv_blend_frag.png" alt="xuenv_material" style="zoom: 100%;" />
修改完成后游戏中该骨骼模型即会出现半透明效果:
<img src="./picture/model/xuenv_blend.png" alt="xuenv_material" style="zoom: 100%;" />
注意:因为半透明材质无法通过深度检测优化性能,所以尽量不要过多使用,不然会造成一定渲染压力。