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…