Three of three JS (WEB GL) perspective perspective and orthogonal perspective, and perspective switching from perspective to normal

Three of three JS (WEB GL) perspective perspective and orthogonal perspective, and perspective switching from perspective to normal

catalogue

Three of three JS (WEB GL) perspective perspective and orthogonal perspective, and perspective switching from perspective to normal

1, Brief introduction

2, Implementation principle

3, Simple explanation of orthographic projection and perspective projection

IV. perspective camera

5, Orthographic camera

6, Basic principle and code implementation of perspective projection camera to orthogonal projection camera in threejs

7, Case realization

8, Case code download

1, Brief introduction

Some knowledge sorting of Three js development is convenient for similar problems in the later stage and can be consulted and used in time.

This section introduces three JS (webgl) perspective perspective and orthogonal perspective, and the implementation is simple. Turn the current perspective angle into orthogonal perspective, and then switch back to the principle case of perspective perspective. If there are deficiencies, you are welcome to point out, or if you have a better method, you are welcome to leave a message.
 

2, Implementation principle

1. Three elements of scene construction: scene, camera and renderer

2. Among them, camera will first create a perspective camera as needed, and then switch to orthogonal camera according to the conversion

3. Then, through the perspective camera or orthogonal camera perspective rendering in renderer, the rendering from perspective perspective perspective to orthogonal perspective view is realized

3, Simple explanation of orthographic projection and perspective projection

The following is a brief introduction to the projection algorithm of orthographic camera and perspective camera. For beginners, you can have an impression. If you want to have an in-depth understanding, you can learn graphics or read the file orthographiccamera in the src directory of threejs official source code JS and perspectivecamera js

All objects in life are three-dimensional, but people's eyes can only see the front, not the blocked back. The effect of three-dimensional geometry in people's eyes is like a two-dimensional photo taken by a camera. What you see is a 2D projection. The process of transforming spatial geometry into a two-dimensional graph is projection. Different projection methods mean different algorithms of projection size.

  • For orthographic projection, the length of projection on the projection plane is different with the angle of a straight line;
  • For perspective projection, the result of projection is not only related to the angle of geometry, but also related to distance. Human eyes observe the world through perspective projection. For example, the farther you observe a railway, the smaller the width between the two tracks.

Whether orthographic or perspective projection, three JS encapsulates the relevant projection algorithms. You only need to choose different projection methods according to different application scenarios. When using an orthographic camera object, three JS will automatically calculate the projection results of geometry according to the orthographic projection algorithm; When using the perspective camera object, three JS will automatically calculate the projection results of geometry according to the perspective projection algorithm.

IV. perspective camera

This projection mode is used to simulate the scene seen by human eyes. It is the most common projection mode used in 3D scene rendering.

1. Construct perspective camera

 new THREE.PerspectiveCamera(60, width / height, 1, 1000)

/**
 * Perspective camera settings
 */
var width = window.innerWidth; //Window width
var height = window.innerHeight; //Window height
/**Perspective projection camera object*/
var camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.set(200, 300, 200); //Set camera position
camera.lookAt(scene.position); //Set the camera direction (pointing to the scene object)
parametermeaningDefault value
fovfov refers to the field of view. The so-called field of view is the angle range that can be seen. People's eyes can see the field of view of about 180 degrees. The size of the angle of view should be set according to the specific application. Generally, the game will set 60 ~ 90 degrees45
aspectAspect represents the aspect ratio of the rendering window. If there is only one full screen canvas on a web page and only one window on the canvas, the value of aspect is the aspect ratio of the client area of the web page windowwindow.innerWidth/window.innerHeight
nearThe near attribute indicates how far away from the camera to start rendering. Generally, a small value will be set.0.1
farFar attribute indicates how far away the camera is from the end of rendering. If the value is set too small, some scenes will not be seen1000

2. Attributes

For common attributes, see its base class Camera.
Please note that you will need to call after most properties have changed Update the projection matrix to make these changes take effect.

. : Float

The aspect ratio of the viewing cone of the camera is usually the width / height of the canvas. The default value is 1 (square canvas).

. : Float

The far end face of the camera. The default value is 2000.

This value must be greater than the value of near plane.

. : Float

Film size, which defaults to 35 (mm). This parameter does not affect the projection matrix of the camera unless filmOffset is set to a non-zero value.

. : Float

Horizontal off center offset, and The filmGauge unit is the same. The default value is 0.

. : Float

The distance of an object used for stereo vision and depth of field effects. This parameter will not affect the projection matrix of the camera unless StereoCamera is used. The default value is 10.

