Welcome to Github address star!

In the previous project, WE used the problem of 3D model demonstration, and sorted out the previous learning summary and the pits encountered. The 3D frameworks include the old Three. Js engine and Microsoft’s Babylon. Js

Three.js Basic concepts

Mainly from the Three.js Development Guide or the online threejs tutorial

Three basic concepts: scene, camera, and renderer.

  • Sence Scene: The scene is a carrier, container, and everything runs in this container (which holds all the rendered objects and the lights used)

  • A camera camera defines the area of view that can be viewed as our eyes and produces snapshots. PerspectiveCamera is the most commonly used camera. For VR scenes), CubeCamera CubeCamera CubeCamera CubeCamera CubeCamera CubeCamera CubeCamera CubeCamera CubeCamera CubeCamera CubeCamera CubeCamera CubeCamera CubeCamera CubeCamera There are two main types of cameras: orthographic cameras and perspective cameras. With orthographic cameras, all blocks render the same size. The distance between the object and the camera does not affect the render, whereas the perspective camera is close to the real world and looks at objects in different ways

  • PerspectiveCamera – Mimics the view of the human eye through PerspectiveCamera

  • The renderer is responsible for how to render the image. Is it to use WegGL or Canvas, similar to render in React, to produce the actual page effect

Some other concepts

  • Mesh: With the scene and camera, you can see the objects in the 3D scene. The most common objects in the scene are called Mesh. The mesh consists of two parts: the geometry and the material
  • Materials, Textures: The surface properties of objects can be simple colors, or complex situations like reflection/transmission/refraction, or texture patterns. Like the texture on the outside of the box.
  • Geometry: Threejs uses Geometry to define the Geometry of objects. In fact, the core of Geometry is a set of points. The reason why there are so many of them is to make it easier to create a set of points with various shapes
  • Lights: Components. If there is no light source, it is impossible to see any render resultsLighting effects and Phong lighting models. Some common light sources:
    1. An AmbientLight is a base light that provides a base brightness for all objects in the scene.
    2. DirectionalLight: Similar to the sunlight, the light sources emitted are parallel
    3. Only the HemisphereLight side of the ball is emitting light.
    4. PointLight: A point that emits a light source around it, usually used in light bulbs.
    5. SpotLight: A cone of light
  • Note: Not every light source can produce a Shadow: DirectionalLight, PointLight, and SpotLight can generate shadows. In addition, if you want to enable shadows for a model, the model is made up of multiple Mesh elements. You also need to walk through all the child meshs under the parent Mesh to enable castShadow and receive receiveShadow for them.
  • Loaders: imported model files that are used to parse. Common examples are OBJLoader (loading.obj files), JSONLoader, and MTLLoader
Var scene = new three.scene (); var scene = new three.scene (); // The camera determines which Angle of the scene will be displayed. Cameras are like people's eyes. People can see different scenery when they are standing in different positions, looking up or looking down. Var camera = new THREE. PerspectiveCamera (75, window. InnerWidth/window. InnerHeight, 0.1, 1000); Var renderer = new three.webglrenderer (); var renderer = new three.webglrenderer (); Renderer.setsize (window.innerWidth, window.innerheight); / / set the renderer for the size of the window width, which is the width of the content of the document. The body. The appendChild (the renderer. DomElement accordingly); // Render loopfunction animate() { render(); // Call requestAnimationFrame, passing a callback argument, and then callback will be called on the next frame. requestAnimationFrame( animate ); } Animation scheme: 1: change the camerafunction animation() { //renderer.clear(); camera.position.x =camera.position.x +1; renderer.render(scene, camera); requestAnimationFrame(animation); } // camera.position.x =camera.position.x +1; // Move the camera 1 unit along the x axis. The camera moves to the right, and the object in the camera moves to the left. // Call requestAnimationFrame(animation), which in turn triggers the animation() function on the next frame, changing the camera's position so that the object appears to be moving. Render (scene, camera); render(scene, camera); render(camera, camera); Change the position of the object itself - a mesh is an object that has a position property, which is a THREE.Vector3 variable, so to move it to the left, you just decrease the value of x. We're subtracting 1 here. / / / rendering authenticity - the Light source used THREE (http://www.hewebgl.com/article/getarticle/60). The Light (hex) it has a parameter hex, accept a hex color value. Var redLight = new three.light (0xFF0000); Var redLight = new three.light (0xFF0000); / / / liberal arts - a 3 d object skin: (http://www.hewebgl.com/article/getarticle/68) Texture class consists of THREE. The Texture, said its constructor is as follows:  THREE.Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format,type, anisotropy )
Copy the code

