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),一起交流学习