Use three.js to import external glb model to implement an nft product

nft digital collections are booming, and every company has launched its own apps and mini-programs. So how are cool 3D collections realized? Let’s find out!

Demand background

As various digital collection projects go online, our team has followed the trend and plans to develop a digital collection product in the short term. What are the main needs? Of course, we should do what others have, with the same functions but different UIs. Other functions are easy to talk about, but the 3D effect of collection play has not been mentioned before, but if you know it, it can be achieved through three.js. So, start reading the documentation and research.

Preparation

First install the three.js plug-in in the project

npm i three
npm i three-orbitcontrols

Then introduce it in the corresponding scene

import * as THREE from 'three';
import {
    
    
	OrbitControls
} from "three/examples/jsm/controls/OrbitControls"; // 鼠标控制器
import {
    
    
	GLTFLoader
} from "three/examples/jsm/loaders/GLTFLoader.js"; // glb模型加载器

use

  1. Create a scene
createScene() {
    
    
	// 创建场景
	this.scene = new THREE.Scene();

	// 创建相机
	let width = this.$refs.container.$el.offsetWidth; // 窗口宽度
	let height = this.$refs.container.$el.offsetHeight; // 窗口高度
	this.width = width;
	this.height = height;
	let k = width / height; // 窗口宽高比
	// 初始化相机,参数通俗讲PerspectiveCamera(远近,宽高比,摄像机视锥体近端面(默认0.1),
	// 摄像机视锥体远端面(默认2000,无限大,表示可以看到最远))
	this.camera = new THREE.PerspectiveCamera(60, k, 0.1, 180); // 透视相机
	this.camera.position.set(0, 0, 4); // 设置相机位置
	this.camera.lookAt(this.scene.position); // 指向场景中心

	// 创建渲染器
	this.renderer = new THREE.WebGLRenderer({
    
    
		antialias: true, // 抗锯齿
		alpha: true,
	});
	// 设置渲染区域尺寸
	this.renderer.setSize(width, height);
	// 设置背景颜色
	this.renderer.setClearColor(0x1e1e1e, 1);
	// 渲染器开启阴影效果
	this.renderer.shadowMap.enabled = true;
	// 阴影贴图类型
	this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;

	// 添加坐标轴,用于调试,可以直观的展示模型的空间位置
	let axes = new THREE.AxesHelper(1000);
	this.scene.add(axes);

	// 将画布添加到容器中
	document.getElementById("container").appendChild(this.renderer.domElement);
	// 创建相机控件
	this.createOrbitControls();
},
  1. Create background
// 创建背景,根据业务需求完善
createUniverse() {
    
    
	const BgImg = ""; // 背景图片
	let texture = new THREE.TextureLoader().load(BgImg); // 加载背景贴图
	this.scene.background = texture; // 设置场景背景
},
  1. Create camera controls
// 创建相机控件
createOrbitControls() {
    
    
	// 创建相机控件对象
	this.mouseControls = new OrbitControls(
		this.camera,
		this.renderer.domElement
	);
	// 右键平移拖拽
	this.mouseControls.enablePan = false; // 在移动端是双指拖拽
	// 鼠标缩放功能
	this.mouseControls.enableZoom = true;
	// 相机距离原点的距离范围
	this.mouseControls.minDistance = 0;
	this.mouseControls.maxDistance = 100;

	// 滑动阻尼,鼠标拖拽旋转的灵敏度,阻尼越小越灵敏
	this.mouseControls.enableDamping = true;
	// 阻尼系数(默认0.25)
	this.mouseControls.dampingFactor = 0.5;
	// 上下翻转角度范围
	this.mouseControls.maxPolarAngle = 3; // y旋转角度范围
	this.mouseControls.minPolarAngle = 0;

	// 是否自动旋转
	this.mouseControls.autoRotate = true;
	this.mouseControls.autoRotateSpeed = 5; // 自转速度
	// 加载模型
	this.loadGlbModel();
},
  1. Create a light source
// 创建光源
createLight() {
    
    
	// 配置环境光
	this.ambientLight = new THREE.AmbientLight(0x999999);
	// 将环境光添加到场景中
	this.scene.add(this.ambientLight);
	// 配置点光源
	this.pointLight = new THREE.PointLight(0xffffff, 1, 0);
	// 配置点光源的位置
	this.pointLight.position.set(500, 300, 400);
	// 将点光源添加至场景中
	this.scene.add(this.pointLight);
},
  1. Load model
