【问题标题】:Three.js: how to put objects to the top of the screen with same sizeThree.js:如何将相同大小的对象放在屏幕顶部
【发布时间】:2017-05-11 04:15:46
【问题描述】:

我是threejs的新手,我不知道这个问题是绝对基础还是太高级。 所以,我想把一些对象放在屏幕的左上角。下一个对象总是转到下一个正确的位置。我需要每个对象看起来都一样大小。我如何计算它们的 3d 位置和比例(如果需要)

我找到了一个可能对我有好处的解决方案:Three.js: Show world coordinate axes in corner of scene,但我认为第二个画布开销太大 - 它覆盖了原始画布。

我认为创建一个球体,其位置是相机位置,半径大于屏幕相机距离,并用光线投射器找到交点,这就是点。这是个好方法吗?

我的对象是箭头(多边形和拉伸),它们为我的用户显示选定的方向。所以这个助手是一个信息框,但我不想覆盖原来的场景。例如屏幕顶部的 10-12 箭头。并且它需要相同的大小才能成为美丽。

【问题讨论】:

    标签: javascript canvas three.js


    【解决方案1】:

    如果您想创建平视显示器 (HUD),一种方法是覆盖第二个精灵场景,使用正交相机渲染。

    使用的编码模式是这样的:

    camera = new THREE.PerspectiveCamera( ... );
    cameraOrtho = new THREE.OrthographicCamera( ... );
    
    scene = new THREE.Scene();
    sceneOrtho = new THREE.Scene(); // overlay scene
    
    renderer = new THREE.WebGLRenderer();
    renderer.autoClear = false; // to allow overlay
    

    并且,在渲染循环中:

    renderer.clear();
    renderer.render( scene, camera );
    renderer.clearDepth();
    renderer.render( sceneOrtho, cameraOrtho );
    

    http://threejs.org/examples/webgl_sprites.html

    three.js r.83

    【讨论】:

    • 听起来不错,示例运行良好,但我经历了第二个场景(正交)仅出现在我的案例中,第一个闪烁了一点时间(当我调整窗口大小时),然后渲染第二个场景,它会在没有第一个场景的情况下保持不变。我做错了什么?
    • 如示例中,您必须设置renderer.autoClear = false;
    • 谢谢,autoClear 是我的问题
    【解决方案2】:

    以下是一个工作示例,它有一个用于管理任意数量层的VisualLayers 类,它使用renderer.autoClear = false 和clearing-depth 技术。

    这种方法很好,因为 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>

    (example on codepen)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多