Как обновить тень при обновлении вершин пользовательской геометрии в a-frame / three.js

1

Я создал пользовательский компонент в a-frame, который создает заказную геометрию. Я использую функцию tick, чтобы оживить обновление вершин геометрии. Все работает отлично, однако тень на геометрии не обновляется.

В моем примере ниже я переключаю плоскую форму между двумя состояниями, свернутыми и развернутыми. Когда, например, он оживляет от сложенного до развернутого, лица сохраняют тень, как будто она все еще сложена.

Вот HTML, см. <a-fold> в этом примере, начиная со сложенного состояния (это можно изменить на "развернутый", изменив этот атрибут).

<html>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script> 
  </head>
  <body>
    <a-scene test>
        <a-fold
            state="folded"
            color="blue"
            position="-7.5 1 -20">
        </a-fold>
        <a-plane 
            position="0 0 -4" 
            rotation="-90 0 0" 
            width="20" 
            height="20" 
            color="#7BC8A4">
        </a-plane>
        <a-sky 
            color="#ECECEC">
        </a-sky>
    </a-scene>
  </body>
</html>

и javascript

//a trigger to test state change
AFRAME.registerComponent('test', {
  init: function () {
    setTimeout(function(){
          var target  = document.querySelector('a-fold');
          target.setAttribute('changestate', true);
    }, 2000);
  }
});

//component
AFRAME.registerComponent('folder', {

    schema: {         
        state: {type: 'string', default: 'unfolded'},
        color: {type: 'color', default: '#000'},
        changestate: {type: 'boolean', default: false},
        changedur: {type: 'number', default: 400},
    },

    store: {          
          unfolded: [{"x":15,"y":0,"z":0},{"x":15,"y":15,"z":0},{"x":0,"y":15,"z":0},{"x":0,"y":0,"z":0},{"x":0,"y":15,"z":0},{"x":15,"y":0,"z":0},],
          folded: [{"x":15,"y":0,"z":0},{"x":12,"y":12,"z":5},{"x":0,"y":15,"z":0},{"x":3,"y":3,"z":5},{"x":0,"y":15,"z":0},{"x":15,"y":0,"z":0},],       
    },

    update: function () {       
        this.geometry = new THREE.Geometry();
        var geometry = this.geometry
        var verts = this.store[this.data.state]

        $.each( verts, function( i, v3 ) {
            geometry.vertices.push ( new THREE.Vector3(v3.x, v3.y, v3.z) );
        });     
        geometry.faces.push(new THREE.Face3(0, 1, 2))
        geometry.faces.push(new THREE.Face3(3, 4, 5))
        geometry.computeBoundingBox();
        geometry.computeFaceNormals();
        geometry.computeVertexNormals();
        this.material = new THREE.MeshStandardMaterial({color: this.data.color});
        this.material.side = THREE.DoubleSide;
        this.material.needsUpdate = true
        this.mesh = new THREE.Mesh(this.geometry, this.material);
        this.mesh.castShadow = true;
        this.mesh.receiveShadow = true;
        this.el.setObject3D('mesh', this.mesh);

    },

    tick: function (t, td) {
        if (this.data.changestate === true){
            var dur = this.data.changedur
            var geom = this.el.getObject3D('mesh').geometry
            var currVerts = geom.vertices
            var toVerts = this.store[this.gotoState()]
            var somethingChanged = false;
            var allAxis = ["x", "y", "z"];
            var thisParent = this

            $.each( currVerts, function( i, vert) {
                var curr = currVerts[i]
                var to = toVerts[i]
                $.each( allAxis, function( i, axis ) {  
                    if (thisParent.approxEqual(curr[axis], to[axis])) {
                    somethingChanged = somethingChanged || false;
                    } else if (curr[axis] < to[axis]) {
                        var step = thisParent.stepCalc(curr[axis], to[axis], dur, td)
                        curr[axis] += step;
                        somethingChanged = true;
                    } else {
                        var step = thisParent.stepCalc(curr[axis], to[axis], dur, td)
                        curr[axis] -= step;
                        somethingChanged = true;
                    }
                });
            });
            geom.verticesNeedUpdate = somethingChanged;
        }
    },

    gotoState: function (){
        var to = ""
        var current = this.data.state
        var states = Object.keys(this.store)
        $.each( states, function( i, state) {
            if ( state != current ){
                to = state
            }
        }); 
        return to;
    },

    approxEqual: function (x, y) {
        return Math.abs(x - y) < 0.00001;
    },

    stepCalc: function (curr, to, dur, delta){
        var distance = Math.abs(curr - to)
        var speed = distance/dur
        var step = speed*delta

        return step;
    },

    remove: function () {
        this.el.removeObject3D('mesh');
    }

});

//primitive
AFRAME.registerPrimitive('a-fold', {

  defaultComponents: {
    folder: {}
  },

  mappings: {
    state: 'folder.state',
    color: 'folder.color',
    changestate: 'folder.changestate',
    changedur: 'folder.changedur'

  }

});

Извините, я знаю его много, но я не хочу его упрощать, если я потеряю проблему. Как вы можете видеть в компоненте, я попытался добавить this.material.needsUpdate = true а также добавить this.mesh.castShadow = true и this.mesh.receiveShadow = true но это не имеет никакого значения.

На стороне примечания, если я делаю анимацию всей сущности (т.е. Вращение), я могу видеть материал, отражающий свет, поэтому материал реагирует на освещение динамически, а не когда я меняю форму, обновляя вершины.

Вот два изображения, которые демонстрируют, что я имею в виду.

Изображение 174551

Изображение 174551

Вы можете видеть, что на втором изображении, хотя плоскость плоская, ее тени предполагают, что она имеет складку. И он делает то же самое наоборот (т.е. Если он начинает разворачиваться, тень правильная и сохраняется, когда она складывается).

Вот скрипка js

Если вам нужна дополнительная информация, пожалуйста, спрашивайте. Любая помощь очень ценится как всегда.

  • 0
    Вот скрипка с анимацией. Я думал, что у вас нет флага material.needsUpdate , но он не помог (хотя вы можете увидеть его в скрипке)
  • 0
    @PiotrAdamMilewski спасибо за попытку .. и за то, что анимация заработала. Из интереса, в моей оригинальной скрипке, почему этот бит js не работал, когда он был на панели js?
Показать ещё 2 комментария
Теги:
three.js
aframe

1 ответ

2
Лучший ответ

Освещение рассчитывается с использованием обычных векторов, в вашем случае вы перемещаете вершины сетки, но оставляете нормальные векторы, поэтому освещение не меняется. Вам нужно добавить geometry.computeVertexNormals(); в вашей функции тика после того, как вы изменили вершины.

В трехмерных тенях различаются оттенки или освещение, проблема, с которой вы сталкиваетесь, связана с освещением, но не с тенями. Вот почему настройка свойств mesh.castShadow не mesh.castShadow себя так, как вы ожидали.

  • 0
    Большое спасибо, Адам, и за ответ, и за объяснение различий, это действительно полезно. Я также пометил ответ Петра, который пришел с тем же решением почти в то же время, но, похоже, исчез. В любом случае, спасибо вам обоим, это сводит меня с ума.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню