【Threejs】代码+图文带你快速上手

前言

  大家好,我是南木元元,热衷分享有趣实用的文章,希望大家多多支持,一起进步!

  个人主页:南木元元

在上篇文章中,我们已经介绍了three.js,并使用vite搭建了three.js的开发环境,实现了第一个3D场景。本文就来继续分享一下three.js的相关知识,通过代码+图文带你快速上手three.js。


目录

Threejs三维坐标系

轨道控制器OrbitControls

让物体动起来

添加光源

加载纹理

集成Gui工具

dat.gui.js库

引入dat.gui.js

基本使用

结语


Threejs三维坐标系

three.js使用的是右手坐标系,这源于OpenGL默认情况下,也是右手坐标系。x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外。

【Threejs】代码+图文带你快速上手_第1张图片

为了便于观察,threejs允许我们创建一个辅助坐标系:

// 创建辅助观察坐标系:红色为x轴,绿色为y轴,蓝色为z轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);

效果如下:

【Threejs】代码+图文带你快速上手_第2张图片

  • 渲染优化

上述会看到明显的锯齿,我们可以通过给渲染器设置一些配置,改善渲染效果。

// 创建渲染器,设置渲染器锯齿属性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。

【Threejs】代码+图文带你快速上手_第3张图片

// 点光源
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提供的网格材质,有的受光照影响,有的不受光照影响。

【Threejs】代码+图文带你快速上手_第4张图片

所以我们这里把材质改为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库

如果每次通过修改代码的方式修改属性值效率太低,这里我们可以集成一种Gui工具dat.gui.js,通过UI交互界面来修改方便很多。

dat.gui.js库:一个前端js库,用于快速创建控制三维场景的UI交互界面。

引入dat.gui.js

我们可以通过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();

结语

如果此文对你有帮助的话,欢迎关注点赞、⭐收藏✍️评论支持一下博主~     

你可能感兴趣的:(#,three.js,three.js,3d,可视化)