. : Float

The vertical field of view angle of the camera cone, expressed in angle from the bottom to the top of the view. The default value is 50.

. : Boolean

Read-only flag to check if a given object is of type PerspectiveCamera.

. : Float

The near end face of the camera. The default value is 0.1.

The valid value range is between 0 and the value of the current camera far plane. Note that, unlike orthographic camera, 0 is not a valid value for the proximal face of the perspective camera.

. : Object

Frustum window specification or null. This value is used setViewOffset method to set, using clearViewOffset method.

. : number

Gets or sets the zoom factor of the camera. Its default value is 1.

3. Method

For common methods, see its base class Camera.

. () : undefined

Clear any setViewOffset sets the offset of the.

. () : Float

combination. zoom to return the current vertical view angle in angle.

. () : Float

Returns the height of the image on the current film, if any If aspect is less than or equal to 1 (portrait format, vertical composition), the result is equal to filmGauge.

. () : Float

Returns the width of the image on the current film, if any If the aspect is greater than or equal to 1 (landscape format, horizontal composition), the result is equal to filmGauge.

. () : Float

Returns the current fov (visual field angle) relative to The focal length of the filmGauge.

. ( focalLength : Float ) : undefined

By relative to the current Focus of filmGauge and set FOV.

By default, the focal length is specified for a 35mm (full frame) camera.

. ( fullWidth : Float, fullHeight : Float, x : Float, y : Float, width : Float, height : Float ) : undefined

fullWidth - full width setting for multi view
fullHeight - full height setting for multi view
x - horizontal offset of secondary camera
y - vertical offset of secondary camera
Width - the width of the secondary camera
Height - height of secondary camera

Setting the offset in a larger viewing frustum is useful for setting up multiple windows or multiple displays.

5, Orthographic camera

In this projection mode, the size of the object remains unchanged in the final rendered image regardless of whether the object is far or close to the camera.
This is useful for rendering 2D scenes or UI elements.

1. Construct orthogonal camera

OrthographicCamera( left : Number, right : Number, top : Number, bottom : Number, near : Number, far : Number )

**
 * Orthographic camera settings
 */
var width = window.innerWidth; //Window width
var height = window.innerHeight; //Window height
var k = width / height; //Window aspect ratio
var s = 150; //The control coefficient of 3D scene display range. The larger the coefficient, the larger the display range
//Create camera object
var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
camera.position.set(200, 300, 200); //Set camera position
camera.lookAt(scene.position); //Set the camera direction (pointing to the scene object)
Parameters (properties)meaning
leftLeft boundary of rendered space
rightRight boundary of rendered space
topUpper boundary of rendering space
bottomLower boundary of rendering space
nearThe near attribute indicates how far away from the camera to start rendering. Generally, a small value will be set. The default value is 0.1
farFar attribute indicates how far away the camera is from the end of rendering. If the value is set too small, some scenes will not be seen. Default 1000

2. Attributes

For common attributes, see its base class Camera.
Please note that you will need to call after most properties have changed Update the projection matrix to make these changes take effect.

. : Float

The lower side of the camera cone.

.far : Float

The camera looks at the far end face of the cone, and its default value is 2000.

This value must be greater than the value of near plane.

. : Boolean

Read-only flag to check if a given object is of type OrthographicCamera.

. : Float

The left side of the camera cone.

.near : Float

The near end face of the visual cone of the camera. Its default value is 1.0

The valid range of its value is between 0 and far (the far end face of the camera cone).
Note that, unlike the perspective camera, 0 is a valid value for the proximal face of the orthographic camera.

. : Float

The right side of the camera cone.

. : Float

The upper side of the camera cone.

.view : Object

This value is set by setViewOffset, and its default value is null.

.zoom : number

Gets or sets the zoom factor of the camera. Its default value is 1.

3. Method

For common methods, see its base class Camera.

.setViewOffset ( fullWidth : Float, fullHeight : Float, x : Float, y : Float, width : Float, height : Float ) : undefined

fullWidth - full width setting for multi view
fullHeight - full height setting for multi view
x - horizontal offset of secondary camera
y - vertical offset of secondary camera
Width - the width of the secondary camera
Height - height of secondary camera

In larger viewing frustum Setting the offset in (viewing cone) is very useful for setting multiple windows or multiple displays. For how to use it, check out the examples in perspective amera.

.clearViewOffset () : undefined

Clear any setViewOffset sets the offset of the.

. () : undefined

Update the camera projection matrix. Must be called after any parameter has been changed.

. (meta : Object) : Object

