如何在A帧中更新GLTF模型的修改顶点?

时间:2019-08-12 19:08:13

标签: animation aframe gltf

我有一个GLTF模型,其中的网格代表低聚水。在A框架中加载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-Frame中加载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模型中,并附加了名为 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 中,而是存储在 geometry.attributes.position.array 中,因为三角形是顶点的索引。这使我怀疑 verticesNeedUpdate 标志仅适用于 geometry.vertices ,但我不确定。

位置数组中的顶点似乎确实已更新,因为我可以看到网格上的阴影变化是由更新的法线引起的。

在绘制水网格时,如何使A-Frame使用更新的顶点?

1 个答案:

答案 0 :(得分:1)

根据docsthis anwser

使用缓冲区几何图形(并且这样加载模型)时,需要在更改的属性对象上设置needsUpdate标志:

// Vertices change
var vertices = node.geometry.attributes.position.array;
// .... updates ...

// nofitication
node.geometry.attributes.position.needsUpdate = true

小故障here