record

Recently, the company received a new project about packaging design, in which the technical difficulty involved is THE 3D model. Since the front end of our company has never been exposed to 3D, I do not know why, the boss entrusted the responsibility to me, so WE have to take it. The chosen technology is Nuxt+three.js, and here I will mainly talk about the use of three.js

Initialize the

Install the necessary three.js plug-ins in the vue project first

npm install three three-orbitcontrols three-obj-mtl-loader stats-js

  • Three: core library;
  • Stats-js: Performance checks
  • Three-orbitcontrols: Controls mouse rotation 3D model use;
  • Three-obj-mtr-loader: model import loader;

The necessary conditions for a model to appear on screen

  • Need a CAVnas as space for 3D rendering;
  • Scene: used to place various models, cameras, or light sources;
  • Camera: Used to view a scene; Camera is divided into perspective camera and face camera;
  • Light source: used to illuminate scenes and objects;
  • Model: The model can be built by three.js or imported by 3D modeling software, which is adopted in this paper. Obj format model file import.

1. Create a scenario

createScene() { this.container = document.getElementById("view-box"); This.scene = new three.scene (); This.scene. background = new three. Color(0xf3F3F3); Renderer = new THREE.WebGLRenderer({antialias: true, // enable transparency alpha: true}); this.renderer.setPixelRatio(window.devicePixelRatio); this.renderer.setSize(this.windowHalfX, this.windowHalfY); / / will render 3 d scenes on mount to the specified DOM this. The container. The appendChild (enclosing the renderer. DomElement accordingly). }Copy the code

Create the camera

CreateCamera () {this.camera = new THREE.PerspectiveCamera() WindowHalfX/this. WindowHalfY, // The farthest side of the camera cone (nearest visible distance). // Near the end of the camera cone (farthest visible distance) 2000); // Set the distance between the camera and the screen this.camera.position.z = 50; // Add the camera to the scene this.scene.add(this.camera); },Copy the code

3. Create lights

Lighting will not be introduced in detail. It is not used in this project, only the basic ambient light is used

CreateLight () {let color = 0xf9f9f9 // create ambientLight, no shadow, will evenly illuminate all objects in the environment let ambientLight = new THREE. This.scene. add(ambientLight); // add ambientLight to the scene. DirectionalLight = new THREE.DirectionalLight(0xb4b6fd); directionalLight.position.set(40, 60, 20); directionalLight.castShadow = true; directionalLight.shadowCameraNear = 2; directionalLight.shadowCameraFar = 100; directionalLight.shadowCameraLeft = -50; directionalLight.shadowCameraRight = 50; directionalLight.shadowCameraTop = 50; directionalLight.shadowCameraBottom = -50; // Add spotlights to the scene this.scene.add(directionalLight); },Copy the code

4, create model import model format (.obj)

createModel() { let mtlLoader = new MTLLoader() let loader = new OBJLoader(this.manager); MTL file mtlLoader.load(' /obj/white-box.mtl ', (materials) => {materials.preload(); Loader.load (' /obj ', (obj) => {this.object = obj; / / set the model center center this. Object. Children [0]. Geometry. The center (). This object. The children [0]. Geometry.com puteBoundingBox (); }); })},Copy the code

5. Create a texture

There are many layers that need to be changed on each surface, so canvas map is chosen, which is more convenient to operate on canvas

/ * * * to create a map * / createTexture () {this. CurrentModelTexture. Top = new THREE. CanvasTexture (enclosing createCanvas ()); this.lodingModelAndTexture() }, LodingModelAndTexture () {let loadModel = () => {this.object.traverse((child) => {// Set the length, width, and height of the model if (child.isMesh) child.scale.set(1, 1, 1); If (child instanceof three.mesh) {if (child.material instanceof Array) {child.material / / item. The map = this. CurrentModelTexture [item. The name] / / this is in order to facilitate understanding, only opened a surface item. The map = this. CurrentModelTexture. Top}); child.castShadow = true; child.receiveShadow = true; }}}); // Add the model to the scene this.scene.add(this.object); }; }, /** * returns a canvas */ createCanvas() {const canvas = utils.createHidpicanvas (width, height, 4) const ctx = canvas.getContext('2d'); ctx.fillStyle = #fff; Ctx.fillrect (0, 0, canvas width, canvas height); ctx.textBaseline = "hanging"; let img = new Image(); img.src = '1.jpg'; / / | transparency rotating let alpha = 1, deg = 180; Img.onload = (e) => {// Save the canvas state ctx.save(); ctx.globalAlpha = alpha; CTX. Translate (Canvas width / 2, canvas height / 2); ctx.rotate(deg * Math.PI / 180); DrawImage (zindex. Img, x, y, w, h); // Restore status ctx.restore(); } return canvas}, /** * create hd Canvas canvas * @param {width} w * @param {height} h * @param {pixel} ratio */ createHiDPICanvas(w, h, ratio) { const PIXEL_RATIO = (function() { const c = document.createElement("canvas"), ctx = c.getContext("2d"), dpr = window.devicePixelRatio || 1, bsr = ctx['webkitBackingStorePixelRatio'] || ctx['mozBackingStorePixelRatio'] || ctx['msBackingStorePixelRatio'] || ctx['oBackingStorePixelRatio'] || ctx['backingStorePixelRatio'] || 1; return dpr / bsr; }) (); if (! ratio) { ratio = PIXEL_RATIO; } const can = document.createElement("canvas"); can.width = w * ratio; can.height = h * ratio; can.style.width = w + "px"; can.style.height = h + "px"; can.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0); return can; }Copy the code

6, animation,

If (this.object) {this.object.children[0].rotation. Y += 0.001 requestAnimationFrame(this.animate); this.renderer && this.render(); }, // render() {this.camera. LookAt (this.scene. Position); this.renderer.render(this.scene, this.camera); }, init() { this.createScene() this.createCamera() this.createLight() this.createTexture() this.$nextTick(() => { this.createModel() }); This.createOther()}, // Start loading loding() {this.windowhalfx = this.$refs.three. this.windowHalfY = window.innerHeight - 80; this.init(); this.animate(); },Copy the code

7. Other controllers and auxiliary Settings

CreateOther () {var AxesHelper = new three.axesHelper (150); this.scene.add(AxesHelper); Var controls = new OrbitControls(this.camera, this.renderer.domElement); this.renderer.domElement.removeAttribute('tabindex') controls.addEventListener("change", this.render); this.render(); },Copy the code

8, performance optimization, cache cleaning

/ / to empty the renderer cache clearRenderer () {enclosing object && enclosing clearCache (enclosing object. The children [0]) enclosing the renderer. ForceContextLoss (); this.currentModelTexture = null this.renderer.dispose(); this.renderer.domElement = null; this.renderer = null; Destroyed () {this.clearrenderer ()}Copy the code

9. Other functional methods used

Insertion sort

/** * @insertion = (arr, 1, 1) const Insertion = (arr, 1, 1) val) => { let handle = [] handle.push(arr[0]) for (let i = 1; i < arr.length; i++) { let a = arr[i][val] for (let j = handle.length - 1; j >= 0; j--) { let b = handle[j][val] if (a > b) { handle.splice(j + 1, 0, arr[i]) break } if (j === 0) { handle.unshift(arr[i]) } } } return handle }Copy the code

Pictures turn base64

/** * const converbase64Img = (url) => {var canvas = document.createElement(' canvas '), ctx = canvas.getContext('2d'); return new Promise((resolve, reject) => { let img = new Image; img.crossOrigin = ''; img.src = url; img.onload = function() { canvas.height = img.height; canvas.width = img.width; ctx.drawImage(img, 0, 0); var dataURL = canvas.toDataURL('image/png'); resolve(dataURL) canvas = null; }; img.onerror = (e) => { reject(e) } }) }Copy the code
  • Three. Js document in Chinese www.yanhuangxueyuan.com/threejs/doc…
  • Vue + threejs using canvas texture lindasoft.com/view/articl…