[SOLVED] Beginner - PerspectiveCamera

Hello there,

I’m a beginner with THREEJS, actually trying to make a simple reproduction of a stockage room. To do this, I am using a simple cube (900 x 10 x 900) to make the floor. It’s important to have a 10-z height. And, to show it, I am using a PerspectiveCamera. All the data I’ve used in based on the canvas_geometry_cube example.

But I don’t really understand how the PerspectiveCamera works - maybe it isn’t the kind of Camera I want. By setting cube position to 0,10,0 (X,Y,Z) and camera to 0,30,0 (X,Y,Z) without any rotation, the render didn’t show anything.

I don’t understand how PerspectiveCamera work. Any tips ?

To be honest, I want a view like in “Nier Automata Hacking Challenge” that you can observe on Google Image easely. I know : the code I’ve made doesn’t place the camera correctly, it is just a test, and I can’t understand why it show nothing.

Here is my full code. I did not get any Javascript error.

<!DOCTYPE html>
<html lang="fr">
<head>
    <title>Stock 3D View</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            font-family: Monospace;
            background-color: #514d41;
            margin: 0px;
            overflow: hidden;
        }
    </style>
</head>
<body>

    <script src="js/three.min.js"></script>

    <script src="js/Projector.js"></script>
    <script src="js/CanvasRenderer.js"></script>

    <script>

        var container;

        var camera, scene, renderer;

        var cube, info;
        
        var arrowUp = false;
        var arrowDown = false;
        var arrowLeft = false;
        var arrowRight = false;
        
        var speed = 4;

        var targetRotation = 0;
        var targetRotationOnMouseDown = 0;

        var mouseX = 0;
        var mouseXOnMouseDown = 0;

        var windowHalfX = window.innerWidth / 2;
        var windowHalfY = window.innerHeight / 2;

        init();
        animate();

        function init() {
            // Initialisation.
            container = document.createElement('div');
            document.body.appendChild(container);

            // Ajout phrase d'accroche en haut.
            info = document.createElement('div');
            info.style.position = 'absolute';
            info.style.top = '10px';
            info.style.width = '100%';
            info.style.textAlign = 'center';
            info.innerHTML = 'Visualisation de l\'entrepôt en 3D';
            container.appendChild(info);

            // Définition de la caméra.
            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
            camera.position.y = 30;
            camera.position.z = 0;
            camera.position.x = 0;
            // camera.rotation.x = 1.5707963;

            // Définition de la scène.
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x514d41);

            // Cube                                  X  Z   Y
            var geometry = new THREE.BoxGeometry(900,10,900);

            // Coloring Cube
            for ( var i = 0; i < geometry.faces.length; i += 2 ) {
                var hex = 0xc0b99c;
                geometry.faces[i].color.setHex(hex);
                geometry.faces[i+1].color.setHex(hex);
            }

            // Adding Cube to Scene
            var material = new THREE.MeshBasicMaterial({vertexColors: THREE.FaceColors, overdraw:0.5});

            cube = new THREE.Mesh(geometry, material);
            cube.position.x = 0;
            cube.position.y = 10;
            cube.position.z = 0;
            scene.add(cube);

            renderer = new THREE.CanvasRenderer();
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            container.appendChild(renderer.domElement);

            window.addEventListener( 'resize', onWindowResize, false );

        }

        function onWindowResize() {
            windowHalfX = window.innerWidth / 2;
            windowHalfY = window.innerHeight / 2;

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

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

        function animate() {
            requestAnimationFrame(animate);
            
            render();
        }

        function render() {
            renderer.render(scene, camera);
            
            window.onkeydown = function(e) { 
                if(e.key == "ArrowUp") {
                    arrowUp = true;
                    arrowDown = false;
                } else if(e.key == "ArrowDown") {
                    arrowUp = false;
                    arrowDown = true;
                } else if(e.key == "ArrowLeft") {
                    arrowLeft = true;
                    arrowright = false;
                } else if(e.key == "ArrowRight") {
                    arrowLeft = false;
                    arrowRight = true;
                }
            }
            
            window.onkeyup = function(e) {
                if(e.key == "ArrowUp") {
                    arrowUp = false;
                } else if(e.key == "ArrowDown") {
                    arrowDown = false;
                } else if(e.key == "ArrowLeft") {
                    arrowLeft = false;
                } else if(e.key == "ArrowRight") {
                    arrowRight = false;
                }
            }
            
            if(arrowUp) {
                camera.position.z -= speed;
            } else if(arrowDown) {
                camera.position.z += speed;
            }
            
            if(arrowLeft) {
                camera.position.x -= speed;
            } else if(arrowRight) {
                camera.position.x += speed;
            }
        }
    </script>

</body>
</html>

“I don’t understand how PerspectiveCamera work. Any tips ?”

see
https://www.youtube.com/watch?v=KyTaxN2XUyQ
https://www.script-tutorials.com/webgl-with-three-js-lesson-9/
https://threejs.org/examples/webgl_camera.html

1 Like

I suggest you use WebGLRenderer instead of CanvasRenderer. CanvasRenderer was originally intended as a fallback for WebGLRenderer but I think it’s not necessary to use it anymore.

Have a look at the following fiddle to see your slightly changed code with WebGLRenderer in action. Also notice the line camera.lookAt( cube.position ); which ensures the camera actually looks at the cube. Without this code, the camera just along the negative z axis.

https://jsfiddle.net/f2Lommf5/15055/

2 Likes

Hello,

Thanx both of you, used to check the link (Not Youtube, I’m at work). Now, I understand why it wasn’t working : check out my first code. camera wasn’t added to the scene. Also, camera have to be added after the scene creation : At least, that what I concluded.

Now using a new code with lookAt, I can easely get a top view of my room by only changing the Y axis. Using the console.log() with camera.position (.x, .y, .z), I noticed that my surface just dissapear when one of the axis get higher than my “far” parameter on my PerspectiveCamera. By increasing it to 3.000, everything seems to work. I now just need to get the good Euler angle to get what I want.

I will probably reply / edit this post when I’ll go further. I will probably encounter other problems with my camera.

@Mugen87 : I didn’t find any WebGLRenderer JS Lib on the /js/renderer/ deposit. Only a WebGLDeferredRenderer.js. Is it that one ? A conversion from CanvasRenderer to WebGLRenderer take times ?

Until then, here is now my full code :

<!DOCTYPE html>
<html lang="fr">
<head>
    <title>Stock 3D View</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            font-family: Monospace;
            background-color: #514d41;
            margin: 0px;
            overflow: hidden;
        }
    </style>
