three.js学习笔记1

301 阅读3分钟

工作之余,就来学学three.js。为了快速开始体验three.js,我选择用vue的脚手架+vue3项目快速搭建了开发环境。项目创建好后,就正式开始three.js的学习之旅吧~

1. 安装并引入three.js

安装:npm install three

引入:

// 方式 1: 导入整个 three.js核心库 
import * as THREE from 'three'; 
const scene = new THREE.Scene(); 
// 方式 2: 按需导入
import { Scene } from 'three'; 
const scene = new Scene();

2. 阅读并按照官方文档进行操练

2.1 了解three.js中的四大要素

three.js中的四大要素:场景、相机、渲染器和渲染主体(物体)。

首先对四大元素进行初始化及参数的配置。

import {Scene,PerspectiveCamera,WebGLRenderer, BoxGeometry, MeshBasicMaterial,Mesh} from 'three'
    // 场景
    let scene = new Scene()

    // 相机 这里用的是透视摄像机
    // PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
    /**
     * 视锥体参数
     * fov: 视野角度
     * aspect:长宽比
     * near:近剪切面
     * far:远剪切面
     */
    let camera = new PerspectiveCamera(35,window.innerWidth/window.innerHeight,0.1,1000)
    // 设置相机位置  两种设置方式
    camera.position.z = 18
    // camera.position.set(0,20,100)

    // 渲染器初始化及渲染参数的配置
    let renderer = new WebGLRenderer({
      antialias:true,  // 抗锯齿   默认值为false,看起有点粗糙,所以我这里设置为true
      // alpha:true    // canvas是否包含alpha (透明度)。默认为 false
    })
    // 设置渲染的尺寸大小
    renderer.setSize(window.innerWidth,window.innerHeight)
    
    // 添加物体
    // 创建几何体
    let geometry = new BoxGeometry(1,1,1)
    let material = new MeshBasicMaterial({color:0xe3c144})
    // 根据几何体和材质创建物体
    let cube = new Mesh(geometry, material)
    // 改变物体的位置  两种设置方式
    cube.position.set(1,1,1)
    // cube.position.x = 1
    
    // 将物体添加到场景中
    scene.add(cube)
    renderer.render(scene,camera)
    
    // 将webgl渲染的canvas内容添加到指定容器内
    outputBox.value.appendChild(renderer.domElement)
    // 因为我是在vue3项目中使用,我想放到指定的容器中去显示,所以用到了ref,你完全可以使用下面这种写法代替。
    //document.body.appendChild(renderer.domElement)

此时在页面上就会出现我们绘制的物体了。效果如下:

图片.png

2.2 加入three.js内置的辅助工具

① 坐标轴辅助器

import {Scene,PerspectiveCamera,WebGLRenderer, BoxGeometry, MeshBasicMaterial,Mesh,AxesHelper} from 'three'

    // 坐标轴辅助器
    const axesHelper = new AxesHelper(5) // 参数是指坐标轴的长度
    //将物体和坐标轴复制器添加到场景中
    scene.add(axesHelper)

图片.png

② 控制器

使用控制器查看3D物体,按住鼠标左键在页面上拖动,就能看到效果,不信你试试?

注:控制器要在循环渲染的动画里才有效果。

// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

    // 使用控制器查看3D物体
    const controls = new OrbitControls(camera,renderer.domElement)
    // 设置控制器阻尼   必须配合在渲染动画的循环里调用.update()
    controls.enableDamping = true
    
    function render() {
       controls.update()
       renderer.render(scene,camera)
       requestAnimationFrame(render)
    }

    onMounted(()=> {
      // 将webgl渲染的canvas内容添加到指定容器内
      outputBox.value.appendChild(renderer.domElement)
      render()
    })

图片.png

2.3 加入第三方工具

① gsap动画库

官方文档

安装 gsap - npm

npm install gsap

引入

import gsap from "gsap";

使用

// 导入动画库
import gsap from 'gsap'

    // 设置动画
    // 动画1
    let ani = gsap.to(
                cube.position,              // 位置变化  移动
                {
                  x:5,
                  duration:6,               // 每次动画的执行时间
                  delay:2,                  // 延迟动画的开始时间
                  ease:"back.inOut(1.7)",   // 动画速度
                  repeat:-1,                // 动画重复次数 , -1 表示无限次
                  yoyo:true,                // 往返运动   要配合着repeat 一起使用才会生效
                  onStart: () => {          // 动画开始的回调
                    console.log('动画开始')
                  },
                  onComplete:() => {        // 动画结束的回调
                    console.log('动画结束')
                  }
                }
              )

    // 动画2
    gsap.to(
      cube.rotation,              // 旋转
      {
        x:2 * Math.PI,            // 旋转角度
        duration:5,
        repeat:-1                
      }
    )

    // 监听单击事件结束或恢复动画
    document.addEventListener('click',() => {
      // 判断当前动画是否是激活状态,是则暂停,不是则恢复
      ani.isActive() ? ani.pause() : ani.resume()
    })

图片.png

② dat.gui UI工具库

安装 dat.gui

npm install --save dat.gui

引入

import * as dat from 'dat.gui';

使用 dat.gui/API

// 导入 dat.gui库   UI工具库
import * as dat from 'dat.gui'

    // 初始化UI调节工具
    const gui = new dat.GUI()
    gui.add(cube.position,'x').min(0).max(5).step(0.01).name('移动X轴').onFinishChange(value => {
      console.log('调整后的值为:',value)
    })
    const params = {color:0x00ff00,fn: () => {
      ani.isActive() ? ani.pause() : ani.resume()
    }}
    gui.addColor(params,"color").onChange((value) => {
      cube.material.color.set(value)
    })
    gui.add(params,'fn').name('物体运动')
    let folder = gui.addFolder('物体属性设置')
    folder.add(cube.material,"wireframe")

