mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
101.58k stars 35.29k forks source link

Reflector: Add support for `OrthographicCamera`. #28954

Open menghuaa opened 1 month ago

menghuaa commented 1 month ago

Description

When I changed the PerspectiveCamera to an OrthographicCamera, the reflector disappeared

Reproduction steps

1.use threejs r153 2.open docs/examples/webgl_mirror.html 2.replace line68 <camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 500 );> with <camera=new THREE.OrthographicCamera(-window.innerWidth/2, window.innerWidth/2, window.innerHeight/2, -window.innerHeight/2, 0.1, 1000);>

Code

<!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>

        <!-- Import maps polyfill -->
        <!-- Remove this when import maps will be widely supported -->
        <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

        <script type="importmap">
            {
                "imports": {
                    "three": "../build/three.module.js",
                    "three/addons/": "./jsm/"
                }
            }
        </script>

        <script type="module">

            import * as THREE from 'three';

            import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
            import { Reflector } from 'three/addons/objects/Reflector.js';

            let camera, scene, renderer;

            let cameraControls;

            let sphereGroup, smallSphere;

            let groundMirror, verticalMirror;

            init();
            animate();

            function init() {

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

                // renderer
                renderer = new THREE.WebGLRenderer( { antialias: true } );
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( window.innerWidth, window.innerHeight );
                container.appendChild( renderer.domElement );

                // scene
                scene = new THREE.Scene();

                // camera
                //camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 500 );
                camera=new THREE.OrthographicCamera(-window.innerWidth/2, window.innerWidth/2, window.innerHeight/2, -window.innerHeight/2, 0.1, 1000);
                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();

                //

                const planeGeo = new THREE.PlaneGeometry( 100.1, 100.1 );

                // reflectors/mirrors

                let geometry, material;

                geometry = new THREE.CircleGeometry( 40, 64 );
                groundMirror = new Reflector( geometry, {
                    clipBias: 0.003,
                    textureWidth: window.innerWidth * window.devicePixelRatio,
                    textureHeight: window.innerHeight * window.devicePixelRatio,
                    color: 0xb5b5b5
                } );
                groundMirror.position.y = 0.5;
                groundMirror.rotateX( - Math.PI / 2 );
                scene.add( groundMirror );

                geometry = new THREE.PlaneGeometry( 100, 100 );
                verticalMirror = new Reflector( geometry, {
                    clipBias: 0.003,
                    textureWidth: window.innerWidth * window.devicePixelRatio,
                    textureHeight: window.innerHeight * window.devicePixelRatio,
                    color: 0xc1cbcb
                } );
                verticalMirror.position.y = 50;
                verticalMirror.position.z = - 50;
                scene.add( verticalMirror );

                sphereGroup = new THREE.Object3D();
                scene.add( sphereGroup );

                geometry = new THREE.CylinderGeometry( 0.1, 15 * Math.cos( Math.PI / 180 * 30 ), 0.1, 24, 1 );
                material = new THREE.MeshPhongMaterial( { color: 0xffffff, emissive: 0x8d8d8d } );
                const sphereCap = new THREE.Mesh( geometry, material );
                sphereCap.position.y = - 15 * Math.sin( Math.PI / 180 * 30 ) - 0.05;
                sphereCap.rotateX( - Math.PI );

                geometry = new THREE.SphereGeometry( 15, 24, 24, Math.PI / 2, Math.PI * 2, 0, Math.PI / 180 * 120 );
                const 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 );

                sphereGroup.add( halfSphere );

                geometry = new THREE.IcosahedronGeometry( 5, 0 );
                material = new THREE.MeshPhongMaterial( { color: 0xffffff, emissive: 0x7b7b7b, flatShading: true } );
                smallSphere = new THREE.Mesh( geometry, material );
                scene.add( smallSphere );

                // walls
                const planeTop = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xffffff } ) );
                planeTop.position.y = 100;
                planeTop.rotateX( Math.PI / 2 );
                scene.add( planeTop );

                const planeBottom = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xffffff } ) );
                planeBottom.rotateX( - Math.PI / 2 );
                scene.add( planeBottom );

                const planeFront = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0x7f7fff } ) );
                planeFront.position.z = 50;
                planeFront.position.y = 50;
                planeFront.rotateY( Math.PI );
                scene.add( planeFront );

                const planeRight = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0x00ff00 } ) );
                planeRight.position.x = 50;
                planeRight.position.y = 50;
                planeRight.rotateY( - Math.PI / 2 );
                scene.add( planeRight );

                const planeLeft = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xff0000 } ) );
                planeLeft.position.x = - 50;
                planeLeft.position.y = 50;
                planeLeft.rotateY( Math.PI / 2 );
                scene.add( planeLeft );

                // lights
                const mainLight = new THREE.PointLight( 0xe7e7e7, 1.5, 250 );
                mainLight.position.y = 60;
                scene.add( mainLight );

                const greenLight = new THREE.PointLight( 0x00ff00, 0.25, 1000 );
                greenLight.position.set( 550, 50, 0 );
                scene.add( greenLight );

                const redLight = new THREE.PointLight( 0xff0000, 0.25, 1000 );
                redLight.position.set( - 550, 50, 0 );
                scene.add( redLight );

                const blueLight = new THREE.PointLight( 0xbbbbfe, 0.25, 1000 );
                blueLight.position.set( 0, 50, 550 );
                scene.add( blueLight );

                window.addEventListener( 'resize', onWindowResize );

            }

            function onWindowResize() {

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

                renderer.setSize( window.innerWidth, window.innerHeight );

                groundMirror.getRenderTarget().setSize(
                    window.innerWidth * window.devicePixelRatio,
                    window.innerHeight * window.devicePixelRatio
                );
                verticalMirror.getRenderTarget().setSize(
                    window.innerWidth * window.devicePixelRatio,
                    window.innerHeight * window.devicePixelRatio
                );

            }

            function animate() {

                requestAnimationFrame( animate );

                const timer = Date.now() * 0.01;

                sphereGroup.rotation.y -= 0.002;

                smallSphere.position.set(
                    Math.cos( timer * 0.1 ) * 30,
                    Math.abs( Math.cos( timer * 0.2 ) ) * 20 + 5,
                    Math.sin( timer * 0.1 ) * 30
                );
                smallSphere.rotation.y = ( Math.PI / 2 ) - timer * 0.1;
                smallSphere.rotation.z = timer * 0.8;

                renderer.render( scene, camera );

            }

        </script>
    </body>