</head>
<body>

    <script src="js/three.min.js"></script>

    <script src="js/Projector.js"></script>
    <script src="js/CanvasRenderer.js"></script>

    <script>

        var container;

        var camera, scene, renderer;
        var cameraRig, activeCamera, activeHelper;
        var cameraPerspective, cameraOrtho;
        var cameraPerspectiveHelper, cameraOrthoHelper;
        var frustumSize = 600;
        var SCREEN_WIDTH = window.innerWidth;
        var SCREEN_HEIGHT = window.innerHeight;
        var aspect = SCREEN_WIDTH / SCREEN_HEIGHT;

        var cube, info;
        
        var arrowUp = false;
        var arrowDown = false;
        var arrowLeft = false;
        var arrowRight = false;
        
        var speed = 4;

        var targetRotation = 0;
        var targetRotationOnMouseDown = 0;

        var mouseX = 0;
        var mouseXOnMouseDown = 0;

        var windowHalfX = window.innerWidth / 2;
        var windowHalfY = window.innerHeight / 2;

        init();
        animate();

        function init() {
            // Initialisation.
            container = document.createElement('div');
            document.body.appendChild(container);

            // Ajout phrase d'accroche en haut.
            info = document.createElement('div');
            info.style.position = 'absolute';
            info.style.top = '10px';
            info.style.width = '100%';
            info.style.textAlign = 'center';
            info.innerHTML = 'Visualisation de l\'entrepôt en 3D';
            container.appendChild(info);

            // Définition de la scène.
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x514d41);

            // Cube                                  X  Z   Y
            var geometry = new THREE.BoxGeometry(900,10,900);

            // Coloring Cube
            for ( var i = 0; i < geometry.faces.length; i += 2 ) {
                var hex = 0xc0b99c;
                geometry.faces[i].color.setHex(hex);
                geometry.faces[i+1].color.setHex(hex);
            }

            // Adding Cube to Scene
            var material = new THREE.MeshBasicMaterial({vertexColors: THREE.FaceColors, overdraw:0.5});

            cube = new THREE.Mesh(geometry, material);
            cube.position.x = 0;
            cube.position.y = 10;
            cube.position.z = 0;
            scene.add(cube);
            
            // Définition de la caméra.
            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 3000);
            camera.lookAt(cube.position);
            camera.position.y = -900;
            scene.add(camera);
            // camera.rotation.x = 0.2;

            // Rendering
            renderer = new THREE.CanvasRenderer();
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            container.appendChild(renderer.domElement);

            window.addEventListener( 'resize', onWindowResize, false );

        }

        function onWindowResize() {
            windowHalfX = window.innerWidth / 2;
            windowHalfY = window.innerHeight / 2;

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

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

        function animate() {
            requestAnimationFrame(animate);
            
            render();
        }

        function render() {
            renderer.render(scene, camera);
            
            window.onkeydown = function(e) { 
                if(e.key == "ArrowUp") {
                    arrowUp = true;
                    arrowDown = false;
                } else if(e.key == "ArrowDown") {
                    arrowUp = false;
                    arrowDown = true;
                } else if(e.key == "ArrowLeft") {
                    arrowLeft = true;
                    arrowright = false;
                } else if(e.key == "ArrowRight") {
                    arrowLeft = false;
                    arrowRight = true;
                }
            }
            
            window.onkeyup = function(e) {
                if(e.key == "ArrowUp") {
                    arrowUp = false;
                } else if(e.key == "ArrowDown") {
                    arrowDown = false;
                } else if(e.key == "ArrowLeft") {
                    arrowLeft = false;
                } else if(e.key == "ArrowRight") {
                    arrowRight = false;
                }
            }
            
            if(arrowUp) {
                camera.position.y -= speed;
            } else if(arrowDown) {
                camera.position.y += speed;
            }
            
            if(arrowLeft) {
                camera.position.x -= speed;
            } else if(arrowRight) {
                camera.position.x += speed;
            }
            
            if(arrowUp || arrowDown || arrowLeft || arrowRight) {
                console.log("CAMERA : [X:"+camera.position.x+"] [Y:"+camera.position.y+"] [Z:"+camera.position.z+"]");
            }
        }
    </script>

</body>
</html>

WebGLRenderer is part of the three.js core. So it’s sufficient if you just import three.js into your app.

1 Like

Perfect.

Now I’ve got something working. Problem solved.

EDIT : For information, using Canvas make the “cube” dissapearing at some camera POV. WebGL solved the problem without doing anything more. So I recommend, as Mugen87 said, to use WebGL and never use Canvas anymore.

Here is the full code containing a simple “cube” (10, 100, 10) with all the margins data. As a last question, is there a way to get a grid of coordination points instead of poping items from their “centered-point” ? I mean, rendering a 10x10x10 cube on axis 0,0,0 will make the center of the cube being axis 0,0,0.

Is there a way to automatically place items as a grid other than calibrate every of them according to their size / 2 and add it on each axis point ? For example, having a 10x10x10 placed on axis 0,0,0 will place it on 5, 5, 5, making the bottom-left point of the cube at axis 0,0,0.

<!DOCTYPE html>
<html lang="fr">
<head>
    <title>Stock 3D View</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            font-family: Monospace;
            background-color: #514d41;
            margin: 0px;
            overflow: hidden;
        }
    </style>
