‘丐版’原宇宙,three+blender+react|大帅老猿threejs特训

587 阅读5分钟
screen.gif ### 前言 📃前几天参加了原宇宙项目的学习,奈何是blender新手,工作时间不能学习,只能忙里偷闲,先先做一个记录,项目用到的和没用到的文件都在src目录下的`resources`文件夹里面。想着做不同的,那么就用框架不一样吧,利用vite搭建的react项目,下载threeJs安装包书写。 ### 准备工作 #### 1 ,工具 使用的是blender + 因为blender对于mac和windows都是可以使用的,3dMax只适用于windows系统(不过因为我之前学过3dMax的,所以,虽然我是mac,但是也安装了一个windows的虚拟机,3dMax跑起来还是可以使用的。)

2,网址

  • blender下载地址:www.blender.org/thanks/

  • 下载贴图或者材质的:www.textures.com/library

  • 一些基本模型:sketchfab.com/ (在sketchfab上可以拿模型,实现模型的小部分自由)(qq邮箱,google邮箱可以,163邮箱注册好像不行)

  • 用于模型绑定骨骼: www.mixamo.com/ (无动画的角色,演示的时候用的glb不支持,没提示。用fbx格式的可以)

3,blender中的一些快捷键

在blender中使用的的右手坐标系

功能快捷键
全选:A取消全选:AA复制物体:Shift+D
移动:G缩放:S移动视角:鼠标滚轮键
旋转:R搜索:F3删除:X
填充面:F合并顶点:M编辑模式:Tab
前视图:1右视图:3顶视图:7
切换视图:Alt+鼠标中键透视显示模式:Alt+Z分离:P
属性栏:N左侧工具栏:T环选:Alt+A
反选:Ctrl+I隐藏物体:H取消隐藏:Alt+H
查看全部:Shift+C父子链接:Ctrl+P相机视图:0
着色模式:Z弹出上次的渲染窗口:F11创建集合:M
设置游标:Shift+S最大化当前窗口:Ctrl+空格物体交互模式设置:Ctrl+Tab
进入/退出三视图:Ctrl+Alt+Q挤出:F9游标回到原点:Shift+C
开启/关闭吸附功能:Shift+Tab选择相连元素:L普通复制:Shift+D
去除父子关系:Alt+P创建父子关系:Ctrl+P移动物体:M
连接节点:F断开节点:Ctrl+鼠标左键添加转换点:Shift+鼠标左键
暂停/播放:Alt+A上一帧/下一帧:左右箭头删除关键帧:Alt+I
切换点线面:Ctrl+Tab撕开后填充:Alt+V倒角工具:Ctrl+B
切割工具:K挤出工具:E细分物体:按住S+鼠标中键
渲染:F12保存用户设置:Ctrl+U

有些快捷键我没有试过,可以自己动手尝试做一下

4,代码实现时可能遇到的几个坑

  • 动画为什么不动, 需要在动画中设置帧的播放顺序
  • 为什么按照操作,整个画面是黑的, 因为相机在内部,需要将相机的位置进行设置
  • 为什么底部的材质是黑的, 可以用一个环境光,将周围的亮度调亮

5,代码

1,使用vite搭建项目

这里选择的是react版本

npm create vite@latest

2,下载three.js安装包

下载后查看文档对应下载的版本进行查看,因为不同的版本相应的文件可能存在的文件夹不一样,这里我用的是0.145.0

npm install three@0.145.0 

3,具体代码

  • 1,引入相关文件
import { useState, useEffect, useRef } from 'react'
import * as THREE from 'three';
// 引入three.js其他扩展库,对应版本查看文档,最新扩展库在addons文件夹下,eg:'three/addons/controls/OrbitControls.js';
// OrbitControls控件支持鼠标左中右键操作和键盘方向键操作
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
  • 2,设置盒子宽高
 function App() {
  const [width, setWidth] = useState(window.innerWidth)
  const [height, setHeight] = useState(window.innerHeight)
  return (
    <div id="container" style={{ width: width, height: height }}></div>
  )
}

export default App
  • 3,若窗口改变,则对窗口进行重置
const resizeUpdate = (e) => {
    // 通过事件对象获取浏览器窗口的高度
    let h = e.target.innerHeight;
    let w = e.target.innerWidth;
    // 对应用大小进行重置
    renderer.setSize(w, h);
    setHeight(h);
    setWidth(w)
  };
  // 重置窗口大小
  useEffect(() => {
    // 页面刚加载完成后获取浏览器窗口的大小
    let h = window.innerHeight;
    setHeight(h)
    let w = window.innerWidth;
    setWidth(w)
    // 页面变化时获取浏览器窗口的大小 
    window.addEventListener('resize', resizeUpdate);
    return () => {
      // 组件销毁时移除监听事件
      window.removeEventListener('resize', resizeUpdate);
    }
  }, [])
  • 4,对相应的变量进行命名
  let scene, camera, renderer, controls // 场景,相机,渲染器,控制器
  let donuts; //后面甜甜圈旋转时的变量
  let mixer; // 甜甜圈播放动画是的变量
  • 5, 初始化/销毁 应用
useEffect(() => {
    // 创建场景
    scene = new THREE.Scene();
    // // 创建相机
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 10);
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(width, height); //设置渲染器尺寸
    document.getElementById('container').appendChild(renderer.domElement);

    camera.position.set(0.3, 0.3, 0.5); // 设置相机位置

    controls = new OrbitControls(camera, renderer.domElement);
    const directionLight = new THREE.DirectionalLight(0xffffff, 0.4); //添加平行光
    scene.add(directionLight);

    gltfLoader() // 场景添加glb文件
    rgbeLoader() // 添加背景天空
    animate() // 动画循环

    // 组件销毁时移除app应用
    return () => {
      document.getElementById('container').removeChild(renderer.domElement);
    }
  }, [])
  • 6,三个分离出来的方法
// 场景添加glb文件
const gltfLoader = () => {
    new GLTFLoader().load('../src/resources/models/donuts.glb', (gltf) => {
      scene.add(gltf.scene);
      donuts = gltf.scene;

      mixer = new THREE.AnimationMixer(gltf.scene);
      const clips = gltf.animations; // 播放所有动画
      clips.forEach((clip) => {
        const action = mixer.clipAction(clip);
        action.loop = THREE.LoopOnce;
        // 停在最后一帧
        action.clampWhenFinished = true;
        action.play();
      });
    })
  }
// 添加背景天空
  const rgbeLoader = () => {
    new RGBELoader()
      .load('../src/resources/sky2.hdr', function (texture) {
        scene.background = texture;
        texture.mapping = THREE.EquirectangularReflectionMapping;
        scene.environment = texture;
        renderer.outputEncoding = THREE.sRGBEncoding;
        renderer.render(scene, camera);
      });
  }
// 动画循环
  const animate = () => {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
    controls.update();
    if (donuts) {
      donuts.rotation.y += 0.01;
    }
    if (mixer) {
      mixer.update(0.02);
    }
  }

6,代码地址

代码在当前github地址,点击可进入查看

7,总结

不管做什么还是要坚持,坚持才会迎来胜利。 如果你有兴趣的话,也可以加入猿创营 (v:dashuailaoyuan),一起交流学习