WebGL (Web Graphics Library) is a 3D drawing protocol. This drawing technology standard allows JavaScript to be combined with OpenGL ES 2.0, By adding a JavaScript binding to OpenGL ES 2.0, WebGL can provide hardware 3D accelerated rendering for HTML5 Canvas, so that Web developers can use the system graphics card to display 3D scenes and models more smoothly in the browser. You can also create complex navigation and data visualizations. Three. Js is an open source mainstream 3D drawing JS engine that simplifies WebGL programming just as jQuery simplifies HTML DOM manipulation.

I. Introduction of Three. Js coordinates

There are mainly two kinds of geometric transformation in three-dimensional space, one is the transformation of position, position transformation and two-dimensional space is the same, coordinate value can be increased or decreased. The other is rotation transformation. Rotation in two-dimensional space can be regarded as rotation around a point, which can only be rotated based on a point on the plane as the center of the circle, while rotation in three-dimensional space is rotation around a line with multiple directions. Three. Js is a self-sustaining rotation matrix, Euler Angle and quaternion.

As for the coordinate system of three-dimensional transformation space, my understanding is that it can be divided into world coordinate system and screen coordinate system. The world coordinate system is the space coordinate system, and the screen coordinate system is the coordinate system we see on the plane. In fact, there should be a transitional coordinate system in the middle, namely the standard equipment coordinate system [-1,1], which can be converted through the existing data processing function. This is just my personal understanding, and there may be deviations. Here’s how to convert world and screen coordinates.

Screen coordinates to world coordinates:

The screen coordinates are starting from the top left corner, so here’s the panorama and the device coordinates are starting from the center of the canvas, and we need to do some processing to get the camera coordinates.

    functionconvertTo3DCoordinate(clientX,clientY){ var mv = new THREE.Vector3( (clientX / window.innerWidth) * 2 - 1, -(clientY/window.innerheight) * 2 + 1, 0.5); Mv.unproject (this.camera); mv.unproject(this.camera); mv.unproject(this.camera); This function converts the device coordinates to homogeneous coordinates and then multiplies them by the inverse of the camera's MP matrix. If you're interested, you can look at linear algebra.return mv;
    }
Copy the code

World coordinates to screen coordinates:

    function convertTo2DCoordinateVar worldVector = new THREE.Vector3(boxmesh.position. x, boxmesh.position. y, boxMesh.position.z ); var standardVector = worldVector.project(camera); Var a = window.innerWidth / 2; var b = window.innerHeight / 2; var param = {} param.x = Math.round(standardVector.x * a + a); Param. y = math.round (-standardvector. Y * b + b); // Switch from standard device coordinates to screen coordinatesreturnParam} where camera projection and backprojection source:function () {
        var matrix = new Matrix4();
            return function project( camera ) {
            matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
            return this.applyMatrix4( matrix );
        };
    }(),
    unproject: function () {
        var matrix = new Matrix4();
        return function unproject( camera ) {
            matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
            returnthis.applyMatrix4( matrix ); }; } (),Copy the code

Latitude and longitude are transformed into spatial coordinates:

// Latitude and longitude sphere radiusfunctionLgltToxyz (longitude, latitude, the radius) {/ / return perspective into radian after the value of the var lg = degToRad (longitude), lt = degToRad (latitude); var y = radius * Math.sin(lt); var temp = radius * Math.cos(lt); var x = temp * Math.sin(lg); var z = temp * Math.cos(lg);return {x:x , y:y ,z:z}
    }
Copy the code

Two, the principle of panorama display

A panorama is a wide-angle map based on isometric cylindrical projection.

Isometric cylindrical projection, also known as square projection, assumes that the sphere and cylinder face are tangent to the equator, and the equator is a line without deformation. The grid of longitude and latitude lines, like the normal cylindrical projection, is projected into two groups of parallel lines perpendicular to each other. Its characteristics are: keep the warp distance and weft distance equal, the warp and weft lines into a square grid; No length deformation along the warp direction; The deformation lines such as Angle and area are parallel to the latitude lines, and the deformation value increases gradually from the equator to higher latitude. The projection is suitable for low latitude mapping.

To put it plainly, all the points on a sphere are projected onto the side of a cylinder. The side expansion of the cylinder contains all the pixels on the sphere, so the length ratio of a standard panorama is 2:1.

Now that we understand the formation process of the panorama, what we need to do is to show the panorama. How to do, is to perform the reverse operation of the cylindrical side projection, and reverse the process! Therefore, we can use WebGL to draw a sphere, and then paste the panorama directly on the sphere. Of course, we need to carry out the mapping process.

Texturing process:

// Create a new sphere (radius of sphere/number of segments horizontally/and number of segments vertically) // Geometry = new THREE.SphereGeometry(500, 100, 100); // Scale -1 along the X-axis so that the sphere faces inward (since we will be viewing from inside the sphere). geometry.scale( - 1, 1, 1 ); Var Material = new three.meshBasicMaterial ({// set texture map: new three.textureLoader ().load())'1.jpg')}); // Combine geometry with material. mesh = new THREE.Mesh( geometry, material ); // Add scene.add(mesh) here and set the camera as the center point of the sphere var camera;function initCamera() {
            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
            camera.target = new THREE.Vector3( 0, 100, 0 );
        }