// 加载glb、glbf模型
loadGlbModel() {
    
    
	let self = this;
	let loader = new GLTFLoader();
	// 本地静态模型调试要放在根目录public中,我们这边用的uniapp就放在static中,不然会出现加载问题
	loader.load(`${
      
      this.modelPath}`, (gltf) => {
    
    
		console.log('gltf ---->>', gltf);
		let loaderScene = gltf.scene;
		loaderScene.scale.set(1, 1, 1); // 设置模型大小缩放
		loaderScene.position.set(0, -1, 0);
		// 模型加入到场景中
		self.scene.add(loaderScene);
		// 渲染器启动
		this.repeatRender();
	}, (xhr) => {
    
    
		console.log(`${
      
       (xhr.loaded / xhr.total) * 100 }%`)
	}, (err) => {
    
    
		console.log('error ---->>', err);
	})
},
  1. Renderer
// 渲染器
repeatRender() {
    
    
	let animate = () => {
    
    
		// 请求动画帧,屏幕每刷新一次调用一次,绑定屏幕刷新频率
		this.clearAnim = requestAnimationFrame(animate);
		// 实时更新相机控件
		this.mouseControls.update();
		// 渲染场景和相机
		this.renderer.render(this.scene, this.camera);
	};
	animate();
},

Complete code

<template>
	<view class="enlarge">
		<view class="header">
			<view class="header-name">{
    
    {
    
     goodsName }}</view>
			<view class="header-auth">
				<view class="auth">创作者:</view>
				<view class="name">{
    
    {
    
     goodsAuth }}</view>
			</view>
		</view>
		<view class="container" id="container" ref="container"></view>
		<view class="bottom-box">
			<image class="bottom-box-img" src="@/static/img/share-bg.png" mode="widthFix"></image>
		</view>
	</view>
</template>

