前言:什么是GLTF?
原文解释是:GLTF是一种被广泛使用的文件格式,用来储存3D模型和3D场景。在xr-frame里你可以非常轻松地引入任意GLTF模型,并将其渲染出来。想要更详细的讲解:glTF -传输格式;
优势(摘自GLTF介绍 | 微信开放文档 (qq.com)):
-
单一文件,完整场景
使用GLTF打包后的GLB文件,可将一整个场景的所有要素包揽进去。轻松管理你的3D资源。
-
成熟生态,海量资源
来自全世界的优秀创作者,在不同社区中分享作品。配合xr-frame的渲染能力,简直是随取随用。
- 以下使用方法、属性、事件、动画等 (摘自GLTF介绍 | 微信开放文档 (qq.com))
1.GLTF模型需要先通过Loader加载进小程序中,才可以渲染。
<xr-asset-load type="gltf" asset-id="gltfModel" src="/assets/xxx.gltf" />
2.GLTF组件
GLTF组件
可以渲染一个已经加载完成的GLTFModel
资源。
使用xr-gltf
标签可以为标签自动创建GLTF组件
:
<xr-gltf id="myGLTF" model="gltfModel"></xr-gltf>
3.标签属性
4.事件:
5.修改 :
GLTF组件提供了一系列接口来让用户操作xr-frame为GLTF模型生成的内部子树,开发者也可以用这些接口来动态修改glTF模型的颜色、纹理等材质属性。
用法:修改GLTF贴图
// gltf信息
{
...
"nodes": [{
"mesh": 0,
"name": "Banana"
}],
...
}
<xr-gltf id="myGLTF" model="gltfModel" bind:gltf-loaded="handleGLTFLoaded"></xr-gltf>
function handleGLTFLoaded({ detail }) {
const el = detail.value.target;
//获取名字为nana的mesh
const gltf = el.getComponent("gltf");
const newMat = this.scene.assets.getAsset("texture", "...texture name...");
for (const mesh of gltf.getPrimitivesByNodeName("Banana")) {
//更改材质
mesh.material.setTexture("u_baseColorMap", newMat);
}
}
6.动画
如果GLTF模型有自带的动画,就会在当前元素下自动创建一个Animator组件
,并将GLTF模型内的动画片段加入到这个Animator组件
中。
如果GLTF组件
所在的元素本来已经拥有Animator组件
,就不会新建,而是直接使用这个Animator组件
。 在标签上添加anim-autoplay
属性可以自动播放GLTF模型内的动画,会播放GLTF内的所有动画片段:
- 自身就有动画直接加 anim-autoplay
<xr-gltf id="myGLTF" model="gltfModel" anim-autoplay></xr-gltf>
- 使用animator实现动画
1) 设置动画json
{
"keyframe": {
"parent": {
"0": {
"rotation": [0, 0, 0]
},
"100": {
"rotation": [0, 6.28, 0]
}
},
"child": {
"0": {
"position.y": -0.5
},
"100": {
"position.y": 1.5
}
}
},
"animation": {
"parent": {
"keyframe": "parent",
"duration": 8,
"ease": "linear",
"loop": -1
},
"child": {
"keyframe": "child",
"duration": 4,
"ease": "ease-in-out",
"direction": "both",
"loop": -1
}
}
}
2)加载动画资源
<xr-asset-load asset-id="anim" type="keyframe" src="/assets/animation/miku-kawaii-animation.json"/>
3)绑定动画
<xr-node anim-keyframe="anim" anim-autoplay="clip:parent">
4)控制动画
如果你只想播放GLTF模型里的某一个动画片段,或者想切换播放的动画片段,可以使用TS脚本来控制。
例如以下代码在GLTF模型渲染完毕后播放名为idle
的动画:
// xml
<xr-gltf id="myGLTF" model="gltfModel" bind:gltf-loaded="handleGLTFLoaded"></xr-gltf>
// ts
function handleGLTFLoaded({ detail }) {
const el = detail.value.target;
const animator = el.getComponent("animator");
animator.play("idle");
}
动画的名字idle
,对应的是.gltf文件中animations
数组节点的每一个元素的name
属性(参考官方文档)。
7.Morph Target:(demo详见下篇文章)
1.什么是Morph Target?详见:静态网格体变形目标 | 虚幻引擎文档 (unrealengine.com)
xr-frame
支持Morph Target动画,但是对target
的数量有限制,同一个模型最多使用8个target
。
这里的target
概念有别于.gltf文件内的target节点:.gltf文件内的一个同时拥有POSITION
和NORMAL
属性的target节点
,在xr-frame
中算作2个target
(不影响渲染效果)。
8.注意事项(gltf模型限制)
GLTF模型
需要满足以下条件才能正常渲染:
- 纹理使用的顶点
UV
不超过2个; - 使用的顶点
JOINTS
不超过1个; - 使用的顶点
WEIGHTS
不超过1个; - 不使用
sparse accessor
; accessor
的normalized
属性不为true
;morph targets
数量小于等于8个;morph
的属性为POSITION
,NORMAL
或TANGENT
;图元类型
不为LINE_LOOP
或TRIANGLE_FAN
;
经测试,只有少数模型会超出限制,大多数模型都可以正常渲染。并且随着项目迭代,未来将会解除或者放宽一些条件。
9.典型案例(多动画显示demo来源于微信开放文档)
1)wxml部分
<xr-scene id="xr-scene" bind:ready="handleReady">
<xr-assets bind:progress="handleAssetsProgress" bind:loaded="handleAssetsLoaded">
<xr-asset-load asset-id="anim" type="keyframe" src="/assets/animation/miku-kawaii-animation.json"/>
<xr-asset-load type="gltf" asset-id="cloud-station" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/cloud-station/index.glb" />
<xr-asset-load type="gltf" asset-id="miku-kawaii" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/shiteyanyo-hatsune-miku/index.glb" />
<xr-asset-load type="gltf" asset-id="miku" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/miku.glb" />
</xr-assets>
<xr-node>
<xr-node node-id="target" position="0 1 0" />
<xr-gltf position="0 0 0" scale="1 1 1" model="cloud-station" anim-autoplay></xr-gltf>
<xr-gltf position="1.8 -0.5 1.5" scale="0.12 0.12 0.12" rotation="0 180 0" model="miku" anim-autoplay></xr-gltf>
<xr-node anim-keyframe="anim" anim-autoplay="clip:parent">
<xr-node position="0 -0.5 3" rotation="0 90 0" anim-keyframe="anim" anim-autoplay="clip:child">
<xr-gltf model="miku-kawaii" anim-keyframe="anim" anim-autoplay></xr-gltf>
</xr-node>
</xr-node>
<xr-camera
id="camera" node-id="camera" position="0 1 7" clear-color="0.925 0.925 0.925 1"
target="target" background="default" camera-orbit-control=""
></xr-camera>
</xr-node>
<xr-node node-id="lights">
<xr-light type="ambient" color="1 1 1" intensity="1" />
<xr-light type="directional" rotation="40 180 0" color="1 1 1" intensity="2" />
</xr-node>
</xr-scene>
2)json动画部分
{
"keyframe": {
"parent": {
"0": {
"rotation": [0, 0, 0]
},
"100": {
"rotation": [0, 6.28, 0]
}
},
"child": {
"0": {
"position.y": -0.5
},
"100": {
"position.y": 1.5
}
}
},
"animation": {
"parent": {
"keyframe": "parent",
"duration": 8,
"ease": "linear",
"loop": -1
},
"child": {
"keyframe": "child",
"duration": 4,
"ease": "ease-in-out",
"direction": "both",
"loop": -1
}
}
}
3)js部分
Component({
properties: {
a: Number,
},
data: {
loaded: false
},
lifetimes: {
attached() {
console.log('data.a', this.data.a) // expected 123
}
},
methods: {
handleReady: function({detail}) {
this.scene = detail.value;
console.log('scene', detail.value);
},
handleAssetsProgress: function({detail}) {
console.log('assets progress', detail.value);
},
handleAssetsLoaded: function({detail}) {
console.log('assets loaded', detail.value);
this.setData({loaded: true});
},
handleRaf: function({detail}) {
console.log('raf', detail.value);
}
}
})
4)效果展示
xr-frame gltf动画