meta -- an object that contains metadata, such as textures or images in its descendants
Convert camera to three js JSON Object/Scene format (three.js JSON object / scene format).

The mesh model whose coordinate value is not in the three-dimensional space in the three-dimensional scene will not be rendered and will be trimmed. For example, if you change the value of far parameter in the above code from 1000 to 420, you will find that part of the box cannot be displayed.

Note: the ratio of the distance between the left and right boundaries and the distance between the upper and lower boundaries should be consistent with the width height ratio of the rendering window of the canvas, otherwise the display effect of the 3D model will be stretched in unequal proportions in one direction

The parameters of the constructor OrthographicCamera (left,right,top,bottom,near,far) are essentially the encapsulation of the WebGL projection matrix. The larger the width and height, the larger the position coordinates of the vertices of the 3D model will be, and the mesh model beyond the visual area will be cut off and will not be displayed on the screen. You can also see that the parameters left and right, top and bottom are opposite to each other, The purpose of this is that the object pointed to by lookAt can be displayed in the middle of the canvas.

6, Basic principle and code implementation of perspective projection camera to orthogonal projection camera in threejs

Here, assuming that the perspective projection camera is known, in the conversion of perspective projection camera into orthogonal projection camera in threejs, the most important thing is to calculate the amount of orthogonal projection required according to the properties of the known perspective camera, that is, the left and right upper and lower far and near planes. The far and near planes are known (i.e. the far and near planes of the perspective camera can be used). This is, the left and right and upper and lower planes are calculated.

It can be simply considered that the required left and right upper and lower planes are the rectangle composed of the far plane and the camera fov, as shown in the figure below.

The calculation method is to get the depth depth from the current perspective camera to the far plane, then calculate the top of the rectangle according to the fov angle, get right according to the aspect ratio of the perspective camera, and then form a positive and negative relationship between top and bottom, right and left, and finally get the parameters required for the construction of the required orthogonal camera.

            //1. Calculate the depth distance from the perspective camera to the scene
            let target = scene.position.clone();;
            let camPos = perCamera.position.clone();
            let depth = camPos.sub(target).length();

            //2. Obtain the aspect ratio and vertical viewing angle of the perspective camera
            let aspect = perCamera.aspect;
            let fov = perCamera.fov;

            //3. Calculate the viewport rectangle of the orthogonal projection camera according to the above variables
            let top_ortho = depth  * Math.atan( (Math.PI/180)*(fov)/2);
            let right_ortho = top_ortho * aspect;
            let bottom_ortho = - top_ortho;
            let left_ortho = - right_ortho;


            //4. Finally, create an orthogonal projection camera
            let near = perCamera.near;
            let far = perCamera.far;
            orthCamera = new THREE.OrthographicCamera(
                    left_ortho , right_ortho ,
                    top_ortho , bottom_ortho ,
                    near, far
            );

7, Case realization

1. Import Three js related js files

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

    import * as THREE from 'three';

    import { OrbitControls } from './js/OrbitControls.js';
    import { RGBELoader } from './js/RGBELoader.js';
    import { GUI } from './js/lil-gui.module.min.js'

2. Initialize scene, renderer and Lights, and add 3D objects

    function initScene()
    {

        // Scene, background color
        scene = new THREE.Scene();
        scene.background = new THREE.Color( 0xcccccc );
        // This is the key, including the environment hdr map of lighting information (because there is no light added, without it, the scene is black)
        scene.environment = new RGBELoader().load( './src/venice_sunset_1k.hdr' );
        scene.environment.mapping = THREE.EquirectangularReflectionMapping;
    }

    function initRenderer()
    {
        const container = document.getElementById( 'app' );

        // Renderer 
        renderer = new THREE.WebGLRenderer( { antialias: true } );
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( window.innerWidth, window.innerHeight );
        renderer.outputEncoding = THREE.sRGBEncoding;
        renderer.toneMapping = THREE.ACESFilmicToneMapping;
        renderer.toneMappingExposure = 0.85;
        container.appendChild( renderer.domElement );
    }


    function addLights(){
        scene.add(new THREE.AmbientLight(0xffffff, 1));

        const hemiLight = new THREE.HemisphereLight(0xffffff, 0x000000, 1);
        hemiLight.position.set(0, 100, 0);
        scene.add(hemiLight);

    }


    function addObject3Ds(){
        const material1 =  new THREE.MeshStandardMaterial( {
            color: 0xff0000, metalness: 0.0, roughness: 0.5
        } );
        const mesh1 = new THREE.Mesh(new THREE.BoxGeometry(10,5,5),material1);
        mesh1.castShadow =true;
        mesh1.receiveShadow =true;
        scene.add(mesh1);

        const material2 = new THREE.MeshStandardMaterial({color:0xff00cc,metalness:0.0,roughness:0.1});
        const mesh2 = new THREE.Mesh(new THREE.BoxGeometry(5,5,5),material2);
        mesh2.position.set(0,5,2);
        scene.add(mesh2);

        const material3 = new THREE.MeshStandardMaterial({color:0xffcc00,metalness:0.1,roughness:0.1});
        const mesh3 = new THREE.Mesh(new THREE.SphereGeometry(5,30),material3);
        mesh3.position.set(0,5,-2);
        scene.add(mesh3);

    }

