渲染对象在顶部使用renderDepth与Three.js

Render objects on top using renderDepth with Three.js

本文关键字:renderDepth Three js 对象 顶部      更新时间:2023-09-26

我正在尝试将对象呈现在其他对象之上。

在小提琴中,让绿色的环面结渲染在红色的盒子和地板上。

我一直在尝试mesh.renderDepth = 0, 1, 1000,但没有改变。

材料已创建如下:

  material = new THREE.MeshBasicMaterial( { color: 0x00ff00, transparent: true, opacity: 0.5 } );

我如何使用renderDepth实现这个?我还遗漏了其他设置吗?

使用depthTesttrue工作,但不能接受,因为凹网格(如环面)受到重叠多边形的影响,特别是在不透明的情况下。

虽然我还没有设法得到这两个场景运行的其他解决方案,它给我的印象是有更多的开销,也许比仅仅使用renderDepth更不灵活

完全工作JSFiddle: http://jsfiddle.net/QHssJ/

Thanks for the help

这篇文章有点老了,但是对于那些偶然发现这一点的人,请确保具有自定义renderDepth的对象的材料将depthWrite设置为false:

 material.depthWrite = false

这是一个更新张贴小提琴:http://jsfiddle.net/QHssJ/20/

下面是一个工作示例,它有一个VisualLayers类来管理任意数量的层,它使用renderer.autoClear = false和清除深度技术。

这种方法很好,因为对象的renderOrder没有被修改(这是另一种方法),因此不会引入与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>

(示例on codepen)