当前位置: 首页 > news >正文

从零学习three.js官方文档(一)——基本篇

个人简介

👀个人主页: 前端杂货铺
🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍍前端面试宝典 🎨100个小功能 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js实战 🍒Three.js
🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧

文章目录

  • 基本篇
    • 基础
      • three.js的应用结构
      • Hello Cube
    • 响应式设计

基本篇

Three.js 是一个尽可能简化网页端获取 3D 内容的库。three.js 其实是使用 WebGL 来绘制三维效果的。 WebGL 是一个只能画点、线和三角形的非常底层的系统。想要用WebGL 来做一些实用的东西通常需要大量的代码, 这就是 Three.js 的用武之地。

GitHub · 项目源码

基础

three.js的应用结构

此处仅需简单了解,随着学习的深入,相关概念就逐渐清晰了。

  • Renderer:渲染器。将摄像机视椎体中的三维场景渲染成一个二维图片显示在画布上。
  • Scene:场景。场景能够让你在什么地方、摆放什么东西来交给three.js来渲染,这是你放置物体、灯光和摄像机的地方。
  • Camera:相机。定义观察者在3D场景中的位置、视角及渲染范围,直接影响用户观察场景的视觉效果。
  • Mesh:网格。网格对象可以理解为用一种特定的材质(Material)来绘制的一个特定的几何体(Geometry)。
  • Geometry:几何体。代表一些几何体,如球体、立方体、平面、狗、猫、人、树、建筑等物体的顶点信息。
  • Material:材质。代表绘制几何体的表面属性,包括使用的颜色,和光亮程度。
  • Texture:纹理。表示一幅要么从文件中加载,要么在画布上生成,要么由另一个场景渲染出的图像。
  • Light:光源。代表不同种类的光。

在这里插入图片描述

Hello Cube

GitHub · 提交记录

初始化项目。

npm create vite@latestnpm create vite@latest . -- --template vanillanpm installnpm install threenpm run dev

修改 index.html 文件,添加画布。主体逻辑我们将写在 main.js 中。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><link rel="icon" type="image/svg+xml" href="/vite.svg" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>study-threejs-docs</title></head><body><canvas id="c"></canvas><script type="module" src="/src/main.js"></script></body>
</html>

main.js 中我们先拿到 canvas,然后创建一个 WebGL 渲染器,将所有数据渲染绘制到 canvas 上。

const canvas = document.querySelector('#c');
// antialias - 执行抗锯齿
const renderer = new THREE.WebGLRenderer({antialias: true, canvas});

接下来我们需要一个 透视摄像机(PerspectiveCamera)。

const fov = 75; // 视野范围 75度
const aspect = 2;  // 画布的宽高比
const near = 0.1; // 近平面
const far = 5; // 远平面
// 透视摄像机
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  • fov:视野范围 (field of view) 的缩写。上述代码中是指垂直方向为75度。
  • aspect:画布的宽高比。在默认情况下 画布是300x150像素,所以宽高比为300/150或者说2。
  • near、far:代表近平面和远平面,它们限制了摄像机面朝方向的可绘区域。任何距离小于或超过这个范围的物体都将被裁剪掉 (不绘制)。

这四个参数定义了一个 视锥(frustum),视椎(frustum)是指一个像被削去顶部的金字塔形状。换句话说,可以把"视椎(frustum)"想象成其他三维形状如球体、立方体、棱柱体、截椎体。

在这里插入图片描述

近平面和远平面的高度由视野范围决定,宽度由视野范围和宽高比决定。

视椎体内部的物体将被绘制,视椎体外的东西将不会被绘制。

摄像机默认指向Z轴负方向,上方向朝向Y轴正方向。我们将会把立方体放置在坐标原点,所以我们需要往后移一下摄像机才能显示出物体

camera.position.z = 2;

在这里插入图片描述

摄像机的位置在z = 2。它朝向Z轴负方向。我们的视椎体范围从摄像机前方0.1到5。因为这张图是俯视图,视野范围会受到宽高比的影响。画布的宽度是高度的两倍,所以水平视角会比我们设置的垂直视角75度要大。

接下来,我们需要创建一个场景 Scene,three.js 绘制的东西都需要加入到 scene 中。

const scene = new THREE.Scene();

然后我们创建一个包含盒子信息的立方几何体(BoxGeometry)。几乎所有希望在three.js中显示的物体都需要一个包含了组成三维物体的顶点信息的几何体。

const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);

然后创建一个基本的材质并设置它的颜色. 颜色的值可以用css方式和十六进制来表示。

const material = new THREE.MeshBasicMaterial({color: 0x44aa88});

