How to handle many Reflectors

Hi everybody,

We are developing an App that contains multiple Reflectors (up to about 13).
As soon an we reach about 6 or 7 Reflectors the App gets very laggy and the OrbitControls are barely usable.
Is there some way to improve the performance of Reflectors?
For us it is more important that the Reflectors show the other 3D objects than the re-reflected image of the other Reflectors.
Can we reduce the recursion rate?
Can we avoid that Reflectors show other Reflectos at all?

Thanks for help

P.S. This modified sample app demonstrates our problem.

<!DOCTYPE html>

<html lang="en">

    <head>

        <title>three.js webgl - mirror</title>

        <meta charset="utf-8">

        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

        <link type="text/css" rel="stylesheet" href="main.css">

        <style>

            body {

                color: #444;

            }

            a {

                color: #08f;

            }

        </style>

    </head>

    <body>

        <div id="container"></div>

        <div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - mirror

        </div>

        <script type="module">

            import * as THREE from '../build/three.module.js';

            import { OrbitControls } from './OrbitControls.js';

            import { Reflector } from './Reflector.js';

            // scene size

            var WIDTH = window.innerWidth;

            var HEIGHT = window.innerHeight;

            // camera

            var VIEW_ANGLE = 45;

            var ASPECT = WIDTH / HEIGHT;

            var NEAR = 1;

            var FAR = 500;

            var camera, scene, renderer;

            var cameraControls;

            var sphereGroup, smallSphere;

            init();

            animate();

            function init() {

                var container = document.getElementById( 'container' );

                // renderer

                renderer = new THREE.WebGLRenderer( { antialias: true } );

                renderer.setPixelRatio( window.devicePixelRatio );

                renderer.setSize( WIDTH, HEIGHT );

                container.appendChild( renderer.domElement );

                // scene

                scene = new THREE.Scene();

                // camera

                camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR );

                camera.position.set( 0, 75, 160 );

                cameraControls = new OrbitControls( camera, renderer.domElement );

                cameraControls.target.set( 0, 40, 0 );

                cameraControls.maxDistance = 400;

                cameraControls.minDistance = 10;

                cameraControls.update();

                //

                var planeGeo = new THREE.PlaneBufferGeometry( 100, 100 );

                // reflectors/mirrors

                var geometry = new THREE.CylinderBufferGeometry( 0.1, 15 * Math.cos( Math.PI / 180 * 30 ), 0.1, 24, 1 );

                var material = new THREE.MeshPhongMaterial( { color: 0xffffff, emissive: 0x444444 } );

                var sphereCap = new THREE.Mesh( geometry, material );

                sphereCap.position.y = - 15 * Math.sin( Math.PI / 180 * 30 ) - 0.05;

                sphereCap.rotateX( - Math.PI );

                var geometry = new THREE.SphereBufferGeometry( 15, 24, 24, Math.PI / 2, Math.PI * 2, 0, Math.PI / 180 * 120 );

                var halfSphere = new THREE.Mesh( geometry, material );

                halfSphere.add( sphereCap );

                halfSphere.rotateX( - Math.PI / 180 * 135 );

                halfSphere.rotateZ( - Math.PI / 180 * 20 );

                halfSphere.position.y = 7.5 + 15 * Math.sin( Math.PI / 180 * 30 );

                scene.add( halfSphere );

                // walls

                var planeTop = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xffffff } ) );

                planeTop.position.y = 100;

                planeTop.rotateX( Math.PI / 2 );

                scene.add( planeTop );

                var planeBottom = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xffffff } ) );

                planeBottom.rotateX( - Math.PI / 2 );

                scene.add( planeBottom );

                // MIRRORS

                var geometry = new THREE.PlaneBufferGeometry( 45, 100 );

                var verticalMirror = new Reflector( geometry, {

                    clipBias: 0.003,

                    textureWidth: WIDTH * window.devicePixelRatio,

                    textureHeight: HEIGHT * window.devicePixelRatio,

                    color: 0x889999

                } );

                verticalMirror.position.y = 50;

                verticalMirror.position.z = - 50;

                scene.add( verticalMirror );

                var verticalMirror = new Reflector( geometry, {

                    clipBias: 0.003,

                    textureWidth: WIDTH * window.devicePixelRatio,

                    textureHeight: HEIGHT * window.devicePixelRatio,

                    color: 0x889999

                } );

                verticalMirror.position.x = 50;

                verticalMirror.position.y = 50;

                verticalMirror.rotateY( - Math.PI / 2 );

                scene.add( verticalMirror );

                var verticalMirror = new Reflector( geometry, {

                    clipBias: 0.003,

                    textureWidth: WIDTH * window.devicePixelRatio,

                    textureHeight: HEIGHT * window.devicePixelRatio,

                    color: 0x889999

                } );

                verticalMirror.position.x = -50;

                verticalMirror.position.y = 50;

                verticalMirror.rotateY(Math.PI / 2 );

                scene.add( verticalMirror );

                var verticalMirror = new Reflector( geometry, {

                    clipBias: 0.003,

                    textureWidth: WIDTH * window.devicePixelRatio,

                    textureHeight: HEIGHT * window.devicePixelRatio,

                    color: 0x889999

                } );

                verticalMirror.position.y = 50;

                verticalMirror.position.z = 50;

                verticalMirror.rotateY(Math.PI);

                scene.add( verticalMirror );

                var verticalMirror = new Reflector( geometry, {

                    clipBias: 0.003,

                    textureWidth: WIDTH * window.devicePixelRatio,

                    textureHeight: HEIGHT * window.devicePixelRatio,

                    color: 0x889999

                } );

                verticalMirror.position.x = 50 * 0.707;

                verticalMirror.position.y = 50;

                verticalMirror.position.z = 50 * -0.707;

                verticalMirror.rotateY(Math.PI * -1 / 4);

                scene.add( verticalMirror );

                var verticalMirror = new Reflector( geometry, {

                    clipBias: 0.003,

                    textureWidth: WIDTH * window.devicePixelRatio,

                    textureHeight: HEIGHT * window.devicePixelRatio,

                    color: 0x889999

                } );

                verticalMirror.position.x = 50 * -0.707;

                verticalMirror.position.y = 50;

                verticalMirror.position.z = 50 * 0.707;

                verticalMirror.rotateY(Math.PI * 3 / 4);

                scene.add( verticalMirror );

                var verticalMirror = new Reflector( geometry, {

                    clipBias: 0.003,

                    textureWidth: WIDTH * window.devicePixelRatio,

                    textureHeight: HEIGHT * window.devicePixelRatio,

                    color: 0x889999

                } );

                verticalMirror.position.x = 50 * 0.707;

                verticalMirror.position.y = 50;

                verticalMirror.position.z = 50 * 0.707;

                verticalMirror.rotateY(Math.PI * -3 / 4);

                scene.add( verticalMirror );

                var verticalMirror = new Reflector( geometry, {

                    clipBias: 0.003,

                    textureWidth: WIDTH * window.devicePixelRatio,

                    textureHeight: HEIGHT * window.devicePixelRatio,

                    color: 0x889999

                } );

                verticalMirror.position.x = 50 * -0.707;

                verticalMirror.position.y = 50;

                verticalMirror.position.z = 50 * -0.707;

                verticalMirror.rotateY(Math.PI * 1 / 4);

                scene.add( verticalMirror );

                // lights

                var mainLight = new THREE.PointLight( 0xcccccc, 1.5, 250 );

                mainLight.position.y = 60;

                scene.add( mainLight );

                var greenLight = new THREE.PointLight( 0x00ff00, 0.25, 1000 );

                greenLight.position.set( 550, 50, 0 );

                scene.add( greenLight );

                var redLight = new THREE.PointLight( 0xff0000, 0.25, 1000 );

                redLight.position.set( - 550, 50, 0 );

                scene.add( redLight );

                var blueLight = new THREE.PointLight( 0x7f7fff, 0.25, 1000 );

                blueLight.position.set( 0, 50, 550 );

                scene.add( blueLight );

            }

            function animate() {

                requestAnimationFrame( animate );

                var timer = Date.now() * 0.01;

                renderer.render( scene, camera );

            }

        </script>

    </body>

</html>

You have to be aware that a reflector has to render the entire scene with its virtual camera (which represents its mirrored view) which can of course impact the performance. Especially when using multiple reflectors.

This rate is fix and can’t be modified. It’s also not possible to exclude other reflectors from the internal, nested rendering. It seems you have to modify the source code of Reflector if you need these features implemented.

2 Likes