threeJs构建3D世界

threejs官网

https://threejs.org/docs/index.html#manual/zh/introduction/Installation (官网非常的详细)

导入安装

npm install three (下载安装threejs)

然后就可以在项目中导入threejs 

import * as THREE from 'three'

创建场景和相机

就是需要一个场景来呈现 3D效果 相机是 对于在哪个位置来观察这个场景中的3D效果(这里用到的Vue2)

<script> import * as THREE from 'three' export default {      camera: null,  //相机对象       scene: null,  //场景对象       renderer: null,  //渲染器对象   mounted () {     this.init()   },   methods: {     init () {       // 先创建一个场景        this.scene = new THREE.Scene();       // 创建一个相机       this.camera = new THREE.PerspectiveCamera(         // 第一个参数是角度 75°         75,         // 第二个参数传入宽高比          // window.innerWidth / window.innerHeight,         600 / 600,         // 近端         0.1,         // 远端         1000       )       // 创建相机定位 set 设置相机位置 x y z       this.camera.position.set(0, 0, 10)       // 把相机添加到场景       this.scene.add(this.camera)       // 创建一个物体   参数是宽高比 一样的大小       const cubeGeometry = new THREE.BoxGeometry(1, 1, 1)       // 创建物体材质       const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0Xffff00 })       // 根据几何体和材质创建物体 参数一是物体体 参数二是材质       const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)       // 将几何体添加到场景        this.scene.add(cube)        //初始化渲染器       this.renderer = new THREE.WebGLRenderer()       // 设置渲染的尺寸大小 可以填屏幕大小 参数是宽高       this.renderer.setSize(600, 600)       // 其实现在的renderer就是画布 把画板的dom渲染到画布上       document.querySelector('#container').appendChild(this.renderer.domElement)       // 使用渲染器 通过相机将场景渲染出来       this.renderer.render(this.scene, this.camera)     }   } } </script>

 

上面就可以呈现基本的物体了(关于材质或者渲染器什么的可以去 官网看看非常的详细)

接下来引入轨道  就是物体可以跟随鼠标的移动而移动

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' 

// 创建轨道控制器 第一个参数是相机 第二个参数是 要渲染的元素  const container = new OrbitControls(this.camera, this.renderer.domElement)
// 先执行一下那个渲染函数
   this.render()

    render () {       // 每一帧都会渲染轨道控制器 参数是场景和摄像头       this.renderer.render(this.scene, this.camera)       // 浏览器自带渲染 下一帧的时候就去在执行这个函数       requestAnimationFrame(this.render)     } 

 还有一些小功能 (不要在意那些变量都是不同代码找到的所以有些变量不一样嘿嘿)

  //   添加坐标 参数是大小的意思       const axesHelper = new THREE.AxesHelper(5)       // 添加到场景 中       this.scene.add(axesHelper)  // 设置物体的位置  // this.cube.position.set(5,0,0) // 也可以单独设置       this.cube.position.x += 0.1   // 物体的缩放 set 参数是xyz       this.cube.scale.set(2, 3, 1)    // 也可以设置旋转 set (x,y,z)    // 下面例子 Math.PI代表180° 'XYZ' 表示先旋转什么    // eg:this.cube.rotation.set(Math.PI /4 0,0,'XYZ')     // 下面是代表不断的旋转      this.cube.rotation.x += 0.1      // 这个是threejs 带的算时间的       this.clock = new THREE.Clock()  // 创建控制器       this.container = new OrbitControls(this.camera, this.renderer.domElement)       // 设置控制阻尼器,让控制器更有真实效果 必须在动画循坏的时候调用update().       container.enableDamping = true

 

当然配合使用一些好用的库 比如gsap(后期有时间会专门写一些这个动画库特别的厉害各种线性动画或者3D动画都是牛的)

https://greensock.com/docs/v3/GSAP 官网

还有一个好用的库  dat.gui 用来控制一些变量来帮助开发(非常的不错用起来也非常简单可以百度一下)

下载安装 npm install --save dat.gui // 导入dat.gui 控制变量 import * as dat from 'dat.gui'

知道了这些就可以做一些简单的好玩的效果了 但是需要找一些好看的3D模型 这里推荐一个

https://sketchfab.com/3d-models?features=downloadable&sort_by=-likeCount 这个也是threejs官网给的里面有好多的模型而且特别多免费的 

下面代码是gltf 模型和组合gsap 动画 包括模型上面带的动画组成的一个会走的宇航员 (非常有意思可以试试)

<template>   <div>     <div id="container" @dblclick="shopOrPlay"></div>   </div> </template>  <script> import * as THREE from 'three' // 导入控制器 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' // 导入模型 import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; // 导入动画 import gsap from 'gsap' export default {   data () {     return {       camera: null,  //相机对象       scene: null,  //场景对象       renderer: null,  //渲染器对象       mode: null, // 航天员       container: null,       flag: true,       clock: new THREE.Clock(),       mixer: '',       mixers: [],       animate1: null,       baseZ: 3,       baseR: 0.7,       basePY: -6,       basePX: -8,       baseMixer: null,       stars: null, //星星       mon: null,//月球     };   },   mounted () {     this.init()   },   methods: {      init () {       // 初始化场景       this.scene = new THREE.Scene();       //  设置背景       // 初始化相机 75度  宽高比 最小0.1 最大2000       this.camera = new THREE.PerspectiveCamera(75,         (document.documentElement.offsetWidth || window.innerWidth) / (document.documentElement.offsetHeight || window.innerHeight),         0.1,         100000)       // 设置摄像头位置       this.camera.position.set(0, 0, 10)       // // 初始化渲染器       this.renderer = new THREE.WebGLRenderer({         // 抗锯齿         // antialias: true,         alpha: true // 这个是背景透明色       })       // 设置渲染器宽高       this.renderer.setSize((document.documentElement.offsetWidth || window.innerWidth), (document.documentElement.offsetHeight || window.innerHeight))       // 实例化控制器       this.container = new OrbitControls(this.camera, this.renderer.domElement)       this.container.enableDamping = true          // 更新摄像头宽高比       this.camera.aspect = (document.documentElement.offsetWidth || window.innerWidth) / (document.documentElement.offsetHeight || window.innerHeight);       // // 更新摄像头投影矩阵       this.camera.updateProjectionMatrix()       // // 添加相机到场景       this.scene.add(this.camera);        // 初始化模型       const loader = new GLTFLoader();       // 添加月球       loader.load('/mon/scene.gltf', (gltf) => {         this.mon = gltf.scene         this.mon.scale.set(1500, 1500, 1500)         this.mon.position.set(0, -12, 0)          this.scene.add(this.mon);        }, undefined, function (error) {         });       //  创建一个超大球体 半径一千 后面的是经纬度 切分为各60       const skyGeomtry = new THREE.SphereGeometry(5000, 50, 50)       // 创建一个纹理       const skMaterial = new THREE.MeshBasicMaterial({         side: THREE.DoubleSide, //两面可见         // 添加纹理 为星河纹理         map: new THREE.TextureLoader().load('./images/bj.jpg')       })       // 把球体翻到里面能看见不然是黑色的 俩面可见就不翻转了       skyGeomtry.scale(1, 1, 1)       // 添加材质       const sky = new THREE.Mesh(skyGeomtry, skMaterial)        // 添加到场景       this.scene.add(sky)       // 创建宇航员       loader.load('./yuhangyuan/scene.gltf', (gltf) => {         this.mode = gltf.scene         this.mode.scale.set(3, 3, 3)         this.mode.position.set(this.basePX, this.basePY, -90)         this.mode.rotation.set(0, 0, 0)         this.mixer = new THREE.AnimationMixer(gltf.scene.children[0]);         this.baseMixer = this.mixer.clipAction(gltf.animations[0]).setDuration(1)         this.baseMixer.play();         this.animate1 = gsap.to(this.mode.position, {           z: this.baseZ, duration: 8, onComplete: () => {             gsap.to(this.mode.rotation, {               y: this.baseR * Math.PI, duration: 1,             })           },         })         this.mixers.push(this.mixer);         this.scene.add(this.mode);       }, undefined, (error) => {        });         // 添加光       let light2 = new THREE.DirectionalLight(0Xfffff, 0.3)       light2.position.set(0, 10, 10)        let light1 = new THREE.HemisphereLight();       this.scene.add(light1, light2       )       // // 设置渲染器编码       this.renderer.outputEncoding = THREE.sRGBEncoding;       // 监听屏幕大小变化修改渲染器的宽高相机比例       window.addEventListener('resize', this.size)       // 监听屏幕按键       window.addEventListener('keyup', this.spacemanMove)       document.querySelector('#container').appendChild(this.renderer.domElement)       this.render()     },     render () {        // 在这里设置阻尼感       this.container.update()       var delta = this.clock.getDelta();       for (var i = 0; i < this.mixers.length; i++) { // 重复播放动画         this.mixers[i].update(delta - 0.011);       }       this.renderer.render(this.scene, this.camera)       requestAnimationFrame(this.render)     },     size () {       this.camera.aspect = (document.documentElement.offsetWidth || window.innerWidth) / (document.documentElement.offsetHeight || window.innerHeight);       this.camera.updateProjectionMatrix()       this.renderer.setSize((document.documentElement.offsetWidth || window.innerWidth), (document.documentElement.offsetHeight || window.innerHeight))     },     // 宇航员移动     spacemanMove (e) {       if (!this.animate1) return       if (!this.animate1.isActive()) {         if (e.keyCode === 38) {           this.animate1 = gsap.to(this.mode.position, {             z: (this.baseZ -= 3)           })         }         if (e.keyCode === 40) {           this.animate1 = gsap.to(this.mode.position, {             z: (this.baseZ += 3)           })         }         if (e.keyCode === 37) {           this.animate1 = gsap.to(this.mode.rotation, {             y: (this.baseR -= 0.3) * Math.PI           })         }         if (e.keyCode === 39) {           this.animate1 = gsap.to(this.mode.rotation, {             y: (this.baseR += 0.3) * Math.PI           })         }         if (e.keyCode === 87) {           this.animate1 = gsap.to(this.mode.position, {             y: (this.basePY += 2)           })         }         if (e.keyCode === 83) {           this.animate1 = gsap.to(this.mode.position, {             y: (this.basePY -= 2)           })         }         if (e.keyCode === 65) {           this.animate1 = gsap.to(this.mode.position, {             x: (this.basePX -= 3)           })         }         if (e.keyCode === 68) {           this.animate1 = gsap.to(this.mode.position, {             x: (this.basePX += 3)           })         }       }     },     shopOrPlay () {       if (!this.flag) {         this.flag = true         this.baseMixer.play()       } else {         this.flag = false         this.animate1 = gsap.to(this.mode.rotation, {           y: (this.baseR += 2) * Math.PI,           yoyo: true,           duration: 10,         })         this.baseMixer.stop()       }     }   },   beforeDestroy () {     window.removeEventListener('resize', this.size)     window.removeEventListener('keyup', this.spacemanMove)   }, } </script> <style scoped lang='scss'> #container {   background: url("@/assets/bj1.jpg");   background-size: cover; } </style>

请多多指教  

发表评论

相关文章