再创建一个网格(Mesh)对象,它包含了:

  1. 几何体(Geometry)(物体的形状)
  2. 材质(Material)(如何绘制物体,光滑还是平整,什么颜色,什么贴图等等)
  3. 对象在场景中相对于他父对象的位置、朝向、和缩放。下面的代码中父对象即为场景对象。

我们常见的体都需要 Mesh 出来,Mesh网格需要几何和材质。

const cube = new THREE.Mesh(geometry, material);

我们将网格添加到场景中。

scene.add(cube);

最后将场景和摄像机传递给渲染器来渲染出整个场景。

renderer.render(scene, camera);

最终,我们得到的效果如下。

在这里插入图片描述

很难看出来这是一个三维的立方体,因为我们直视Z轴的负方向并且立方体和坐标轴是对齐的,所以我们只能看到一个面。

我们来让立方体旋转起来,以便更好的在三维环境中显示。为了让它动起来我们需要用到一个渲染循环函数 requestAnimationFrame

requestAnimationFrame 函数会告诉浏览器你需要显示动画。传入一个函数作为回调函数。本例中的函数是 render 函数。如果你更新了跟页面显示有关的任何东西,浏览器会调用你传入的函数来重新渲染页面。

// 渲染函数
function render(time) {// 将时间单位变为秒time *= 0.001;cube.rotation.x = time;cube.rotation.y = time;// 使用渲染器渲染renderer.render(scene, camera);requestAnimationFrame(render);
}
requestAnimationFrame(render);

在这里插入图片描述

接下来我们添加一下光照效果,让它看起来更像三维的。

平行光有一个位置和目标点。默认值都为(0, 0, 0)。我们这里 将灯光的位置设为(-1, 2, 4),让它位于摄像机前面稍微左上方一点的地方。目标点还是(0, 0, 0),让它朝向坐标原点方向。

// 设置光照颜色-白色
const color = 0xffffff;
// 设置光照强度
const intensity = 3;
// 从上方照射的白色平行光,强度为 0.5
const light = new THREE.DirectionalLight(color, intensity);
// 设置光源位置
light.position.set(-1, 2, 4);
// 将光源添加到场景中
scene.add(light);

我们还需要改变下立方体的材质。MeshBasicMaterial材质不会受到灯光的影响。我们将他改成会受灯光影响的 MeshPhongMaterial 材质。

const material = new THREE.MeshPhongMaterial({color: 0x44aa88});  // 绿蓝色

在这里插入图片描述

当前我们的项目结构如下:

在这里插入图片描述

下面,我们再添加两个立方体。每个立方体会引用同一个几何体和不同的材质,这样每个立方体将会是不同的颜色。

GitHub · 提交记录

首先我们创建一个根据指定的颜色生成新材质的函数。它会根据指定的几何体生成对应网格,然后将网格添加进场景并设置其X轴的位置

/*** 创建立方体* @param {*} geometry 几何体* @param {*} color 颜色* @param {*} x 位置* @returns 网格模型*/
function makeInstance(geometry, color, x) {const material = new THREE.MeshPhongMaterial({ color });const cube = new THREE.Mesh(geometry, material);scene.add(cube);cube.position.x = x;return cube;
}

将用三种不同的颜色和X轴位置调用三次函数,将生成的网格实例存在一个数组中。

// 创建三个立方体实例,并设置不同的颜色和位置
const cubes = [makeInstance(geometry, 0x44aa88, 0),makeInstance(geometry, 0x8844aa, -2),makeInstance(geometry, 0xaa8844, 2),
];

最后我们将在渲染函数中旋转三个立方体。我们给每个立方体设置了稍微不同的旋转角度。

// 渲染函数
function render(time) {// 将时间转换为秒time *= 0.001;// 设置不同的立方体旋转速度cubes.forEach((cube, ndx) => {const speed = 1 + ndx * 0.1;const rot = time * speed;cube.rotation.x = rot;cube.rotation.y = rot;});renderer.render(scene, camera);requestAnimationFrame(render);
}

在这里插入图片描述

此时,我们的项目结构如下:

在这里插入图片描述

我们有三个网格(Mesh)引用了相同的立方几何体(BoxGeometry)。每个网格(Mesh)引用了一个单独的 MeshPhongMaterial 材质来显示不同的颜色。


响应式设计

GitHub · 提交记录

网页的响应式是指让其在桌面、平板及手机等不同尺寸的屏幕上显示良好。

我们添加CSS来让 canvas 填充整个页面。

html,
body {margin: 0;height: 100%;
}
#c {width: 100%;height: 100%;display: block;
}

在这里插入图片描述

此时,canvas充满了整个页面。但不难发现我们的立方体被拉伸了,并且立方体看起来分辨率太低或者说块状化或者有点模糊。

