如何使用Raycaster和GLTF加载器选择单一材料

时间:2019-07-10 09:36:42

标签: javascript three.js mesh raycasting gltf

我正在通过GLTF加载程序加载模型。我想在鼠标悬停时选择一个网格。一切都变得很酷,但是主要的问题是,当将其名称相同的所有材料都改变颜色时(根据我的研究)。当我调试其相交返回单一材料。我不知道为什么会这样。经过大量研究,我在这里问这个问题。

请在下面查看我的代码。

<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 个答案:

答案 0 :(得分: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);