</html>

Live example

image

Screenshots

No response

Version

153

Device

Desktop

Browser

Google Chrome

OS

Windows

Mugen87 commented 1 month ago

Would it be possible for you to switch over to WebGPURenderer? This issue is already solved there:

https://jsfiddle.net/xasqgp4v/

The mirror creation is different compared to WebGLRenderer but with the new material system you can easier control how the mirror effect should look like.

menghuaa commented 1 month ago

I cannot use WebGPURenderer, I can only use WebGLRenderer. How should I modify it? Why can't the reflector be displayed when using an OrthographicCamera?

Mugen87 commented 1 month ago

The problem is the dependency to projective texture mapping which is implemented differently with the new material. It is fixable but it requires some refactoring in Reflector.

menghuaa commented 1 month ago

The problem is the dependency to projective texture mapping which is implemented differently with the new material. It is fixable but it requires some refactoring in Reflector. @Mugen87 I want to refactor Reflector to make WebGL support OrthographicCamera create mirror. Can you indicate some directions for modification or provide me with some reference materials?

Mugen87 commented 1 month ago

I think we have to move away from texture2DProj() and a separate texture matrix and use uv coordinates like in WebGPURenderer instead.

menghuaa commented 1 month ago

I think we have to move away from texture2DProj() and a separate texture matrix and use uv coordinates like in WebGPURenderer instead.

Also, I would like to know how long it will take to refactor the reflector? I have an urgent task that needs to be completed next Thursday. Is there any other way to use Threejs to achieve mirroring in orthogonal cameras besides using Reflexor? If have, can you provide me with an implementation idea?

Mugen87 commented 1 month ago

Sorry, but this is a low-priority issue so I doubt somebody from the core team is going to working on this soon.

Please ask at the forum if you need help for an implementation.

menghuaa commented 1 month ago

Sorry, but this is a low-priority issue so I doubt somebody from the core team is going to working on this soon.

Please ask at the forum if you need help for an implementation.

Someone raised this question at thr forum a long time ago, but no one replied. I am a beginner in Threejs and I would like to know if there are any simple methods to achieve mirror.

WestLangley commented 1 month ago

@menghuaa Friendly tip:

camera = new THREE.OrthographicCamera( -window.innerWidth/2, window.innerWidth/2, window.innerHeight/2, -window.innerHeight/2, 0.1, 1000 )

That is incorrect in your example. See this StackOverflow answer for how to instantiate an orthographic camera. Also see how @Mugen87 instantiates it in https://jsfiddle.net/xasqgp4v/.

WestLangley commented 1 month ago

I cannot use WebGPURenderer, I can only use WebGLRenderer. I would like to know if there are any simple methods to achieve mirror.

I hear you, but to clarify, another possible workaround is to use WebGPURenderer, but force the WebGL back-end.

renderer = new THREE.WebGPURenderer( { antialias: true, forceWebGL: true } );
menghuaa commented 1 month ago

ut to clarif @WestLangley @Mugen87 I would like to know how long it would take for me to refactor Refactor myself? Also, because I render a lot of objects in the scene, would it make an error if I simply replace webglrender with webgpurender and use the functions in the original Three.module.js for other parts?

menghuaa commented 1 month ago

I cannot use WebGPURenderer, I can only use WebGLRenderer. I would like to know if there are any simple methods to achieve mirror.

I hear you, but to clarify, another possible workaround is to use WebGPURenderer, but force the WebGL back-end.

renderer = new THREE.WebGPURenderer( { antialias: true, forceWebGL: true } );

@WestLangley @Mugen87 I set renderer=new THREE WebGPURenderer( { antialias: true, forceWebGL: true } ); Is it compatible with the appearance and materials in the non GPU version of Threejs