Three.js - 在另一个几何体的顶部

15
在 Three.js 中,是否可以让一个网格始终呈现在场景的顶部,即使它的位置在所有对象后面?我正在使用网格实现套索选择,我需要将选择框呈现在场景的其余部分之上。
4个回答

35

是的。

首先做这个:

renderer.autoClear = false;

首先创建一个包含你想要置顶的物体的第二个场景。接下来,在你的渲染循环中:
renderer.clear();                     // clear buffers
renderer.render( scene, camera );     // render scene 1
renderer.clearDepth();                // clear depth buffer
renderer.render( scene2, camera );    // render scene 2

three.js r.152


1
这是唯一的方法吗?我试图通过mesh.renderDepth、material.depthTest和material.depthWrite来实现相同的效果,但都没有成功。 - Andrew
@Andrew 如果你有问题,请发帖。 - WestLangley
@Andrew 正在尝试做同样的事情,查看 http://stackoverflow.com/questions/16247280/force-a-sprite-object-to-always-be-in-front-of-skydome-object-in-three-js - sixFingers
@trusktr 这个答案仍然有效。在你的 Codepen 中,执行 object2.renderOrder = 999; object2.onBeforeRender = function( renderer ) { renderer.clearDepth(); };。如果你有问题,请创建一个新的问题。 - WestLangley
@WestLangley 谢谢!我犯了一个错误。答案有效。我删除了我的评论。 - trusktr
我更喜欢使用一个场景和renderOrderonBeforeRender的方法。 - Gangula

3
如果你想在前面只渲染一个网格,你也可以通过将该对象的材质的depthTest设置为false来管理:
var onTopMaterial = new THREE.MeshStandardMaterial({
  color: 'red',
  depthTest: false
});

mesh.material = onTopMaterial;

请在这个示例中查看。


注意: 确保将 renderer.sortObjects 设置为默认值true


1
这不是一个好主意。有许多陷阱。首先,在蓝色立方体上设置 transparent: true。但最大的问题是通过设置 depthTest = false,所谓的“顶部网格”将不再深度测试自身,这可能会导致渲染伪影。 - WestLangley
@WestLangley 感谢您的反馈。在我的情况下,与透明设置冲突是可以接受的,但对于工件我不确定。我该如何测试?我只需要在“mouseover”时暂时将对象移动到所有其他对象的前面,然后在“mouseout”上移回。在这种情况下,似乎临时将它添加到不同的场景中是不可取的。 - Wilt
我会遵循被接受的答案中的方法,除非你有一个非常受限制的用例。 - WestLangley

1
以下是一个工作示例,其中有一个名为VisualLayers的类来管理任意数量的图层,并且它使用了renderer.autoClear = false和清除深度技术,就像West Langley在他的答案中提到的那样。
这种方法很好,因为不会修改对象的renderOrder(这是另一种方法),因此不会引入其他不同的问题,并且可以将其保存用于与分层无关的其他用例。
尝试在UI中玩弄选项以查看其功能:

// @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>

(在CodePen上的示例)


0

除了设置 object.renderOrder,您还需要在相关对象上将 material.depthTest 设置为 false。

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 );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接