前言
?📫 大家好,我是南木元元,热衷分享有趣实用的文章,希望大家多多支持,一起进步!
?🍅?个人主页:南木元元
在上篇文章中,我们已经介绍了three.js,并使用vite搭建了three.js的开发环境,实现了第一个3D场景。本文就来继续分享一下three.js的相关知识,通过代码+图文带你快速上手three.js。
目录
three.js使用的是右手坐标系,这源于OpenGL默认情况下,也是右手坐标系。x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外。
为了便于观察,threejs允许我们创建一个辅助坐标系:
// 创建辅助观察坐标系:红色为x轴,绿色为y轴,蓝色为z轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
效果如下:
上述会看到明显的锯齿,我们可以通过给渲染器设置一些配置,改善渲染效果。
// 创建渲染器,设置渲染器锯齿属性antialias为true表示开启抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置设备像素比为当前屏幕对应的设备像素比,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
完整代码:
import * as THREE from "three";
// 创建场景
const scene = new THREE.Scene();
// 创建透视相机
const fov = 45;
const aspect = window.innerWidth / window.innerHeight;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// 定位相机
camera.position.set(200, 300, 200);
camera.lookAt(0, 0, 0);
// 创建立方体(几何+材质)
const geometry = new THREE.BoxGeometry(10, 10, 10);
const material = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh(geometry, material);
// 添加到场景
scene.add(mesh);
// 创建辅助观察坐标系:红色为x轴,绿色为y轴,蓝色为z轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
// 创建渲染器,设置渲染器锯齿属性antialias为true表示开启抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置设备像素比为当前屏幕对应的设备像素比,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 渲染
renderer.render(scene, camera);
上面x轴和z轴的方向和预期不一致是因为我们这里把相机定位到了(200, 300, 200)导致的。为了方便调试与观察,我们可以使用轨道控制器(OrbitControls
)控制相机的位置。
OrbitControls
轨道控制器可以改变相机在空间坐标系中的位置,进而方便从不同的角度观察物体。
效果:
1.导入组件
OrbitControls
是一个附加组件,在使用之前需要先导入。
// 导入轨道控制器
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
2.创建控制器
// 创建相机轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
3.使用OrbitControls
OrbitControls本质上就是改变相机的参数如坐标位置,改变坐标后,需要重新渲染。
// 监听鼠标、键盘事件, 如果OrbitControls改变了相机参数,则重新渲染
controls.addEventListener("change", function () {
renderer.render(scene, camera);
});
完整代码:
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
// 创建场景
const scene = new THREE.Scene();
// 创建透视相机
const fov = 45;
const aspect = window.innerWidth / window.innerHeight;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// 定位相机
camera.position.set(200, 300, 200);
camera.lookAt(0, 0, 0);
// 创建立方体(几何+材质)
const geometry = new THREE.BoxGeometry(10, 10, 10);
const material = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh(geometry, material);
// 添加到场景
scene.add(mesh);
// 创建辅助观察坐标系:红色为x轴,绿色为y轴,蓝色为z轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
// 创建渲染器,设置渲染器锯齿属性antialias为true表示开启抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置设备像素比为当前屏幕对应的设备像素比,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 渲染
renderer.render(scene, camera);
// 创建相机轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 监听鼠标、键盘事件, 如果OrbitControls改变了相机参数,则重新渲染
controls.addEventListener("change", function () {
renderer.render(scene, camera);
});
接下来,我们添加一个旋转的动画,让物体动起来。
//循环渲染
function animation() {
// 改变角度, 每次绕y轴旋转0.01弧度
mesh.rotateY(0.01);
// 重新渲染
renderer.render(scene, camera);
// 下一帧渲染回调
requestAnimationFrame(animation);
}
animation();
上述代码实现了一个周期性的渲染,不断旋转角度和重绘。核心是requestAnimationFrame这个方法, 它告诉浏览器你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。实现效果:
完整代码:
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
// 创建场景
const scene = new THREE.Scene();
// 创建透视相机
const fov = 45;
const aspect = window.innerWidth / window.innerHeight;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// 定位相机
camera.position.set(200, 300, 200);
camera.lookAt(0, 0, 0);
// 创建立方体(几何+材质)
const geometry = new THREE.BoxGeometry(10, 10, 10);
const material = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh(geometry, material);
// 添加到场景
scene.add(mesh);
// 创建辅助观察坐标系:红色为x轴,绿色为y轴,蓝色为z轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
// 创建渲染器,设置渲染器锯齿属性antialias为true表示开启抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置设备像素比为当前屏幕对应的设备像素比,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建相机轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 循环渲染
function animation() {
// 改变角度, 每次绕y轴旋转0.01弧度
mesh.rotateY(0.01);
// 更新轨道控制器,以响应用户交互
controls.update();
// 重新渲染
renderer.render(scene, camera);
// 下一帧渲染回调
requestAnimationFrame(animation);
}
animation();
three.js中的光照分为两种类型:
其中直接光照有三种重要的光源:点光源PointLight、平行光DirectionalLight、聚光灯SpotLight。
// 点光源
var point = new THREE.PointLight(0xffffff);
point.position.set(400, 200, 300);
scene.add(point);
// 环境光
var ambient = new THREE.AmbientLight(0xffffff);
scene.add(ambient);
上面代码中,添加了一个直接照射的点光源PointLight和间接的环境光AmbientLight。需要注意的是,threejs提供的网格材质,有的受光照影响,有的不受光照影响。
所以我们这里把材质改为MeshStandardMaterial。
const material = new THREE.MeshStandardMaterial();
添加光照后的效果:
除了光照外,对几何体材质添加纹理是常见的操作,我们接下来给立方体添加纹理。
// 引入图片
import texture from "../images/texture.jpg";
// 初始化纹理加载器
const textloader = new THREE.TextureLoader();
// 加载纹理
const material = new THREE.MeshStandardMaterial({
map: textloader.load(texture),
});
效果:
如果每次通过修改代码的方式修改属性值效率太低,这里我们可以集成一种Gui工具dat.gui.js,通过UI交互界面来修改方便很多。
dat.gui.js库:一个前端js库,用于快速创建控制三维场景的UI交互界面。
我们可以通过npm或github方式获得dat.gui.js库,当然为了学习方便,threejs官方案例扩展库中也提供了gui.js,可以直接使用。
// 引入dat.gui.js的一个类GUI
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
创建一个GUI对象,你可以看到浏览器右上角多了一个交互界面。
// 初始化gui
const gui = new GUI();
???????add()方法可以快速创建一个UI交互界面,比如一个拖动条,用来改变一个js对象属性的属性值。
gui.add(cube.position, 'x').min(-10).max(10).step(1)
这里我们可以针对三种基础变换(平移、旋转、缩放)做一个分组。
// 初始化gui
const gui = new GUI();
// 平移
const guiPosition = gui.addFolder("平移");
guiPosition.add(mesh.position, "x").min(-10).max(10).step(1);
guiPosition.add(mesh.position, "y").min(-10).max(10).step(1);
guiPosition.add(mesh.position, "z").min(-10).max(10).step(1);
// 旋转
const guiRotation = gui.addFolder("旋转");
guiRotation.add(mesh.rotation, "x").min(-Math.PI).max(Math.PI).step(0.01);
guiRotation.add(mesh.rotation, "y").min(-Math.PI).max(Math.PI).step(0.01);
guiRotation.add(mesh.rotation, "z").min(-Math.PI).max(Math.PI).step(0.01);
// 缩放
const guiScale = gui.addFolder("缩放");
guiScale.add(mesh.scale, "x").min(1).max(10).step(1);
guiScale.add(mesh.scale, "y").min(1).max(10).step(1);
guiScale.add(mesh.scale, "z").min(1).max(10).step(1);
效果:
完整代码:
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
// 引入dat.gui.js的一个类GUI
import { GUI } from "three/addons/libs/lil-gui.module.min.js";
// 引入图片
import texture from "../images/texture.jpg";
// 创建场景
const scene = new THREE.Scene();
// 创建透视相机
const fov = 45;
const aspect = window.innerWidth / window.innerHeight;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// 定位相机
camera.position.set(200, 300, 200);
camera.lookAt(0, 0, 0);
// 创建立方体(几何+材质)
const geometry = new THREE.BoxGeometry(10, 10, 10);
// 初始化纹理加载器
const textloader = new THREE.TextureLoader();
// 加载纹理
const material = new THREE.MeshStandardMaterial({
map: textloader.load(texture),
});
const mesh = new THREE.Mesh(geometry, material);
// 初始化gui
const gui = new GUI();
// 平移
const guiPosition = gui.addFolder("平移");
guiPosition.add(mesh.position, "x").min(-10).max(10).step(1);
guiPosition.add(mesh.position, "y").min(-10).max(10).step(1);
guiPosition.add(mesh.position, "z").min(-10).max(10).step(1);
// 旋转
const guiRotation = gui.addFolder("旋转");
guiRotation.add(mesh.rotation, "x").min(-Math.PI).max(Math.PI).step(0.01);
guiRotation.add(mesh.rotation, "y").min(-Math.PI).max(Math.PI).step(0.01);
guiRotation.add(mesh.rotation, "z").min(-Math.PI).max(Math.PI).step(0.01);
// 缩放
const guiScale = gui.addFolder("缩放");
guiScale.add(mesh.scale, "x").min(1).max(10).step(1);
guiScale.add(mesh.scale, "y").min(1).max(10).step(1);
guiScale.add(mesh.scale, "z").min(1).max(10).step(1);
// 添加到场景
scene.add(mesh);
// 点光源
var point = new THREE.PointLight(0xffffff);
point.position.set(400, 200, 300);
scene.add(point);
//环境光
var ambient = new THREE.AmbientLight(0xffffff);
scene.add(ambient);
// 创建辅助观察坐标系:红色为x轴,绿色为y轴,蓝色为z轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
// 创建渲染器,设置渲染器锯齿属性antialias为true表示开启抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置设备像素比为当前屏幕对应的设备像素比,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建相机轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
//循环渲染
function animation() {
// 改变角度, 每次绕y轴旋转0.01弧度
mesh.rotateY(0.01);
// 更新轨道控制器,以响应用户交互
controls.update();
// 重新渲染
renderer.render(scene, camera);
// 下一帧渲染回调
requestAnimationFrame(animation);
}
animation();
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、?收藏、??评论,支持一下博主~?????