Как выбрать один материал с помощью Raycaster и загрузчика GLTF

Я загружаю модель через загрузчик GLTF. Я хочу выбрать сетку при наведении мыши. Все идет хорошо, но основная проблема заключается в том, что при наведении он меняет цвет у всех материалов с одинаковым названием (согласно моим исследованиям). Когда я отлаживаю его INTERSECTED, возвращающий один материал. Я не знаю, почему это происходит. После многих исследований я задаю этот вопрос здесь.

Пожалуйста, смотрите мой код ниже.

<div id="ThreeJS" style="position: absolute; left:0px; top:0px"></div>
var container, scene, camera, renderer, controls, stats;
            var clock = new THREE.Clock();
            var xyzz;

            // custom global variables
            var cube;
            var projector,
                mouse = {
                    x: 0,
                    y: 0
                },
                INTERSECTED;

            init();
            animate();

            // FUNCTIONS
            function init() {
                // SCENE
                scene = new THREE.Scene();
                // CAMERA
                var SCREEN_WIDTH = window.innerWidth,
                    SCREEN_HEIGHT = window.innerHeight;
                var VIEW_ANGLE = 45,
                    ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT,
                    NEAR = 0.1,
                    FAR = 20000;
                camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
                scene.add(camera);
                camera.position.set(0, 0, 0);
                camera.lookAt(scene.position);

                renderer = new THREE.WebGLRenderer({
                    antialias: true
                });
                renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
                container = document.getElementById("ThreeJS");
                container.appendChild(renderer.domElement);
                // EVENTS

                // CONTROLS
                controls = new THREE.OrbitControls(camera, renderer.domElement);
                // STATS
                stats = new Stats();
                stats.domElement.style.position = "absolute";
                stats.domElement.style.bottom = "0px";
                stats.domElement.style.zIndex = 100;
                container.appendChild(stats.domElement);
                // LIGHT
                const skyColor = 0xb1e1ff; // light blue
                const groundColor = 0xb97a20; // brownish orange
                const intensity = 5;
                const light = new THREE.HemisphereLight(
                    skyColor,
                    groundColor,
                    intensity
                );
                scene.add(light);
                scene.background = new THREE.Color("#fff");


                // GLTF Loader
                function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {
                    const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
                    const halfFovY = THREE.Math.degToRad(camera.fov * 0.5);
                    const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
                    // compute a unit vector that points in the direction the camera is now
                    // in the xz plane from the center of the box
                    const direction = new THREE.Vector3()
                        .subVectors(camera.position, boxCenter)
                        .multiply(new THREE.Vector3(1, 0, 1))
                        .normalize();

                    // move the camera to a position distance units way from the center
                    // in whatever direction the camera was from the center already
                    camera.position.copy(
                        direction.multiplyScalar(distance).add(boxCenter)
                    );

                    // pick some near and far values for the frustum that
                    // will contain the box.
                    camera.near = boxSize / 100;
                    camera.far = boxSize * 100;

                    camera.updateProjectionMatrix();

                    // point the camera to look at the center of the box
                    // camera.position.set(0, 150, 400);
                    camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
                }
                var loader = new THREE.GLTFLoader();
                loader.load(
                    // resource URL
                    "models/gltf/DamagedHelmet/glTF/50423_ Revit Model.gltf",
                    // called when the resource is loaded
                    function(gltf) {
                        const root = gltf.scene;
                        scene.add(root);
                        // console.log(dumpObject(root).join("\n"));
                        const box = new THREE.Box3().setFromObject(root);

                        const boxSize = box.getSize(new THREE.Vector3()).length();
                        const boxCenter = box.getCenter(new THREE.Vector3());

                        // set the camera to frame the box
                        frameArea(boxSize * 1, boxSize, boxCenter, camera);

                        // update the Trackball controls to handle the new size

                        controls.maxDistance = boxSize * 10;
                        controls.target.copy(boxCenter);
                        controls.update();
                    },
                    // called while loading is progressing
                    function(xhr) {
                        console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
                    },
                    // called when loading has errors
                    function(error) {
                        debugger;
                        console.log("An error happened");
                    }
                );
                projector = new THREE.Projector();

                // when the mouse moves, call the given function
                document.addEventListener("mousemove", onDocumentMouseMove, false);
            }

            function onDocumentMouseMove(event) {
                // update the mouse variable
                mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
                mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
            }

            function animate() {
                requestAnimationFrame(animate);
                render();
                update();
            }

            function update() {
                // find intersections

                // create a Ray with origin at the mouse position
                //   and direction into the scene (camera direction)
                var vector = new THREE.Vector3(mouse.x, mouse.y, 1);
                vector.unproject(camera);
                var ray = new THREE.Raycaster(
                    camera.position,
                    vector.sub(camera.position).normalize()
                );

                // create an array containing all objects in the scene with which the ray intersects

                var intersects = ray.intersectObjects(scene.children, true);

                // INTERSECTED = the object in the scene currently closest to the camera
                //      and intersected by the Ray projected from the mouse position

                // if there is one (or more) intersections
                if (intersects.length > 0) {
                    // if the closest object intersected is not the currently stored intersection object
                    if (intersects[0].object != INTERSECTED) {
                        // restore previous intersection object (if it exists) to its original color
                        if (INTERSECTED) {
                            INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
                        }

                        // store reference to closest object as current intersection object

                        INTERSECTED = intersects[0].object;
                        console.log(INTERSECTED);
                        // store color of closest object (for later restoration)
                        INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
                        // set a new color for closest object
                        INTERSECTED.material.color.setHex(0xffff00);
                    }
                }
                // there are no intersections
                else {
                    // restore previous intersection object (if it exists) to its original color
                    if (INTERSECTED)
                        INTERSECTED.material.color.setHex(INTERSECTED.currentHex);

                    // remove previous intersection object reference
                    //     by setting current intersection object to "nothing"
                    INTERSECTED = null;
                }

                controls.update();
                stats.update();
            }

            function render() {
                renderer.render(scene, camera);
            }
            function dumpObject(obj, lines = [], isLast = true, prefix = "") {
                const localPrefix = isLast ? "└─" : "├─";
                lines.push(
                    `${prefix}${prefix ? localPrefix : ""}${obj.name || "*no-name*"} [${
                        obj.type
                    }]`
                );
                const newPrefix = prefix + (isLast ? "  " : "│ ");
                const lastNdx = obj.children.length - 1;
                obj.children.forEach((child, ndx) => {
                    const isLast = ndx === lastNdx;
                    dumpObject(child, lines, isLast, newPrefix);
                });
                return lines;
            }

Пожалуйста, помогите мне.


person Abhay Singh    schedule 10.07.2019    source источник


Ответы (1)


Я не читал весь код, но думаю, что это уже может помочь:

В обработчике пересечения вы обновляете цвет материала, назначенного объекту (INTERSECTED.material.color.setHex(...)). Это вызовет проблемы, которые вы описываете, поскольку идентичные материалы, скорее всего, будут повторно использоваться для нескольких объектов. Чтобы предотвратить это, вы можете использовать другой материал:

const hightlightMaterial = new MeshStandardMaterial(...);

и вместо того, чтобы просто обновить цвет, замените материал:

INTERSECTED.originalMaterial = INTERSECTED.material;
INTERSECTED.material = highlightMaterial;

Восстановить оригинал при «отмене выделения» объекта:

INTERSECTED.material = INTERSECTED.originalMaterial;
delete INTERSECTED.originalMaterial;

Если вам нужно, чтобы highlightMaterial сохранил другие свойства материала из оригинала, вы можете сделать это, чтобы заранее скопировать все свойства материала:

highlightMaterial.copy(INTERSECTED.material);
highlightMaterial.color.copy(highlightColor);
person Martin Schuhfuß    schedule 10.07.2019