Как выбрать отдельный материал с помощью Raycaster и GLTF loader - PullRequest
0 голосов
/ 10 июля 2019

Я загружаю модель через загрузчик 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;
            }

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

1 Ответ

1 голос
/ 10 июля 2019

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

В вашем обработчике пересечений вы обновляете цвет материала, назначенного объекту (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);
...