The following is the basic concept of three.js

Then give a simple example

// Introduce the three.js library <script SRC ="https://unpkg.com/three"></script>
function init() {// get the width and height of the browser window, Var width = window.innerWidth var height = window.innerheight // Var scene = new three.scene () // Var camera = new three.perspectivecamera (45, width/height, 0.1, 800) // Set the camera position, X = 10 Camera.position. y = 10 Camera.position. z = 30 Camera.lookat (scene.position) // Create one WebGL renderer, three. js also provides < Canvas >, < SVG >, CSS3D renderer. Var renderer = new three.webglrenderer (); var renderer = new three.webglrenderer (); // If you want to use body as the background, you can leave clearColor unset and then set alpha when creating the renderer:trueNew three. WebGLRenderer({alpha:true}) renderer.setClearColor(0xffffff) renderer.setSize(width, Var cubeGeometry = new THREE.BoxGeometry(4, 4, 4) Var cubeMaterial = new three.meshBasicMaterial ({color: 0xff0000}) // Create a mesh: Var cube = new three.mesh (cubeGeometry, X = 0 cube.position.y = -2 cube.position.z = 0 scene.add(cube) // Will the output of the renderer (this is a canvas element) is inserted into the document in the body. The body. The appendChild (the renderer. DomElement accordingly) / / rendering, Renderer.render (scene, camera)} init()Copy the code

Click on the online example

The practical application

Three Performance monitor stats

It is used to display performance frames

  • FPS: The number of frames in the last second, the bigger the smoother

  • MS: The time (MS) required to render a frame, the lower the better

  • MB: indicates the memory occupied

  • CUSTOM: Customizes the panel

var stats = new Stats()
stats.showPanel(1)
document.body.appendChild(stats.dom)
function animate() {
  requestAnimationFrame(animate)
}
requestAnimationFrame(animate)
Copy the code

Here are some examples that don’t need to import

Can the github.com/mrdoob/thre… Download the file and look at the \three.js-master\examples to get familiar with the code

Import 3D model examples

Import model type description

To import model files, we need to use the corresponding loader, which is usually exported by 3D software. In this project, we mainly use OBJ and MTL types. OBJ defines geometry and MTL defines materials

// When the MTL references DDS images, you need to import DDSLoader files. // Here the SRC path depends on the actual development <script SRC ="js/loaders/DDSLoader.js"></script>
<script src="js/loaders/MTLLoader.js"></script>
<script src="js/loaders/OBJLoader.js"></script> THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() ); var mtlLoader = new THREE.MTLLoader(); Mtlloader.setpath (mtlloader.setpath (mtlloader.setpath))'obj/male02/' );
    mtlLoader.load( 'male02_dds.mtl', / / resource loading after a successful execution of function / / @ params materials. THREE MTLLoader. MaterialCreatorfunction( materials ) {
    materials.preload();
    var objLoader = new THREE.OBJLoader();
        objLoader.setMaterials( materials );
        objLoader.setPath( 'obj/male02/' );
        objLoader.load( 'male02.obj'.function ( object ) {
    		object.position.y = - 95;
    		scene.add( object );
    	});
});
Copy the code

You can view specific examples

There are problems with importing OBJ. When the model is exported to OBJ format, the file is too large (which causes serious performance problems when placed on the server), and there is an additional import to MTL, otherwise only the geometric model will be displayed
GlTF model format

Obj is a static model, which does not support animation data storage and cannot use the animation of the model, and it is large. GlTF is a 3D model file format developed by Khronos Group, which is characterized by reducing the size of 3D model files to the maximum extent. It improves the efficiency of transferring, loading, and parsing 3D model files, and is extensible and interoperable.

GLTF contains description information about the node hierarchy, cameras, meshes, materials, and animations in the scene

The glTF format used in three. js requires the addition of the GLtFloader.js loader.

