如何更新 A-Frame 中 GLTF 模型的修改顶点?
How to update modified vertices of GLTF model in A-Frame?
我有一个 GLTF 模型,其网格代表低聚水。在 A-Frame 中加载 GLTF 模型后,我试图通过修改 tick() 函数中每个顶点的 y 坐标来为水设置动画。
问题是修改后的顶点没有绘制在屏幕上,只绘制了初始修改。我错过了什么?
为了重现该问题,我制作了一个简单的 GLTF 模型,其中包含一个 4x4 网格中的 32 个三角形的单个网格:
{
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5123,
"count": 96,
"max": [
24
],
"min": [
0
],
"type": "SCALAR"
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 25,
"max": [
1.0,
0.0,
1.0
],
"min": [
-1.0,
0.0,
-1.0
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 0,
"componentType": 5126,
"count": 25,
"max": [
0.0,
1.0,
0.0
],
"min": [
0.0,
1.0,
0.0
],
"type": "VEC3"
}
],
"asset": {
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 192,
"byteOffset": 0,
"target": 34963
},
{
"buffer": 0,
"byteLength": 300,
"byteOffset": 192,
"target": 34962
},
{
"buffer": 0,
"byteLength": 300,
"byteOffset": 492,
"target": 34962
}
],
"buffers": [
{
"byteLength": 792,
"uri": "data:application/octet-stream;base64,AAABAAIAAAADAAEAAwAEAAEAAwAFAAQABQAGAAQABQAHAAYABwAIAAYABwAJAAgABgAIAAoABgAKAAsABAAGAAsABAALAAwAAQAEAAwAAQAMAA0AAgABAA0AAgANAA4ADgANAA8ADgAPABAAEAAPABEAEAARABIADwATABEADwAUABMADQAUAA8ADQAMABQADAAVABQADAALABUACwAWABUACwAKABYAFQAWABcAFQAXABgAFAAVABgAFAAYABMAAACAvwAAAAAAAIC/AAAAvwAAAAAAAAC/AAAAvwAAAAAAAIC/AACAvwAAAAAAAAC/AAAAvwAAAAAAAACAAACAvwAAAAAAAAAAAAAAvwAAAAAAAAA/AACAvwAAAAAAAAA/AAAAvwAAAAAAAIA/AACAvwAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAA/AAAAAAAAAAAAAACAAAAAAAAAAAAAAAC/AAAAAAAAAAAAAIC/AAAAPwAAAAAAAAC/AAAAPwAAAAAAAIC/AACAPwAAAAAAAAC/AACAPwAAAAAAAIC/AACAPwAAAAAAAAAAAAAAPwAAAAAAAACAAAAAPwAAAAAAAAA/AAAAPwAAAAAAAIA/AACAPwAAAAAAAIA/AACAPwAAAAAAAAA/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAA"
}
],
"materials": [
{
"name": "Water",
"pbrMetallicRoughness": {
"baseColorFactor": [
0.0,
0.46700000762939453,
0.7450000047683716,
1.0
],
"metallicFactor": 0.0,
"roughnessFactor": 1.0
}
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 2,
"POSITION": 1
},
"indices": 0,
"material": 0,
"mode": 4
}
]
}
],
"nodes": [
{
"mesh": 0,
"name": "water"
}
],
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
]
}
要在 A 帧中加载 GLTF 模式并设置动画,我使用以下 HTML + JavaScript:
<html>
<head>
<script src="https://aframe.io/releases/0.9.2/aframe.min.js"></script>
</head>
<body>
<script>
AFRAME.registerComponent('aniwater', {
init: function() {
const data = this.data;
this.setupWater();
this.el.addEventListener('object3dset', this.setupWater.bind(this));
},
tick: function() {
const mesh = this.el.getObject3D('mesh');
if (!mesh) {
return;
}
mesh.traverse(function (node) {
// water is child node
if (node.name === "water") {
var vertexCount = node.geometry.attributes.position.count;
var vertices = node.geometry.attributes.position.array;
// update y of vertex
for (var i = 0; i < vertexCount; i++) {
var vy = vertices[i * 3 + 1];
// get the data associated to it
var vprops = this.waves[i];
// update the position of the vertex
vy = vprops.y + Math.sin(vprops.ang) * vprops.amp;
// increment the angle for the next frame
vprops.ang += vprops.speed;
vertices[i * 3 + 1] = vy;
}
// compute normals
node.geometry.computeFaceNormals();
node.geometry.computeVertexNormals();
// this does not work?
node.geometry.verticesNeedUpdate=true;
}
});
},
setupWater: function() {
const mesh = this.el.getObject3D('mesh');
if (!mesh) {
return;
}
mesh.traverse(function (node) {
// water is child node
if (node.name === "water") {
// array with wave information
this.waves = [];
var vertexCount = node.geometry.attributes.position.count;
var vertices = node.geometry.attributes.position.array;
// create an array to store new data associated to each vertex
for (var i = 0; i < vertexCount; i++) {
// get y position of each vertex
var vy = vertices[i * 3 + 1];
// store some data associated to it
this.waves.push({
y: vy,
// random start angle
ang: Math.random() * Math.PI * 2,
// random amplitude
amp: 0.5 + Math.random() * 0.15,
// random speed
speed:0.016 + Math.random() * 0.032
});
}
}
});
}
});
</script>
<a-scene>
<a-assets>
<a-asset-item id="water_asset" src="water.gltf">
</a-assets>
<a-gltf-model id="water" src="#water_asset" position="0 0 -4" aniwater></a-gltf-model>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
</body>
</html>
使用资产将 GLTF 模型加载到 a-gltf-model 中,并附加名为 aniwater 的自定义组件。
自定义组件基于 aframes-extra 存储库 (https://github.com/donmccurdy/aframe-extras/blob/master/src/misc/cube-env-map.js) 中的 cube-env-map.js 并定义了 3 个函数。
setupWater() 函数创建一个数组,其中包含有关每个顶点的信息,以便可以为水设置动画。这是目前基于此处找到的代码:https://tympanus.net/codrops/2016/04/26/the-aviator-animating-basic-3d-scene-threejs/
init() 函数在加载模型时将 setupWater() 函数附加到模型。这似乎工作正常。
顶点的实际修改是在函数 tick() 中完成的。对于每个顶点,更新 y 坐标并计算 y 的新值。在此之后,法线被重新计算并且 verticesNeedUpdate 标志被设置为 true 以指示更改。 这似乎不起作用。
我认为问题出在这样一个事实,即 GLTF 模型的顶点没有存储在 geometry.vertices 中,而是存储在 [=56 中=] 因为三角形是顶点的索引。这让我怀疑 verticesNeedUpdate 标志仅适用于 geometry.vertices,但我不确定。
位置数组中的顶点似乎确实更新了,因为我可以看到网格上的阴影变化是由更新的法线引起的。
如何让A-Frame在绘制水网格时使用更新后的顶点?
我有一个 GLTF 模型,其网格代表低聚水。在 A-Frame 中加载 GLTF 模型后,我试图通过修改 tick() 函数中每个顶点的 y 坐标来为水设置动画。
问题是修改后的顶点没有绘制在屏幕上,只绘制了初始修改。我错过了什么?
为了重现该问题,我制作了一个简单的 GLTF 模型,其中包含一个 4x4 网格中的 32 个三角形的单个网格:
{
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5123,
"count": 96,
"max": [
24
],
"min": [
0
],
"type": "SCALAR"
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 25,
"max": [
1.0,
0.0,
1.0
],
"min": [
-1.0,
0.0,
-1.0
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 0,
"componentType": 5126,
"count": 25,
"max": [
0.0,
1.0,
0.0
],
"min": [
0.0,
1.0,
0.0
],
"type": "VEC3"
}
],
"asset": {
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 192,
"byteOffset": 0,
"target": 34963
},
{
"buffer": 0,
"byteLength": 300,
"byteOffset": 192,
"target": 34962
},
{
"buffer": 0,
"byteLength": 300,
"byteOffset": 492,
"target": 34962
}
],
"buffers": [
{
"byteLength": 792,
"uri": "data:application/octet-stream;base64,AAABAAIAAAADAAEAAwAEAAEAAwAFAAQABQAGAAQABQAHAAYABwAIAAYABwAJAAgABgAIAAoABgAKAAsABAAGAAsABAALAAwAAQAEAAwAAQAMAA0AAgABAA0AAgANAA4ADgANAA8ADgAPABAAEAAPABEAEAARABIADwATABEADwAUABMADQAUAA8ADQAMABQADAAVABQADAALABUACwAWABUACwAKABYAFQAWABcAFQAXABgAFAAVABgAFAAYABMAAACAvwAAAAAAAIC/AAAAvwAAAAAAAAC/AAAAvwAAAAAAAIC/AACAvwAAAAAAAAC/AAAAvwAAAAAAAACAAACAvwAAAAAAAAAAAAAAvwAAAAAAAAA/AACAvwAAAAAAAAA/AAAAvwAAAAAAAIA/AACAvwAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAA/AAAAAAAAAAAAAACAAAAAAAAAAAAAAAC/AAAAAAAAAAAAAIC/AAAAPwAAAAAAAAC/AAAAPwAAAAAAAIC/AACAPwAAAAAAAAC/AACAPwAAAAAAAIC/AACAPwAAAAAAAAAAAAAAPwAAAAAAAACAAAAAPwAAAAAAAAA/AAAAPwAAAAAAAIA/AACAPwAAAAAAAIA/AACAPwAAAAAAAAA/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAA"
}
],
"materials": [
{
"name": "Water",
"pbrMetallicRoughness": {
"baseColorFactor": [
0.0,
0.46700000762939453,
0.7450000047683716,
1.0
],
"metallicFactor": 0.0,
"roughnessFactor": 1.0
}
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 2,
"POSITION": 1
},
"indices": 0,
"material": 0,
"mode": 4
}
]
}
],
"nodes": [
{
"mesh": 0,
"name": "water"
}
],
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
]
}
要在 A 帧中加载 GLTF 模式并设置动画,我使用以下 HTML + JavaScript:
<html>
<head>
<script src="https://aframe.io/releases/0.9.2/aframe.min.js"></script>
</head>
<body>
<script>
AFRAME.registerComponent('aniwater', {
init: function() {
const data = this.data;
this.setupWater();
this.el.addEventListener('object3dset', this.setupWater.bind(this));
},
tick: function() {
const mesh = this.el.getObject3D('mesh');
if (!mesh) {
return;
}
mesh.traverse(function (node) {
// water is child node
if (node.name === "water") {
var vertexCount = node.geometry.attributes.position.count;
var vertices = node.geometry.attributes.position.array;
// update y of vertex
for (var i = 0; i < vertexCount; i++) {
var vy = vertices[i * 3 + 1];
// get the data associated to it
var vprops = this.waves[i];
// update the position of the vertex
vy = vprops.y + Math.sin(vprops.ang) * vprops.amp;
// increment the angle for the next frame
vprops.ang += vprops.speed;
vertices[i * 3 + 1] = vy;
}
// compute normals
node.geometry.computeFaceNormals();
node.geometry.computeVertexNormals();
// this does not work?
node.geometry.verticesNeedUpdate=true;
}
});
},
setupWater: function() {
const mesh = this.el.getObject3D('mesh');
if (!mesh) {
return;
}
mesh.traverse(function (node) {
// water is child node
if (node.name === "water") {
// array with wave information
this.waves = [];
var vertexCount = node.geometry.attributes.position.count;
var vertices = node.geometry.attributes.position.array;
// create an array to store new data associated to each vertex
for (var i = 0; i < vertexCount; i++) {
// get y position of each vertex
var vy = vertices[i * 3 + 1];
// store some data associated to it
this.waves.push({
y: vy,
// random start angle
ang: Math.random() * Math.PI * 2,
// random amplitude
amp: 0.5 + Math.random() * 0.15,
// random speed
speed:0.016 + Math.random() * 0.032
});
}
}
});
}
});
</script>
<a-scene>
<a-assets>
<a-asset-item id="water_asset" src="water.gltf">
</a-assets>
<a-gltf-model id="water" src="#water_asset" position="0 0 -4" aniwater></a-gltf-model>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
</body>
</html>
使用资产将 GLTF 模型加载到 a-gltf-model 中,并附加名为 aniwater 的自定义组件。
自定义组件基于 aframes-extra 存储库 (https://github.com/donmccurdy/aframe-extras/blob/master/src/misc/cube-env-map.js) 中的 cube-env-map.js 并定义了 3 个函数。
setupWater() 函数创建一个数组,其中包含有关每个顶点的信息,以便可以为水设置动画。这是目前基于此处找到的代码:https://tympanus.net/codrops/2016/04/26/the-aviator-animating-basic-3d-scene-threejs/
init() 函数在加载模型时将 setupWater() 函数附加到模型。这似乎工作正常。
顶点的实际修改是在函数 tick() 中完成的。对于每个顶点,更新 y 坐标并计算 y 的新值。在此之后,法线被重新计算并且 verticesNeedUpdate 标志被设置为 true 以指示更改。 这似乎不起作用。
我认为问题出在这样一个事实,即 GLTF 模型的顶点没有存储在 geometry.vertices 中,而是存储在 [=56 中=] 因为三角形是顶点的索引。这让我怀疑 verticesNeedUpdate 标志仅适用于 geometry.vertices,但我不确定。
位置数组中的顶点似乎确实更新了,因为我可以看到网格上的阴影变化是由更新的法线引起的。
如何让A-Frame在绘制水网格时使用更新后的顶点?