图片.png

2.4 优化

① 进入全屏和退出全屏

    document.addEventListener('dblclick',() => {
       const fullScreenElement = document.fullscreenElement
       !fullScreenElement ? renderer.domElement.requestFullscreen() : document.exitFullscreen()
    })

② 页面尺寸变化时,同步更新渲染画面

    // 监听页面变化,更新渲染画面
    window.addEventListener('resize',() => {
      // 更新摄像头
      camera.aspect = window.innerWidth / window.innerHeight
      // 更新摄像机的投影矩阵
      camera.updateProjectionMatrix()
      // 更新渲染器
      renderer.setSize(window.innerWidth,window.innerHeight)
      // 设置渲染器的像素比
      renderer.setPixelRatio(window.devicePixelRatio)
    })

2.5 完整代码

BaseDemo.vue

<template>
  <div ref="outputBox"></div>
</template>

<script>
import { onMounted,ref } from 'vue'
import {Scene,PerspectiveCamera,WebGLRenderer, BoxGeometry, MeshBasicMaterial,Mesh,AxesHelper} from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
// 导入动画库
import gsap from 'gsap'
// 导入 dat.gui库   UI工具库
import * as dat from 'dat.gui'

export default {
  name: 'BaseDemo',
  setup(){
    // 元素引用
    const outputBox = ref(null)

    // 场景
    let scene = new Scene()

    // 摄像机 这里用的是透视摄像机
    // PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
    /**
     * 视锥体参数
     * fov: 视野角度
     * aspect:长宽比
     * near:近剪切面
     * far:远剪切面
     */
    let camera = new PerspectiveCamera(35,window.innerWidth/window.innerHeight,0.1,1000)
    // 设置相机位置  两种设置方式
    camera.position.z = 18
    // camera.position.set(0,20,100)

    // 初始化渲染器及渲染参数的配置
    let renderer = new WebGLRenderer({
      antialias:true,  // 抗锯齿
      // alpha:true    // canvas是否包含alpha (透明度)。默认为 false
    })
    // 设置渲染的尺寸大小
    renderer.setSize(window.innerWidth,window.innerHeight)

    // 添加物体
    // 创建几何体
    let geometry = new BoxGeometry(1,1,1)
    let material = new MeshBasicMaterial({color:0xe3c144})
    // 根据几何体和材质创建物体
    let cube = new Mesh(geometry, material)
    // 改变物体的位置
    // cube.position.set(1,1,1)
    // cube.position.x = 1

    // 坐标轴辅助器
    const axesHelper = new AxesHelper(5)
    //将物体和坐标轴复制器添加到场景中
    scene.add(cube,axesHelper)

    // 使用控制器查看3D物体
    const controls = new OrbitControls(camera,renderer.domElement)
    // 设置控制器阻尼   必须配合在渲染动画的循环里调用.update()
    controls.enableDamping = true

    // 设置动画
    // 动画1
    let ani = gsap.to(
                cube.position,              // 位置变化  移动
                {
                  x:5,
                  duration:6,               // 每次动画的执行时间
                  delay:2,                  // 延迟动画的开始时间
                  ease:"back.inOut(1.7)",   // 动画速度
                  repeat:-1,                // 动画重复次数 , -1 表示无限次
                  yoyo:true,                // 往返运动   要配合着repeat 一起使用才会生效
                  onStart: () => {          // 动画开始的回调
                    console.log('动画开始')
                  },
                  onComplete:() => {        // 动画结束的回调
                    console.log('动画结束')
                  }
                }
              )

    // 动画2
    gsap.to(
      cube.rotation,              // 旋转
      {
        x:2 * Math.PI,            // 旋转角度
        duration:5,
        repeat:-1                
      }
    )

    // 初始化UI调节工具
    const gui = new dat.GUI()
    gui.add(cube.position,'x').min(0).max(5).step(0.01).name('移动X轴').onFinishChange(value => {
      console.log('调整后的值为:',value)
    })
    const params = {color:0x00ff00,fn: () => {
      ani.isActive() ? ani.pause() : ani.resume()
    }}
    gui.addColor(params,"color").onChange((value) => {
      cube.material.color.set(value)
    })
    gui.add(params,'fn').name('物体运动')
    let folder = gui.addFolder('物体属性设置')
    folder.add(cube.material,"wireframe")

    // 监听双击事件 进入全屏和退出全屏
    document.addEventListener('dblclick',() => {
      const fullScreenElement = document.fullscreenElement
      !fullScreenElement ? renderer.domElement.requestFullscreen() : document.exitFullscreen()
    })

    // 监听页面变化,更新渲染画面
    window.addEventListener('resize',() => {
      // 更新摄像头
      camera.aspect = window.innerWidth / window.innerHeight
      // 更新摄像机的投影矩阵
      camera.updateProjectionMatrix()
      // 更新渲染器
      renderer.setSize(window.innerWidth,window.innerHeight)
      // 设置渲染器的像素比
      renderer.setPixelRatio(window.devicePixelRatio)
    })

    // 渲染函数
    function render() {
       controls.update()
       renderer.render(scene,camera)
       requestAnimationFrame(render)
    }

    onMounted(()=> {
       // 将webgl渲染的canvas内容添加到指定容器内
       outputBox.value.appendChild(renderer.domElement)
       // document.body.appendChild(renderer.domElement)
       render()
    })
    
    return {
       outputBox
    }   

}
}

</script>

好啦,本篇就记录这么多。music~

b5af6672b1d6444da871404b629b2c90.gif