<script>
	import * as THREE from 'three';
	import {
    
    
		OrbitControls
	} from "three/examples/jsm/controls/OrbitControls"; // 鼠标控制器
	import {
    
    
		GLTFLoader
	} from "three/examples/jsm/loaders/GLTFLoader.js"; // 模型加载器
	import {
    
    
		FBXLoader
	} from 'three/examples/jsm/loaders/FBXLoader.js';
	export default {
    
    
		data() {
    
    
			return {
    
    
				goodsName: '做前端死路一条',
				goodsAuth: '鲁迅',
				scene: null, // 场景
				camera: null, // 相机
				mouseControls: null, // 相机控件
				renderer: null, // 渲染
				ambientLight: null, // 环境光
				clearAnim: null, // 动画帧
				autoRotate: true, // 是否自转
				autoRotateSpeed: 10, // 自转速度,数值越小速度越慢
				modelPath: "static/model/elvenKing.glb", // 模型路径
				width: null, // 画布宽度
				height: null, // 画布高度
				distance: 60, // 视野距
				near: 1, // 最小视野距
				far: 180, // 最大视野距
			};
		},
		onReady() {
    
    
			this.init();
		},
		onUnload() {
    
    
			// 组件卸载
			cancelAnimationFrame(this.clearAnim);
			// 场景
			this.scene = null;
			// 相机
			this.camera = null;
			// 相机控件
			this.mouseControls = null;
			// 渲染器
			this.renderer = null;
			// 环境光
			this.ambientLight = null;
			// 动画帧
			this.clearAnim = null;
		},
		methods: {
    
    
			init() {
    
    
				this.createScene(); // 创建场景
				this.createUniverse(); // 创建背景
				this.createLight(); // 创建光源
			},
			// 初始化步骤
			createScene() {
    
    
				// 创建场景
				this.scene = new THREE.Scene();

				// 创建相机
				let width = this.$refs.container.$el.offsetWidth; // 窗口宽度
				let height = this.$refs.container.$el.offsetHeight; // 窗口高度
				this.width = width;
				this.height = height;
				let k = width / height; // 窗口宽高比
				// 初始化相机,参数通俗讲PerspectiveCamera(远近,宽高比,摄像机视锥体近端面(默认0.1),
				// 摄像机视锥体远端面(默认2000,无限大,表示可以看到最远))
				this.camera = new THREE.PerspectiveCamera(this.distance, k, this.near, this.far); // 透视相机
				this.camera.position.set(0, 0, 4); // 设置相机位置
				this.camera.lookAt(this.scene.position); // 指向场景中心

				// 创建渲染器
				this.renderer = new THREE.WebGLRenderer({
    
    
					antialias: true, // 抗锯齿
					alpha: true,
				});
				// 设置渲染区域尺寸
				this.renderer.setSize(width, height);
				// 设置背景颜色
				this.renderer.setClearColor(0x1e1e1e, 1);
				// 渲染器开启阴影效果
				this.renderer.shadowMap.enabled = true;
				// 阴影贴图类型
				this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;

				// 添加坐标轴,辅助判断位置,可用于调试
				let axes = new THREE.AxesHelper(1000);
				this.scene.add(axes);

				// 将画布添加到容器中
				document.getElementById("container").appendChild(this.renderer.domElement);
				this.createOrbitControls();
			},
			// 创建背景,根据业务需求完善
			createUniverse() {
    
    
				const BgImg = "";
				let texture = new THREE.TextureLoader().load(BgImg); // 加载背景贴图
				this.scene.background = texture; // 设置场景背景
			},
			// 创建相机控件
			createOrbitControls() {
    
    
				// 创建相机控件对象
				this.mouseControls = new OrbitControls(
					this.camera,
					this.renderer.domElement
				);
				// 右键平移拖拽
				this.mouseControls.enablePan = false;
				// 鼠标缩放
				this.mouseControls.enableZoom = true;
				// 相机距离原点的距离范围
				this.mouseControls.minDistance = 0;
				this.mouseControls.maxDistance = 100;

				// 滑动阻尼,鼠标拖拽旋转的灵敏度,阻尼越小越灵敏
				this.mouseControls.enableDamping = true;
				// 阻尼系数(默认0.25)
				this.mouseControls.dampingFactor = 0.5;
				// 上下翻转角度范围
				this.mouseControls.maxPolarAngle = 3; // y旋转角度范围
				this.mouseControls.minPolarAngle = 0;

				// 是否自动旋转
				this.mouseControls.autoRotate = this.autoRotate;
				this.mouseControls.autoRotateSpeed = this.autoRotateSpeed; // 自转速度
				// 加载模型
				this.loadGlbModel();
			},
			// 创建光源
			createLight() {
    
    
				// 配置环境光
				this.ambientLight = new THREE.AmbientLight(0x999999);
				// 将环境光添加到场景中
				this.scene.add(this.ambientLight);
				// 配置点光源
				this.pointLight = new THREE.PointLight(0xffffff, 1, 0);
				// 配置点光源的位置
				this.pointLight.position.set(500, 300, 400);
				// 将点光源添加至场景中
				this.scene.add(this.pointLight);
			},
			// 加载glb、glbf模型
			loadGlbModel() {
    
    
				let self = this;
				let loader = new GLTFLoader();
				loader.load(`${
      
      this.modelPath}`, (gltf) => {
    
    
					console.log('gltf---->>', gltf);
					let loaderScene = gltf.scene;
					loaderScene.scale.set(1, 1, 1); // 设置模型大小缩放
					loaderScene.position.set(0, -1, 0);

					self.scene.add(loaderScene);
					// 渲染器启动
					this.repeatRender();
				}, (xhr) => {
    
    
					console.log(`${
      
       (xhr.loaded / xhr.total) * 100 }%`)
				}, (err) => {
    
    
					console.log('error ---->>', err);
				})
			},

			// 渲染器
			repeatRender() {
    
    
				let animate = () => {
    
    
					// 请求动画帧,屏幕每刷新一次调用一次,绑定屏幕刷新频率
					this.clearAnim = requestAnimationFrame(animate);
					// 实时更新相机控件
					this.mouseControls.update();
					// 渲染场景和相机
					this.renderer.render(this.scene, this.camera);
				};
				animate();
			},
		}
	}
</script>

<style lang="less">
	.enlarge {
    
    
		padding: 32rpx;
		width: 100%;
		height: 100%;
		box-sizing: border-box;
		position: fixed;
		top: 0;
		right: 0;
		background-color: #1e1e1e;

		.header {
    
    
			position: absolute;
			top: 32rpx;
			left: 32rpx;
			color: #fff;
			background-color: rgba(0, 0, 0, 0);

			.header-name {
    
    
				font-size: 40rpx;
			}

			.header-auth {
    
    
				font-size: 28rpx;
				display: flex;
				color: #b1b1b1;
				margin-top: 16rpx;
			}
		}

		.container {
    
    
			width: 100%;
			height: 100%;
		}

		.bottom-box {
    
    
			width: 100%;
			padding: 0 32rpx;
			box-sizing: border-box;
			position: absolute;
			bottom: 160rpx;
			left: 0;
			z-index: -1;

			&-img {
    
    
				width: 100%;
			}
		}
	}
</style>

Guess you like

Origin blog.csdn.net/zw7518/article/details/125414001