Vue+Three.js+Rhino三维建模研究学习
最近在研究三维可视化,发现了一堆奇怪的问题,我好菜啊我怎么这么菜,记录一下过程和一些坑。
Three.js官网:https://threejs.org/
学犀牛中文论坛:http://www.xuexiniu.com/
最近在研究三维可视化,发现是个水很深的领域。Three.js是一个基于WebGL的三维可视化库,功能很强大,官网上有很多炫酷的例子。
Rhino是一个类似3Dmax的建模软件,为什么不用3Dmax……是因为Mac电脑装不了3Dmax ╮(╯_╰)
官网的Documentation中对Three.js的基础用法介绍的很详细,这里就不般教程了,只是稍微记录下这两天学习(爬坑)的过程。
警告⚠️:本篇不是个详细的Three.js教程,事实上,Vue,Three.js,Rhino单拎出来都不是很困难,遇到的大部分问题都是因为作者心血来潮要同时用这仨。
一. 环境搭建
Three.js的官网教程是基于html+js的,集成到Vue里直接npm或者yarn装就行了。
npm install three
<template> <div class="area"><!-- Three.js主体展示 --><div id="container"></div></div></template><script>import * as THREE from 'three'export default {name: 'ThreeTest',data() {return {camera: null,scene: null,renderer: null,}}methods: {}}</script>
methods: { init() {let container = document.getElementById('container');this.scene = new THREE.Scene();this.camera = new THREE.PerspectiveCamera(45, container.clientWidth/container.clientHeight, 0.1, 1000);this.camera.position.x = 40;this.camera.position.y = 40;this.camera.position.z = 40;this.camera.lookAt(this.scene.position);this.renderer = new THREE.WebGLRenderer();this.renderer.setClearColor(0xEEEEEE); //背景色this.renderer.setSize(container.clientWidth, container.clientHeight); //场景大小this.renderer.shadowMap.enabled = true; //启用阴影console.log(scene);console.log(camera);console.log(renderer);container.appendChild(this.renderer.domElement);this.renderer.render(this.scene, this.camera);}},mounted() {this.init();}
"ReferenceError: THREE is not defined"
import * as THREE from 'three'
二. Three.js基础元素
为了便于查看,先在场景中建立一个直角坐标系,包括坐标轴和平面。
//添加坐标轴let axes = new THREE.AxesHelper(20);this.scene.add(axes);//平面let planeGeometry = new THREE.PlaneGeometry(60,20)let planeMaterial = new THREE.MeshLambertMaterial({color: 0xcccccc});let plane = new THREE.Mesh(planeGeometry, planeMaterial);plane.receiveShadow = true;plane.rotation.x = -0.5* Math.PI;this.scene.add(plane);
let spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(40, 80, -20);spotLight.castShadow = true;this.scene.add(spotLight);
下面给这个场景加两个几何体。加一个球和一个方块。
首先在data里加上cube和sphere用来存储球和方块。
data() { return {……sphere:null,cube:null,}}
//方体let cubeGeometry = new THREE.BoxGeometry(5, 5, 5);let cubeMaterial = new THREE.MeshLambertMaterial({color: 0xffee6b});this.cube = new THREE.Mesh(cubeGeometry, cubeMaterial);this.cube.castShadow = true;this.cube.position.x = -10;this.cube.position.y = 10;this.scene.add(this.cube);//球体let sphereGeometry = new THREE.SphereGeometry(2, 20, 20);let sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});this.sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);this.sphere.castShadow = true;this.sphere.position.x = 20;this.sphere.position.y = 5;this.sphere.position.z = 0;this.scene.add(this.sphere);
MeshLambertMaterial
是Three.js中一种不太亮的纯色材质,Three.js中的材质还有很多,可以去官网文档的Materials那栏多尝试几个。
MeshNormalMaterial
,效果非常炫酷。
现在我们想让这两个元素动一动,所以新建一个animate方法。(记得先在data里定义好step,用来作为球移动的变量。)
animate(){requestAnimationFrame(this.animate);//旋转方体this.cube.rotation.x += 0.02;this.cube.rotation.y += 0.02;//弹跳球this.step += 0.03;this.sphere.position.x = 10 + ( 10 * (Math.cos(this.step)));this.sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(this.step)));this.renderer.render(this.scene, this.camera);}
mounted() {this.init();this.animate();}
let ambiColor = "#523318";let ambientLight = new THREE.AmbientLight(ambiColor);this.scene.add(ambientLight);
现在方块和球的颜色都是我们自己定的rgb颜色,但是三维模型一般都是需要贴图的,(我在给外部Obj贴图的时候遇到了个很大的坑!这个第四部分再述。)Three.js中,提供
TextureLoader
可以直接加载外部纹理图片。
TextureLoader
加载图片。注意引用静态资源直接加个
/
就行。
//加载纹理new THREE.TextureLoader().load('/Elizabeth.jpg',texture => {this.cube.material = new THREE.MeshLambertMaterial({map: texture});});
三. 添加Control组件
npm install dat.gui
import dat from 'dat.gui'
data() {return {……step:0,controls: {rotationSpeed: 0.02,bouncingSpeed: 0.03}}},
initgui() {const gui = new dat.GUI(); // gui监测器gui.add(this.controls, 'rotationSpeed', 0, 0.2);gui.add(this.controls, 'bouncingSpeed', 0, 0.2);this.init();},
let { controls } = thisrequestAnimationFrame(this.animate);//旋转方体this.cube.rotation.x += controls.rotationSpeed;this.cube.rotation.y += controls.rotationSpeed;//弹跳球this.step += controls.bouncingSpeed;this.sphere.position.x = 10 + ( 10 * (Math.cos(this.step)));this.sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(this.step)));this.renderer.render(this.scene, this.camera);
四. 引入外部三维模型
Uncaught Error: THREE.OBJLoader: Unexpected line: “”
npm install three-obj-mtl-loader
import { MTLLoader, OBJLoader } from 'three-obj-mtl-loader'
首先data里定义一个数据存放鱼。
data() {return {camera: null,scene: null,renderer: null,fish: null,}}
init() {……//加载模型let mtlLoader = new MTLLoader();let objLoader = new OBJLoader();mtlLoader.load('/fish.mtl', (materials) => {materials.preload();objLoader.setMaterials(materials);objLoader.load('/fish.obj', (object) => {object.scale.set(1, 1, 1);this.fish = object;this.scene.add(object);})})……},animate() {requestAnimationFrame(this.animate);if(this.fish){this.fish.rotation.y += 0.006;}this.renderer.render(this.scene, this.camera);}
object.scale.set(1, 1, 1);
对于有些模型,可能要放大到100倍才能看到,也有些模型可能要缩小到0.1.
map_Kd /Grass.png
/
路径(°ー°〃)。再回到浏览器里,发现纹理已经加上了。
就是只加载obj,然后手动用前文所说的
TextureLoader
自己加载纹理。
//曲线救国加载纹理new OBJLoader().load('/fish.obj' , obj => {let texture = new THREE.TextureLoader().load('/Grass.png');let material = new THREE.MeshLambertMaterial( { map: texture } );obj.children.forEach(child => {child.material = material;});this.fish = obj;this.scene.add(obj);})