var gltfLoader = new THREE.gltfLoader()
gltfLoader.load('./assets/box.gltf'.function(sence) {var object = scene.gltf // Model object scene.add(object) // Add model to scene})Copy the code

GlTF models can be animated using Blender. After exporting them, use GLTFLoader to load them into Three.js to get an animations array. The animations array contains each animation Action for the model.

For better network performance, the Draco tool can also be used for compression, and compression is recommended only if there are many model files (because the format changes after compression, other parsing tools need to be introduced).

animation

The above mentioned animation, about animation, you can directly Tween animation library, which is used in the research provided by Xu’s colleague. RequestAnimationFrame () is usually used in three.js animations. You can call this method whenever you need to update the screen. The callback function is executed before the browser redraws next. The number of callbacks is typically 60 per second.

It is recommended to use GSAP to realize the animation such as fading in and out, scaling, displacement and rotation of the model.

letTween = new TimelineMax() tween. To (box. Scale, 1, {// scale from 1 to 2, take 1 second x: 2, y: 2, z: 2, ease: Power0. EaseInOut, // Speed curve onStart:function() {// listen for animation start}, onUpdate:function() {// listen to the animation process}, onComplete:function() {// listen to the end of animation}}).to(box. Position, 1, {// After the end of zoom, shift x to 10, take 1 second x: 10, y: 0, z: 0})Copy the code

Control orbitcontrols

Scene controller, OrbitControls is a method for debugging the Camera. After instantiation, you can drag the mouse to rotate the Angle of the Camera lens. The mouse wheel can control the distance of the Camera lens.

// Import file <script SRC ="js/OrbitControls.js"></script> // Scene controller initializationfunction initControls() {
      controls = new THREE.OrbitControls(camera, renderer.domElement);
      controls.enabled = true; // Whether mouse control is available // Whether automatic rotation controls.autoRotate =true; Controls. AutoRotateSpeed = 0.05; // Whether to rotate, rotate speed (left mouse button) controls.enableRotate =true; Controls. RotateSpeed = 0.3; //controls.target = new THREE.Vector(); Controls. MinDistance = 10; // Controls. MinDistance = 10; controls.maxDistance = 40; // Controls. MinPolarAngle = math.pi / 4; // 45 ° Angle controls.maxPolarAngle = math.pi / 2.4; // The default sliding size is 0.25 controls.enableDamping =true; Controls. DampingFactor = 0.25; // The default move speed is 7px controls.enablePan =true; Controls. The panSpeed = 0.5; //controls.screenSpacePanning =true; // Wheel zoom controls.enableZoom =true; Controls. ZoomSpeed = 1.5; Horizontal Angle limit / / / / controls minAzimuthAngle = - Math. PI / 4; //controls.maxAzimuthAngle = Math.PI/4; }Copy the code

Click on the interaction

In 3D models, mouse clicks are important interactions. For three. js, it has no hierarchical relationship similar to DOM and is in a THREE-DIMENSIONAL environment, so we need to judge whether an object is selected by the following methods.

functionVar vector = new three.vector3 ((event.clientx /) Window.innerwidth) * 2-1, -(event.clienty/window.innerheight) * 2 + 1, 0.5); // The vector.unproject method converts the click position on the screen to the coordinates in the three.js scene. Vector = vector.unproject(camera); Var Raycaster = new three.raycaster (camera. Position, vector.sub(camera.position).normalize()); / / using raycaster intersectObjects method to determine the specified object, which is the light to / / to show different color var intersects = raycaster intersectObjects ([sphere, cylinder, cube]);if(intersects.length > 0) { console.log(intersects[0]); / / click after change the transparency intersects [0]. Object. Material. Transparent =true; Intersects [0]. Object. Material. The opacity = 0.1; <! -... This is where you can implement the interaction you need -->}}Copy the code

React

NPM i-s three <! --GisThree.js--> <! There is still a lot of room for optimization. --> import React, { Component, Fragment } from'react';
import './GisThree.less';
import OBJLoader from './threejsLibs/OBJLoader';
import Orbitcontrols from './threejsLibs/OrbitControls';
import MTLLoader from './threejsLibs/MTLLoader_module';
import { Icon } from 'antd';

import exhibitObj from './modal/exhibit2.obj';
import exhibitMtl from './modal/exhibit2.mtl';

let THREE = require('three'); Orbitcontrols(THREE); OBJLoader(THREE); MTLLoader(THREE); Const objectArrName = [const objectArrName = ["House of 1101"."House of 1150"."House of 600"."House of 70"."House of 45"."House of 362"."House of 363"."House of 364"."House of 500" ];

class GisThree extends Component {

  constructor( props ) {
    super(props);
    this.state = {
      isModel: false,
      currentName: 'No name yet',
      clientX: 0,
      clientY: 0
    };
    this.threeRef = React.createRef();
  }

  componentDidMount() { const width = window.innerWidth; const height = window.innerHeight; // Todo to initialize the scene const scene = new three.scene (); Const Camera = new THREE.PerspectiveCamera(60, width/height, 0.1, 80); camera.position.set(0, 25, 25); camera.lookAt(new THREE.Vector3(0, 0, 0)); // Todo const ambLight = new three.ambientlight (0x404040, 0.5); Const pointLight = new THREE.PointLight(0x404040, 0.8); Const directionalLight = new three.directionAllight (0xffFFFF, 0.5); pointLight.position.set(100, 10, 0); pointLight.receiveShadow =true;
    scene.add(ambLight);
    scene.add(pointLight);
    scene.add(directionalLight);

    //todo  renderer
    const renderer = new THREE.WebGLRenderer({
      antialias: true}); renderer.setSize(width, height - 10); //renderer.setClearColor(0xb9d3ff,1); The renderer. SetClearColor (0 x000000, 1.0); // Todo loads the model modellet mtlLoader = new THREE.MTLLoader();
    mtlLoader.load(exhibitMtl,
      function ( materials ) {
        console.log('sdj exhibit.obj', materials)
        materials.preload();
        let objLoader = new THREE.OBJLoader();
        objLoader.setMaterials(materials);
        objLoader.load(exhibitObj, function ( object ) {
          console.log('sdj exhibit.obj')
          console.log('sdj exhibit.obj object', object);

          for ( let i = 0; i < object.children.length; i++ ) {
            let material = object.children[ i ].material;
            let meshObj = new THREE.Mesh(object.children[ i ].geometry, material);
            meshObj.receiveShadow = true;
            meshObj.castShadow = true; MeshObj. Scale. The set (0.02, 0.02, 0.02); meshObj.name ="Home"+ i; meshObj.position.x = 0; meshObj.position.y = 0; meshObj.position.z = -20; scene.add(meshObj); }}); }); Const Controls = new three. OrbitControls(Camera, renderer. DomElement); controls.enabled =true; // Whether mouse control is available // Whether automatic rotation controls.autoRotate =true; Controls. AutoRotateSpeed = 0.05; // Whether to rotate, rotate speed (left mouse button) controls.enableRotate =true; Controls. RotateSpeed = 0.3; //controls.target = new THREE.Vector(); Controls. MinDistance = 10; // Controls. MinDistance = 10; controls.maxDistance = 40; // Controls. MinPolarAngle = math.pi / 4; // 45 ° Angle controls.maxPolarAngle = math.pi / 2.4; // The default sliding size is 0.25 controls.enableDamping =true; Controls. DampingFactor = 0.25; // The default move speed is 7px controls.enablePan =true; Controls. The panSpeed = 0.5; //controls.screenSpacePanning =true; // Wheel zoom controls.enableZoom =true; Controls. ZoomSpeed = 1.5; Horizontal Angle limit / / / / controls minAzimuthAngle = - Math. PI / 4; //controls.maxAzimuthAngle = Math.PI/4; // Todo is bound to the class this.scene = scene; this.camera = camera; this.renderer = renderer; this.controls = controls; // Mousein and mouseout events highlight the selected model this.currentobjectColor = null; This.currentobject = null; / / mouse moving model initialization / / scene / / loaded into the dom elements on this. ThreeRef. Current. The appendChild (enclosing the renderer. DomElement accordingly) enclosing the start (); window.addEventListener('resize',this.resizeFunc1 ,false);
    window.addEventListener('resize',this.resizeFunc2 ,false);
  }

  componentWillUnmount() {
    this.stop();
    this.threeRef.current.removeChild(this.renderer.domElement);
    window.removeEventListener('resize',this.resizeFunc1 ,false);
    window.removeEventListener('resize',this.resizeFunc2 ,false); } // Initialize start = () => {if(! This.frameid){this.frameId = requestAnimationFrame(this.animate)}} stop = () => { cancelAnimationFrame(this.frameId); } animate = () => {this.controls.update(); this.renderScene(); this.frameId = requestAnimationFrame(this.animate); } renderScene = () => { this.renderer.render(this.scene, this.camera); }} changeModel = (e) => {e.toppropagation (); this.setState({ isModel: ! this.state.isModel }) } closeModel = ( e ) => { e.stopPropagation();if(this.controls && ! this.controls.autoRotate){ this.controls.autoRotate =true;
    }
    this.setState({
      isModel: false})} // Click on the 3D model to match mouseClick = (e) => {// map the mouse coordinates to the 3D coordinates e.preventDefault(); const that = this; const mouse = new THREE.Vector2(); mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;if(! this.camera || ! this.scene)return;
    letVector = new three.vector3 (mouse.x, mouse.y, 0.5).unproject(this. Camera);let raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize());
    let intersects = raycaster.intersectObjects(this.scene.children, true); // Select the 3d model console.log('sdj position',intersects)
    if (intersects.length > 0) {
      let SELECTED = intersects[0];
      let currentName = SELECTED.object.name;
      console.log('sdj position', e.clientX, e.clientY, e.screenX, e.screenY);
      if (objectArrName.indexOf(currentName) == -1) {
        if (this.controls.autoRotate){
          this.controls.autoRotate = false;
        }
        that.changeModel(e);
        that.setState({
          currentName,
          clientX: e.clientX,
          clientY: (e.clientY - 60)
        })
        console.log("The name of the object you selected is:"+ currentName); }}} // Mouse focus mouseenterObject = (e) => {// mouse coordinates map to 3d coordinates e.preventDefault();let mouse = new THREE.Vector2();
    mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
    letVector = new three.vector3 (mouse.x, mouse.y, 0.5).unproject(this. Camera);let raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize());
    let intersects = raycaster.intersectObjects(this.scene.children, true); // The selected 3d modelif(! Intersects.length && this.currentobjectColor && this.currentobject) {// Moves from the model to the outside. Intersects.length && this.currentobject) {if it is intersects.length && this.currentobjectColor this.currentObject.object.material.color.setHex(this.currentObjectColor); this.currentObjectColor = null; this.currentObject = null; }if (intersects.length > 0) {
      let SELECTED = intersects[0];
      let currentName = SELECTED.object.name;
      if (objectArrName.indexOf(currentName) == -1) {
        if (this.currentObject && currentName === this.currentObject.object.name) {
          return;
        }
        if(this.currentObjectColor && this.currentObject && currentName ! = = this. CurrentObject. Object. The name) {/ / color value is an object this.currentObject.object.material.color.setHex(this.currentObjectColor); } this.currentObject = SELECTED; this.currentObjectColor = SELECTED.object.material.color.getHex(); SELECTED.object.material.color.set(0x74bec1); }else {
        if(this.currentObjectColor && this.currentObject && currentName ! = = this. CurrentObject. Object. The name) {/ / color value is an object this.currentObject.object.material.color.setHex(this.currentObjectColor); } this.currentObjectColor = null; this.currentObject = null; } } } resizeFunc1 = () => { this.controls.update(); } resizeFunc2 = (e) => { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); }render() {
    return (
      <Fragment>
        <div
          className={ this.props.className || 'three-component' }
          id="d3"
          ref={ this.threeRef }
          onClick={this.mouseClick}
          onMouseMove={this.mouseenterObject}
        />
        {
          this.state.isModel && (
            <div
              className="three-modal"
              style={ {
                top: this.state.clientY,
                left: this.state.clientX
              } }
            >
              <Icon
                className="three-modal-close"
                type="close" theme="outlined"
                onClick={ this.closeModel }
              />
              <ul>
                <li>
                  <span className="modal-title"Word-wrap: break-word! Important; "> <span style =" max-width: 100%"modal-data">{ this.state.currentName }</span>
                </li>
                <li>
                  <span className="modal-title"> <span className="modal-data""> <span style =" max-width: 100%; clear: both; min-height: 1em"modal-title"Word-wrap: break-word! Important; "> <span style =" max-width: 100%"modal-data">6</span>
                </li>
                <li>
                  <span className="modal-title"Word-wrap: break-word! Important; "> <span className="modal-data">16</span>
                </li>
              </ul>
            </div>
          )
        }
      </Fragment>
    )
  }
}

