Three.js - Geometry on top of another

后端 未结 4 2015
耶瑟儿~
耶瑟儿~ 2020-11-29 08:01

Is it posible in Three.js to have a mesh always rendered on top of the scene, even if it\'s position is behind all objects? I\'m implementing a lasso selection with a mesh a

相关标签:
4条回答
  • 2020-11-29 08:09

    Yes.

    First do this:

    renderer.autoClear = false;
    

    Then create a second scene that contains just the objects you want to be on top. Then, in your render loop:

    renderer.clear();                     // clear buffers
    renderer.render( scene, camera );     // render scene 1
    renderer.clearDepth();                // clear depth buffer
    renderer.render( scene2, camera );    // render scene 2
    

    EDIT: Another solution is to have just one scene, but use this pattern:

    mesh.renderOrder = 999;
    mesh.onBeforeRender = function( renderer ) { renderer.clearDepth(); };
    

    If the mesh has a single material, it will render "on top".

    0 讨论(0)
  • 2020-11-29 08:16

    On top of setting object.renderOrder you have to set material.depthTest to false on the relevant objects.

    var spriteMaterial = new THREE.SpriteMaterial( { map: texture1, depthTest: false} );
    
        this.context1 = context1;
        this.texture1 = texture1;
    
        var sprite1 = new THREE.Sprite( spriteMaterial );
        sprite1.scale.set(30,15,1);
        sprite1.center.x=0;
        sprite1.center.y=0;
        sprite1.position.set( 0, 0, 0 );
        this.scene.add( sprite1 );
    
    0 讨论(0)
  • 2020-11-29 08:22

    The following is a working example that has a VisualLayers class for managing any number of layers, and it uses the renderer.autoClear = false and clearing-depth technique like West Langley mentioned in his answer.

    This approach is nice because renderOrder of objects is not being modified (that's another approach) and thus will not introduce other different issues, and you can save it for other use cases decoupled from layering.

    Try playing with the options in the UI to see what it does:

    // @ts-check
    
    ////////////////////////
    // LAYER SYSTEM
    ////////////////////////
    
    /** @typedef {{name: string, backingScene: THREE.Scene, order: number}} Layer */
    
    class VisualLayers {
        /**
         * @type {Array<Layer>}
         * @private
         */
        __layers = [];
    
        constructor(
            /** @private @type {THREE.WebGLRenderer} */ __renderer,
            /** @private @type {typeof THREE.Scene} */ __Scene = THREE.Scene
        ) {
            this.__renderer = __renderer;
            this.__Scene = __Scene;
        }
    
        defineLayer(/** @type {string} */ name, /** @type {number=} */ order = 0) {
            const layer = this.__getLayer(name);
    
            // The default layer always has order 0.
            const previousOrder = layer.order;
            layer.order = name === "default" ? 0 : order;
    
            // Sort only if order changed.
            if (previousOrder !== layer.order)
                this.__layers.sort((a, b) => a.order - b.order);
    
            return layer;
        }
    
        /**
         * Get a layer by name (if it doesn't exist, creates it with default order 0).
         * @private
         */
        __getLayer(/** @type {string} */ name) {
            let layer = this.__layers.find((l) => l.name === name);
    
            if (!layer) {
                layer = { name, backingScene: new this.__Scene(), order: 0 };
                layer.backingScene.autoUpdate = false;
                this.__layers.push(layer);
            }
    
            return layer;
        }
    
        removeLayer(/** @type {string} */ name) {
            const index = this.__layers.findIndex((l) => {
                if (l.name === name) {
                    l.backingScene.children.length = 0;
                    return true;
                }
    
                return false;
            });
    
            if (index >= 0) this.__layers.splice(index, 1);
        }
    
        hasLayer(/** @type {string} */ name) {
            return this.__layers.some((l) => l.name === name);
        }
    
        /** @readonly */
        get layerCount() {
            return this.__layers.length;
        }
    
        addObjectToLayer(
            /** @type {THREE.Object3D} */ obj,
            /** @type {string | string[]} */ layers
        ) {
            if (Array.isArray(layers)) {
                for (const name of layers) this.__addObjectToLayer(obj, name);
                return;
            }
    
            this.__addObjectToLayer(obj, layers);
        }
    
        addObjectsToLayer(
            /** @type {THREE.Object3D[]} */ objects,
            /** @type {string | string[]} */ layers
        ) {
            for (const obj of objects) {
                this.addObjectToLayer(obj, layers);
            }
        }
    
        /** @private @readonly */
        __emptyArray = Object.freeze([]);
    
        /** @private */
        __addObjectToLayer(
            /** @type {THREE.Object3D} */ obj,
            /** @type {string} */ name
        ) {
            const layer = this.__getLayer(name);
            const proxy = Object.create(obj, {
                children: { get: () => this.__emptyArray }
            });
            layer.backingScene.children.push(proxy);
        }
    
        removeObjectFromLayer(
            /** @type {THREE.Object3D} */ obj,
            /** @type {string | string[]} */ nameOrNames
        ) {
            if (Array.isArray(nameOrNames)) {
                for (const name of nameOrNames) {
                    const layer = this.__layers.find((l) => l.name === name);
                    if (!layer) continue;
                    this.__removeObjectFromLayer(obj, layer);
                }
                return;
            }
    
            const layer = this.__layers.find((l) => l.name === nameOrNames);
            if (!layer) return;
            this.__removeObjectFromLayer(obj, layer);
        }
    
        /** @private */
        __removeObjectFromLayer(
            /** @type {THREE.Object3D} */ obj,
            /** @type {Layer} */ layer
        ) {
            const children = layer.backingScene.children;
            const index = children.findIndex(
                (proxy) => /** @type {any} */ (proxy).__proto__ === obj
            );
    
            if (index >= 0) {
                children[index] = children[children.length - 1];
                children.pop();
            }
        }
    
        removeObjectsFromAllLayers(/** @type {THREE.Object3D[]} */ objects) {
            for (const layer of this.__layers) {
                for (const obj of objects) {
                    this.__removeObjectFromLayer(obj, layer);
                }
            }
        }
    
        render(
            /** @type {THREE.Camera} */ camera,
            /** @type {(layerName: string) => void} */ beforeEach,
            /** @type {(layerName: string) => void} */ afterEach
        ) {
            for (const layer of this.__layers) {
                beforeEach(layer.name);
                this.__renderer.render(layer.backingScene, camera);
                afterEach(layer.name);
            }
        }
    }
    
    //////////////////////
    // VARS
    //////////////////////
    
    let camera, stats, geometry, material, object, object2, root;
    let time = 0;
    /** @type {THREE.Scene} */
    let scene;
    /** @type {THREE.WebGLRenderer} */
    let renderer;
    /** @type {VisualLayers} */
    let visualLayers;
    const clock = new THREE.Clock();
    const greenColor = "#27ae60";
    const options = {
        useLayers: true,
        showMiddleBox: true,
        rotate: true,
        layer2Order: 2
    };
    
    //////////////////////
    // INIT
    //////////////////////
    
    ~(function init() {
        setup3D();
        renderLoop();
    })();
    
    ////////////////////////////////
    // SETUP 3D
    ////////////////////////////////
    
    function setup3D() {
        const container = document.createElement("div");
        container.id = "container";
        document.body.appendChild(container);
    
        // CAMERA
        camera = new THREE.PerspectiveCamera(
            70,
            window.innerWidth / window.innerHeight,
            1,
            10000
        );
        camera.position.x = 0;
        camera.position.z = 500;
        camera.position.y = 0;
    
        scene = new THREE.Scene();
    
        // RENDERERS
    
        renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        renderer.setClearColor(0x111111);
        container.appendChild(renderer.domElement);
    
        // LAYERS
    
        visualLayers = new VisualLayers(renderer);
        // Layers don't have to be defined. Adding an object to a layer will
        // automatically create the layer with order 0. But let's define layers with
        // order values.
        visualLayers.defineLayer("layer1", 1);
        visualLayers.defineLayer("layer2", 2);
        visualLayers.defineLayer("layer3", 3);
    
        // LIGHTS
    
        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
        directionalLight.position.set(300, 0, 300);
        scene.add(directionalLight);
        visualLayers.addObjectToLayer(directionalLight, [
            "layer1",
            "layer2",
            "layer3"
        ]);
    
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
        scene.add(ambientLight);
        visualLayers.addObjectToLayer(ambientLight, ["layer1", "layer2", "layer3"]);
    
        // GEOMETRY
    
        root = new THREE.Object3D();
        scene.add(root);
    
        geometry = new THREE.BoxGeometry(100, 100, 100);
        material = new THREE.MeshPhongMaterial({
            color: greenColor,
            transparent: false,
            opacity: 1
        });
    
        object = new THREE.Mesh(geometry, material);
        root.add(object);
        visualLayers.addObjectToLayer(object, "layer1");
        object.position.y = 80;
        object.position.z = -20;
        // object.rotation.y = -Math.PI / 5
    
        object2 = new THREE.Mesh(geometry, material);
        object.add(object2);
        visualLayers.addObjectToLayer(object2, "layer2");
        object2.position.y -= 80;
        object2.position.z = -20;
        object2.rotation.y = -Math.PI / 5;
    
        const object3 = new THREE.Mesh(geometry, material);
        object2.add(object3);
        visualLayers.addObjectToLayer(object3, "layer3");
        object3.position.y -= 80;
        object3.position.z = -20;
        object3.rotation.y = -Math.PI / 5;
    
        // GUI
    
        const pane = new Tweakpane({
            title: "VisualLayers"
        });
        pane.addInput(options, "useLayers", { label: "use layers" });
        pane.addInput(options, "showMiddleBox", { label: "show middle box" });
        pane.addInput(options, "rotate");
        pane
            .addInput(options, "layer2Order", {
                label: "layer2 order",
                options: {
                    0: 0,
                    2: 2,
                    4: 4
                }
            })
            .on("change", () => visualLayers.defineLayer("layer2", options.layer2Order));
    
        // STATS
        // SEE: https://github.com/mrdoob/stats.js
    
        stats = new Stats();
        stats.domElement.style.position = "absolute";
        stats.domElement.style.left = "0px";
        stats.domElement.style.top = "0px";
        stats.setMode(0);
        document.body.appendChild(stats.domElement);
    }
    
    //////////////////////
    // RESIZE
    //////////////////////
    
    (window.onresize = function (event) {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    })();
    
    //////////////////////
    // RAF RENDER LOOP
    //////////////////////
    
    function render() {
        stats.begin();
    
        if (options.rotate) {
            time += clock.getDelta();
            object.rotation.y += 0.02;
            root.rotation.y = Math.PI / 2 + (Math.PI / 6) * Math.sin(time * 0.001);
        }
    
        object2.visible = options.showMiddleBox;
    
        if (options.useLayers) {
            scene.updateWorldMatrix(true, true);
            renderer.autoClear = false;
            renderer.clear();
            visualLayers.render(camera, beforeEachLayerRender, afterEachLayerRender);
        } else {
            renderer.autoClear = true;
            renderer.render(scene, camera);
        }
    
        stats.end();
    }
    
    function renderLoop() {
        render();
        requestAnimationFrame(renderLoop);
    }
    
    function beforeEachLayerRender(layer) {}
    function afterEachLayerRender(layer) {
        renderer.clearDepth();
    }
    html,
    body,
    #container {
        margin: 0px;
        padding: 0px;
        width: 100%;
        height: 100%;
    }
    
    canvas {
        background: transparent;
        display: block;
        width: 100%;
        height: 100%;
        position: absolute;
        left: 0;
        top: 0;
    }
    <script src="https://cdn.jsdelivr.net/npm/tweakpane@1.5.5/dist/tweakpane.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/stats.js/r11/Stats.min.js"></script>
    <script src="//unpkg.com/three@0.121.1/build/three.min.js"></script>
    <script src="//unpkg.com/postprocessing@6.17.4/build/postprocessing.js"></script>

    (example on codepen)

    0 讨论(0)
  • 2020-11-29 08:27

    If you want render only one mesh on front you can also manage by setting depthTest for the material of that object to false:

    var onTopMaterial = new THREE.MeshStandardMaterial({
      color: 'red',
      depthTest: false
    });
    
    mesh.material = onTopMaterial;
    

    Check a demonstation in this fiddle


    Note: Make sure you have renderer.sortObjects set to the default true value.

    0 讨论(0)
提交回复
热议问题