</head>
<body>

    <script src="js/three.min.js"></script>

    <script src="js/Projector.js"></script>

    <script>

        var container;

        var camera, scene, renderer;

        var ground, info;
        
        var arrowUp = false;
        var arrowDown = false;
        var arrowLeft = false;
        var arrowRight = false;
        
        var speed = 4;

        var windowHalfX = window.innerWidth / 2;
        var windowHalfY = window.innerHeight / 2;

        init();
        animate();

        function init() {
            // Initialisation.
            container = document.createElement('div');
            document.body.appendChild(container);

            // Ajout phrase d'accroche en haut.
            info = document.createElement('div');
            info.style.position = 'absolute';
            info.style.top = '10px';
            info.style.width = '100%';
            info.style.textAlign = 'center';
            info.innerHTML = 'Visualisation de l\'entrepôt en 3D';
            container.appendChild(info);

            // Définition de la scène.
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x514d41);

            // ground                             X   Y  Z
            var geometry = new THREE.BoxGeometry(500,10,500);
            var geometry_cube = new THREE.BoxGeometry(10, 100, 10);

            // Coloring Ground
            var hex = 0xc0b99c;
            var hex2 = 0x7d775c;
            geometry.faces[0].color.setHex(hex2);
            geometry.faces[1].color.setHex(hex2);
            geometry.faces[2].color.setHex(hex2);
            geometry.faces[3].color.setHex(hex2);
            geometry.faces[4].color.setHex(hex);
            geometry.faces[5].color.setHex(hex);
            geometry.faces[6].color.setHex(hex2);
            geometry.faces[7].color.setHex(hex2);
            geometry.faces[8].color.setHex(hex2);
            geometry.faces[9].color.setHex(hex2);
            geometry.faces[10].color.setHex(hex2);
            
            // Coloring cubes.
            for ( var i = 0; i < geometry_cube.faces.length; i += 2 ) {
                var hex = 0x5b5a55;
                geometry_cube.faces[i].color.setHex(hex);
                geometry_cube.faces[i+1].color.setHex(hex);
            }

            // Adding ground to Scene
            var material = new THREE.MeshBasicMaterial({vertexColors: THREE.FaceColors, overdraw:0.5});
            ground = new THREE.Mesh(geometry, material);
            ground.position.x = 0;
            ground.position.y = 10;
            ground.position.z = 0;
            scene.add(ground);
            
            // Adding initial cube to scene.
            var material2 = new THREE.MeshBasicMaterial({vertexColors: THREE.FaceColors, overdraw:0.5});
            cube = new THREE.Mesh(geometry_cube, material2);
            cube.position.x = -(ground.geometry.parameters.width / 2) + (cube.geometry.parameters.width / 2);
            cube.position.y = (cube.geometry.parameters.height / 2) + (ground.geometry.parameters.height / 2) + ground.geometry.parameters.height;
            console.log(ground.geometry.parameters.height / 2);
            cube.position.z = (ground.geometry.parameters.width / 4) - cube.geometry.parameters.width;
            scene.add(cube);
            
            // Définition de la caméra.
            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 3000);
            camera.lookAt(ground.position);
            camera.position.y = ground.geometry.parameters.width / 2;
            camera.position.z = ground.geometry.parameters.width / 2;
            scene.add(camera);
            camera.rotation.x = -0.65;

            // Rendering
            renderer = new THREE.WebGLRenderer();
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            container.appendChild(renderer.domElement);

            window.addEventListener('resize', onWindowResize, false);
        }

        function onWindowResize() {
            windowHalfX = window.innerWidth / 2;
            windowHalfY = window.innerHeight / 2;

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

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

        function animate() {
            requestAnimationFrame(animate);
            
            render();
        }

        function render() {
            renderer.render(scene, camera);
            
            window.onkeydown = function(e) { 
                if(e.key == "ArrowUp") {
                    arrowUp = true;
                    arrowDown = false;
                } else if(e.key == "ArrowDown") {
                    arrowUp = false;
                    arrowDown = true;
                } else if(e.key == "ArrowLeft") {
                    arrowLeft = true;
                    arrowright = false;
                } else if(e.key == "ArrowRight") {
                    arrowLeft = false;
                    arrowRight = true;
                }
            }
            
            window.onkeyup = function(e) {
                if(e.key == "ArrowUp") {
                    arrowUp = false;
                } else if(e.key == "ArrowDown") {
                    arrowDown = false;
                } else if(e.key == "ArrowLeft") {
                    arrowLeft = false;
                } else if(e.key == "ArrowRight") {
                    arrowRight = false;
                }
            }
            
            if(arrowUp) {
                camera.position.z -= speed;
            } else if(arrowDown) {
                camera.position.z += speed;
            }
            
            if(arrowLeft) {
                camera.position.x -= speed;
            } else if(arrowRight) {
                camera.position.x += speed;
            }
        }
    </script>
</body>
</html>

Not sure this helps but you can translate the geometry like in the following fiddle so it looks like the box lies on a xz-plane (visualized through the grid). The box still has the position (0,0,0).

Seems like it works.

Will do a function to automatically do it for each element before render.
Thanx you ! Problem solved.