export default GisThree;


Copy the code

The server errors, and the local server is not problem Reference stackoverflow.com/questions/4…

objLoader.js:624 Uncaught Error: THREE.OBJLoader: Unexpected line: "
      en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000">
       < title > _ intelligence community management background < / title > < link href = "/ static/CSS/main. Bdb0e864. CSS" rel="stylesheet">
      
      
"
at OBJLoader.parse (objLoader.js:624) at objLoader.js:385 at XMLHttpRequest.<anonymous> (three1.js:630) objLoader.js:624 Uncaught Error: THREE.OBJLoader: Unexpected line:" " at OBJLoader.parse (objLoader.js:624) at objLoader.js:385 at XMLHttpRequest.<anonymous> (three1.js:630) Copy the code

When you discard mtr-loader (and upgrade to Webpack4), the material is displayed correctly, and git ignores the.obj issue.

If there are any mistakes or irregularities, please be sure to correct them. Thank you very much!

Reference:

  1. Getting Started –Three.js Learn and sell
  2. Threejs official documentation
  3. Three. Js Chinese tutorial
  4. 1. Orbit Controls plug-in –2
  5. 3D model import
  6. react three.js
  7. The first Threejs project – Front-end Pit Filling Guide mainly introduces the transformation of C4D to JSON and some animation model considerations
  8. 【Three. Js 】OrbitControl rotation plug-in
  9. Read the Blender model and import the animation
  10. 3D physics in ten minutes