我们首先解决拉伸问题。为此我们要将相机的宽高比设置为canvas的宽高比。 我们可以通过canvas 的 clientWidth 和 clientHeight 属性来实现。

render() 方法中添加如下代码,动态设置相机的宽高比,防止动画变形。并更新相机的投影矩阵,使设置生效。

  // 获取渲染器的 canvas 元素const canvas = renderer.domElement;// 根据 canvas 的实际宽高比动态设置相机的宽高比,防止画面变形camera.aspect = canvas.clientWidth / canvas.clientHeight;// 更新相机的投影矩阵,使设置生效camera.updateProjectionMatrix();

在这里插入图片描述

接下来,我们解决块状化的问题。

canvas 元素有两个尺寸。一个是 canvas 在页面上的显示尺寸, 是我们用 CSS 来设置的。另一个尺寸是 canvas本身像素的数量

一个 canvas 的内部尺寸,它的分辨率,通常被叫做绘图缓冲区(drawingbuffer)尺寸。 在 three.js 中我们可以通过调用 renderer.setSize设置canvas的绘图缓冲区

在这里插入图片描述

我们写一个函数来检查渲染器的canvas尺寸是不是和canvas的显示尺寸不一样 ,如果不一样就设置它。

创建 resetSize.js 文件,暴露 resizeRendererToDisplaySize 方法。

// 检查并调整渲染器的尺寸以适应 canvas 的实际显示尺寸
export default function resizeRendererToDisplaySize(renderer) {// 获取渲染器绑定的 canvas 元素const canvas = renderer.domElement;// 获取 canvas 的实际显示宽高const width = canvas.clientWidth;const height = canvas.clientHeight;// 判断 canvas 的绘图缓冲区尺寸是否与显示尺寸不一致const needResize = canvas.width !== width || canvas.height !== height;if (needResize) {// 如果不一致,则调整渲染器尺寸以匹配显示尺寸renderer.setSize(width, height, false);}// 返回是否进行了尺寸调整return needResize;
}

render 的时候我们检查 canvas 是否真的需要调整大小,如果需要我们再做调整。

  // 调整渲染器的尺寸以适应 canvas 的实际显示尺寸if (resizeRendererToDisplaySize(renderer)) {// 获取渲染器的 canvas 元素const canvas = renderer.domElement;// 根据 canvas 的实际宽高比动态设置相机的宽高比,防止画面变形camera.aspect = canvas.clientWidth / canvas.clientHeight;// 更新相机的投影矩阵,使设置生效camera.updateProjectionMatrix();}

此时,我们即可得到高分辨率且不变形的几何体了,妙哉。

在这里插入图片描述


好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!

参考资料:

  1. 百度 · 百科
  2. Three.js · 官方文档
  3. Three.js 权威指南
  4. VS Code · Copilot

在这里插入图片描述


http://www.lryc.cn/news/614877.html

相关文章:

  • 校招秋招春招实习快手在线测评快手测评题库|测评解析和攻略|题库分享
  • 【linux基础】Linux目录和Windows目录的区别
  • 免费开发数字人API
  • Milvus 向量数据库基础操作解析
  • Kubernetes 无法识别你定义的 `CronJob` 资源*逐步解决方案
  • 不足3个细胞怎么做差异分析?
  • 目标检测数据集 - 足球场广告横幅检测数据集下载「包含VOC、COCO、YOLO三种格式」
  • 【Datawhale AI夏令营】从Baseline到SOTA:深度剖析金融问答RAG管道优化之路
  • [CUDA] CUTLASS | `CuTe DSL` 创新
  • 《TypeScript搭建的认知桥梁:游戏化学习应用的深层架构》
  • day22|学习前端ts语言
  • Javaweb - 14.1 - 前端工程化
  • 政府数字化大屏系统 - Flask实现方案
  • 使用LangGraph从零构建多智能体AI系统:实现智能协作的完整指南
  • OpenAI开源大模型 GPT-OSS 开放权重语言模型解析:技术特性、部署应用及产业影响
  • HTML金色流星雨
  • 人工智能技术发展历史演变
  • Android中RecyclerView基本使用
  • 深入理解Qt事件处理机制
  • C++实现MATLAB矩阵计算程序
  • 【Redis7.x】docker配置主从+sentinel监控遇到的问题与解决
  • Debian 系统更新命令
  • PDF 转 HTML API 数据接口
  • 免费PDF编辑软件 pdf24-creator 及其安装包
  • 力扣-74.搜索二维矩阵
  • MyBatis联合查询 - 注解篇
  • 【洛谷题单】--分支结构(三)
  • JAVA基础-使用BIO / NIO实现聊天室功能
  • 一文详解 C++ 继承体系
  • AI_RAG