Copy the code

Basically we are ready to display the panorama, and we can optimize it because the number of vertices and faces in the spherical model is very large, and the more these elements there are, the more browser resources will be consumed. We can create a cube, divide a panorama into six sides of the cube, and attach it to the cube. If the cube is big enough, we will not feel its edge, thus avoiding the overlap of pixels at the two poles. The code is as follows:

    letloader = new THREE.TextureLoader(); // Set texture map // panorama decomposition const imgList = ['top.png'.'right.png'.'bottom.png'.'left.png'.'front.png'.'behind.png']; Promise. All (imgList. Map ()function(val) {// Load the image, create a new material and pass it to the next step.return new Promise(function (resolve, reject) {
            loader.load(val, functionResolve (new THREE.MeshBasicMaterial({map: texture, side: THREE.BackSide})); }); }); })).then(function{// Create cube geometry = new THREE.BoxGeometry(200, 200, 200); Cube = new THREE.Mesh(Geometry, new THREE.MeshFaceMaterial(materials) // It can paste pictures on each surface of the object); scene.add(cube); animate(); });Copy the code

And then finally with the WebGLRenderer.

Introduction to Photo-sphere-viewer

Now that I’ve introduced the background of the panorama, I’m going to use the photo-sphere-viewer (view.js). Three. Js can be understood as JQ, and view.js is the plug-in of JQ.

Resources needed: View. Min. CSS, three js, D.m. In js, uEvent. Js, doT. Js, CanvasRenderer. Js, Projector, js, DeviceOrientationControls. Js, the min js, Zepto_1. 1.3. Js.

Parameter Description:

  • panorama(Mandatory) Path of the panorama.
  • container(Mandatory) Container for storing the panorama.
  • autoload(Default: true) True means automatic loading of the panorama, false means late loading of the panorama (via load method).
  • usexmpdata(Default: true) Whether photo Sphere Viewer must read XMP data, false: no.
  • cors_anonymous(The default value is true.) True indicates that users cannot be obtained through cookies
  • pano_size(Default value: null) Panorama size, whether to crop.
  • default_positionDefines the default position, the first dot the user sees, for example: {long: math.pi, lat: math.pi/2}.
  • min_fov(Default: 30) The smallest area to observe, in units of degrees, is between 1 and 179.
  • max_fov(Default: 90) The maximum area to observe, in units of degrees, is between 1 and 179.
  • allow_user_interactionsFalse disables user interaction with panorama (the navigation bar is unavailable).
  • allow_scroll_to_zoom(Default: true) If set to false, the user cannot zoom the image by scrolling.
  • tilt_up_max(Default value: math.pi/2) Maximum upward tilt Angle, in radians.
  • tilt_down_max(Default: math.pi/2) Maximum downward tilt Angle, in radians.
  • min_longitude(Default: 0) The minimum longitude that can be displayed.
  • max_longitude(Default: 2PI) Maximum dimension that can be displayed.
  • zoome_levelDefault zoom level. The value is between 0 and 100.
  • long_offset(PI/360 by default) The longitude value that each pixel passes through when mouse/touch moves.
  • lat_offset(PI/180 by default) The latitude value that each pixel passes through when mouse/touch moves.
  • time_anim(Default: 2000) The panorama is automatically animated after time_anim milliseconds. (Set to false to disable it)
  • reverse_anim(Default: true) Whether the direction of the animation reverses when the horizontal direction reaches the maximum/minimum longitude (just can’t see the full circle).
  • anim_speed(Default: 2rpm) Animation speed per second/minute.
  • vertical_anim_speed(Default: 2rpm) How fast the vertical direction animation is per second/minute.
  • vertical_anim_target(Default: 0) Dimension when rotated automatically. Default: equator.
  • navbar(Default false) Displays the navigation bar.
  • navbar_style(Default: false) Navigation bar style. Valid attributes:
  • backgroundColor: navigation bar background color (default rGBA (61, 61, 61, 0.5));
  • buttonsColor: button foreground color (default rGBA (255, 255, 255, 0.7));
  • buttonBackgroundColor: Background color for button activation (default rGBA (255, 255, 255, 0.1));
  • buttonsHeight: button height, px (default: 20);
  • autorotateThickness: Automatically rotates the layers of the image (default 1);
  • zoomRangeWidth: Scale the width of the cursor in px (default 50);
  • zoomRangeThickness: Scale the cursor layer (default: 1);
  • zoomRangeDisk: Zoom cursor magnification in px (default: 7);
  • fullscreenRatio: Ratio of full-screen ICONS (default: 4/3);
  • fullscreenThickneee: Layer of the full-screen image, px (default: 2)
  • loading_msg(The default value is Loading…) Load information.
  • loading_img(Default value: null) Loading Path of an image.
  • loading_html(Default: null) HTML loader (element or string added to container).
  • sizeThe final size of the panorama container, for example {width: 500, height: 300}.
  • onreadyCallback function after the panorama is ready and the first image is displayed.

Method introduction:

  • AddAction () : Adds an event (the plug-in does not provide a method for executing the event, but seems to provide it for internal use).
  • FitToContainer () : Adjusts the panorama container size to the specified size.
  • GetPosition () : Gets the latitude and longitude of the coordinates.
  • GetPositionInDegrees () : Gets the latitude and longitude degree.
  • GetZoomLevel () : Gets the zoom level.
  • Load () : Loads the panorama ().
  • MoveTo (longitude, latitude) : moveTo a certain point based on longitude and latitude.
  • Rotate (dlong, DLAT) : Moves to a point based on the longitude and latitude.
  • ToggleAutorotate () : Whether to enable automatic panorama rotation.
  • ToggleDeviceOrientation () : Whether to enable gravity sensing orientation control.
  • ToggleFullscreen () : Whether to enable full screen panorama.
  • ToggleStereo () : Whether to enable stereo effects (available on WebVR).
  • Zoom (level) : Sets the zoom level.
  • ZoomIn () : zoomIn.
  • ZoomOut () : Zooming out.

Use:

Start by creating a div for the render area

    <div id="photosphere"></div> var PSV = new PhotoSphereViewer({container:'photosphere',
                    panorama: panos[0].url
    })
Copy the code

Create a tag

When displaying panoramas, we generally encounter the following scenarios: a large area is displayed, which contains many detailed locations that need to be introduced, so we can create markers to display relevant information.

Use:

// Define self-executing markers in new: (function() {
               returncommon(); // Create marker wrapper}())function common() {
           var a = [];
           for(var i = 0; i < Math.PI * 2; i += Math.PI / 4) {
              for(var j = -Math.PI / 2 + Math.PI / 4; j < Math.PI / 2; j += Math.PI / 4) {
                   a.push({
                       id: The '#' + a.length,
                       tooltip: 'Click on me.'// Latitude and longitude can be replaced by x and y to represent the pixel coordinates longitude: I, image:'.. /img/pin2.png', // Image marker background width: 32, // width height: 32, anchor:'bottom center', data: {// Set data deletable:true}}); }}returnA //marker accepts arrays so it inverts as arrays}Copy the code

Custom SVG:

    a.push({
        id: 'circle', //id Mandatory tooltip:'A circle of radius 30', circle: 20, // radius svgStyle: {fill:'rgba(255,255,0,0.3)',
            stroke: 'yellow',
            strokeWidth: '2px'
        },
        //                      longitude: 0.14842681258549928,
        //                      latitude: -0.8678522571819425,
        x: 8395,
        y: 6827,
        anchor: 'center right'}); Custom HTML a.ush ({id:'text', longitude: -0.5, latitude: 0.28, HTML:♥ love you love you ♥,  
        anchor: 'bottom right', style: {// define the style maxWidth:'320px',
        color: 'red',
        fontSize: '20px',
        fontFamily: 'Helvetica, sans-serif',
        textAlign: 'center'}, tooltip: {// content:'An HTML marker',
        position: 'right'}});Copy the code

5. Automatic playback of scenery map with background music

    <audio src=".. /libs/source.mp3" id="audios" style="position: absolute; left: 99999999999999px; top: 999999999px;" loop="true"></audio>

    PSV.on('ready'.function(){// ready psv.toggleAutorotate (); // auto-play $('#audios')[0].play(); // Play background music $('.scene').hide(); //loading: $('.playBtn').show(); // Music player button});Copy the code

6. Picture caching and scene switching

Cache_texture :number: cache_texture:number: cache_texture:number :number

Click marker to switch the scene:

    PSV.on('select-marker'.function(marker) {// Listen for the marker click event. Psv. setPanorama(url, {longitude: 3.848, latitude: -0.244},true)
                            .then(function() {$('.back').show();
                                $('.scene').hide();
                                PSV.setCaption('Scene Description');
                            });
    })

    setPanorama: image address, initial latitude and longitude for the next scene, transition default (false) you can also set navbar: ['autorotate'.'zoom'.'download'.'markers'.'spacer-1',
              {
                  title: 'Change image',
                  className: 'custom-button',
                  content: 'am I',
                  onClick: (function() {// custom logic}())}]Copy the code

Content from the definition navigation.

Add that Panorama can accept either an array (six urls) ora single image URL. If a cut in _loadEquirectangularTexture this function in the internal, if is an array, direct rendering, improves performance.

Loading is carried out during scene switching. Since caching is enabled, a variable (switch idea) needs to be set to control the display times of loading box. For example, when scene 1 enters Scene 2 for the first time, loading is required. But jump from scenario 2 back to scenario 1 because caching is loaded only once. Loading is made in SVG, no code is posted here.

Source code download address:

Download.csdn.net/download/su…