3. Initialize the camera camera, create the perspective camera by default, take the perspective camera as the current camera, and create the orthogonal camera as the current camera when switching the camera

    function initCamera(){
        curCamera = createPerCamera();
    }

    function createPerCamera(){

        if(perCamera == null){
            perCamera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200);
            perCamera.position.set (-10,4, 25);
        }

        return perCamera;
    }

    function createPerCameraToOrthCamera(perCamera){

        if(perCamera == null){
            console.error(' perCamera is null');
            return null;
        }

        if(orthCamera == null){
            //1. Calculate the depth distance from the perspective camera to the scene
            let target = scene.position.clone();;
            let camPos = perCamera.position.clone();
            let depth = camPos.sub(target).length();

            //2. Obtain the aspect ratio and vertical viewing angle of the perspective camera
            let aspect = perCamera.aspect;
            let fov = perCamera.fov;

            //3. Calculate the viewport rectangle of the orthogonal projection camera according to the above variables
            let top_ortho = depth  * Math.atan( (Math.PI/180)*(fov)/2);
            let right_ortho = top_ortho * aspect;
            let bottom_ortho = - top_ortho;
            let left_ortho = - right_ortho;


            //4. Finally, create an orthogonal projection camera
            let near = perCamera.near;
            let far = perCamera.far;
            orthCamera = new THREE.OrthographicCamera(
                    left_ortho , right_ortho ,
                    top_ortho , bottom_ortho ,
                    near, far
            );
        }
        return orthCamera;
    }

4. Create orbitControls camera controller

Note: only the perspective camera can roam and observe, and the orthogonal camera cannot roam and observe

    function initOrbitControls(){
        const  controls = new OrbitControls(curCamera, renderer.domElement);
        controls.minDistance = 5;
        controls.maxDistance = 50;
        controls.enablePan = false; // Prohibit move operation
    }

5. Add GUI operation panel to switch perspective and orthogonal cameras

    function addGUI(){
        const param = {
            "Perspective to normal intersection":orthCameraView,
            "Perspective to orthographic top view":orthCameraLookDownView,
            "Orthogonal perspective":orthCameraBackPerCamera,

        }

        const gui = new GUI();
        gui.add(param,"Perspective to normal intersection");
        gui.add(param,"Perspective to orthographic top view");
        gui.add(param,"Orthogonal perspective");
    }

6. Effect preview

 

7. Complete code

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
    <title>PersCameraToOrthCamera</title>
    <style type="text/css">
        * {
            margin: 0px;
            padding: 0px;
        }
    </style>
</head>
<body>
<div id="app">

</div>

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

    import * as THREE from 'three';

    import { OrbitControls } from './js/OrbitControls.js';
    import { RGBELoader } from './js/RGBELoader.js';
    import { GUI } from './js/lil-gui.module.min.js'

    // Current camera, perspective camera, orthographic camera
    let curCamera,perCamera,orthCamera
    let scene, renderer;

    init();
    animate();

    function init(){
        initScene();
        initRenderer();
        initCamera();
        addLights();
        initOrbitControls();
        addObject3Ds();
        addGUI();
    }

    function initScene()
    {

        // Scene, background color
        scene = new THREE.Scene();
        scene.background = new THREE.Color( 0xcccccc );
        // This is the key, including the environment hdr map of lighting information (because there is no light added, without it, the scene is black)
        scene.environment = new RGBELoader().load( './src/venice_sunset_1k.hdr' );
        scene.environment.mapping = THREE.EquirectangularReflectionMapping;
    }

    function initRenderer()
    {
        const container = document.getElementById( 'app' );

        // Renderer 
        renderer = new THREE.WebGLRenderer( { antialias: true } );
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( window.innerWidth, window.innerHeight );
        renderer.outputEncoding = THREE.sRGBEncoding;
        renderer.toneMapping = THREE.ACESFilmicToneMapping;
        renderer.toneMappingExposure = 0.85;
        container.appendChild( renderer.domElement );
    }

    function initCamera(){
        curCamera = createPerCamera();
    }

    function createPerCamera(){

        if(perCamera == null){
            perCamera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200);
            perCamera.position.set (-10,4, 25);
        }

        return perCamera;
    }

    function createPerCameraToOrthCamera(perCamera){

        if(perCamera == null){
            console.error(' perCamera is null');
            return null;
        }

        if(orthCamera == null){
            //1. Calculate the depth distance from the perspective camera to the scene
            let target = scene.position.clone();;
            let camPos = perCamera.position.clone();
            let depth = camPos.sub(target).length();

            //2. Obtain the aspect ratio and vertical viewing angle of the perspective camera
            let aspect = perCamera.aspect;
            let fov = perCamera.fov;

            //3. Calculate the viewport rectangle of the orthogonal projection camera according to the above variables
            let top_ortho = depth  * Math.atan( (Math.PI/180)*(fov)/2);
            let right_ortho = top_ortho * aspect;
            let bottom_ortho = - top_ortho;
            let left_ortho = - right_ortho;


            //4. Finally, create an orthogonal projection camera
            let near = perCamera.near;
            let far = perCamera.far;
            orthCamera = new THREE.OrthographicCamera(
                    left_ortho , right_ortho ,
                    top_ortho , bottom_ortho ,
                    near, far
            );
        }
        return orthCamera;
    }

    function addLights(){
        scene.add(new THREE.AmbientLight(0xffffff, 1));

        const hemiLight = new THREE.HemisphereLight(0xffffff, 0x000000, 1);
        hemiLight.position.set(0, 100, 0);
        scene.add(hemiLight);

    }

    function initOrbitControls(){
        const  controls = new OrbitControls(curCamera, renderer.domElement);
        controls.minDistance = 5;
        controls.maxDistance = 50;
        controls.enablePan = false; // Prohibit move operation
    }

    function addObject3Ds(){
        const material1 =  new THREE.MeshStandardMaterial( {
            color: 0xff0000, metalness: 0.0, roughness: 0.5
        } );
        const mesh1 = new THREE.Mesh(new THREE.BoxGeometry(10,5,5),material1);
        mesh1.castShadow =true;
        mesh1.receiveShadow =true;
        scene.add(mesh1);

        const material2 = new THREE.MeshStandardMaterial({color:0xff00cc,metalness:0.0,roughness:0.1});
        const mesh2 = new THREE.Mesh(new THREE.BoxGeometry(5,5,5),material2);
        mesh2.position.set(0,5,2);
        scene.add(mesh2);

        const material3 = new THREE.MeshStandardMaterial({color:0xffcc00,metalness:0.1,roughness:0.1});
        const mesh3 = new THREE.Mesh(new THREE.SphereGeometry(5,30),material3);
        mesh3.position.set(0,5,-2);
        scene.add(mesh3);

    }

    function addGUI(){
        const param = {
            "Perspective to normal intersection":orthCameraView,
            "Perspective to orthographic top view":orthCameraLookDownView,
            "Orthogonal perspective":orthCameraBackPerCamera,

        }

        const gui = new GUI();
        gui.add(param,"Perspective to normal intersection");
        gui.add(param,"Perspective to orthographic top view");
        gui.add(param,"Orthogonal perspective");
    }

    function orthCameraView(){
        const  tmpCamera = createPerCameraToOrthCamera(perCamera);
        tmpCamera.position.set(perCamera.position.x,perCamera.position.y,perCamera.position.z);
        tmpCamera.rotation.set(perCamera.rotation.x,perCamera.rotation.y,perCamera.rotation.z);

        curCamera = tmpCamera;
    }

    function orthCameraLookDownView(){
        //1. Calculate the depth distance depth from the perspective camera to the scene as the height of the top view
        let target = scene.position.clone();;
        let camPos = perCamera.position.clone();
        let depth = camPos.sub(target).length();
        const  tmpCamera = createPerCameraToOrthCamera(perCamera);
        tmpCamera.position.set(0,depth,0);
        tmpCamera.rotation.set(- Math.PI/2, 0, 0);

        curCamera = tmpCamera;
    }

    function orthCameraBackPerCamera(){
        curCamera = createPerCamera();
    }

    function animate(){

        requestAnimationFrame(animate);

        render();
    }

    function render(){
        renderer.render(scene, curCamera);
    }


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

8, Case code download

csdn audit in progress

Tags: Javascript webgl ThreeJS

Posted by nayone on Tue, 03 May 2022 08:08:13 +0300