学习总结初衷:
1. 公司项目需要。
2. 做一下笔记,方便以后学习查看,好记性不如烂笔头;也能筑固基础,加深印象。
3. 现在国内关于Three.js的学习资料很少,总结一下多多少少也能给有需要的小伙伴一些帮助。
一、前言
1. 什么是three.js?你将它理解成three+js,three表示3D的意思,js表示JavaScript的意思,合起来,three.js就是使用JavaScript来写3D程序的意思。three.js是基于WebGL的一个运行在浏览器上的开源框架,使得WebGL的使用更方便、快捷,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象。
2. three.js是基于webGL封装的库,保留了webGL的灵活性,开源免费,可以满足大部分的3D需求。
二、学习参考资料
官网:Three.js – JavaScript 3D Library
官网中文文档(通过 三方式本地搭建three.js官网):three.js docs
学习推荐:
郭隆邦_技术博客:WebGL
webGL中文网:WebGL中文网
三、本地搭建three.js官网
第一步:进入官网Three.js – JavaScript 3D Library
第二步:点击code中的github
第三步:dev分支—- code中Download ZIP
第四步:vscode编辑器打开—- npm install 下载依赖包、打开package.json查看运行方式(这里是 npm start 如图)
第五步:打开上方运行出的https地址可以查看到:
three.js-dev目录结构示意图:
第六步:点击three.js-dev目录 中的 docs 文件夹
四、three.js安装引入
以下的例子都是按照npm的方式安装的
五、基础知识
1、右手坐标系
three.js中使用的都是右手坐标系,即图示右手大拇指指向x轴正向,食指指向y轴正向,中指指向z轴正向,这样的一个三维坐标系。具体来说,x,y坐标用于控制浏览器平面,如果不改变z坐标则物体只在浏览器平面上运动,因此z坐标控制的就是远近。
2、整个程序解构:
程序整个运行过程就是:场景 —— 相机 —— 渲染器
六、three.js核心组成
1、场景(secne)
/* *1. 创建场景-- 放置物体对象的环境 */const scene = new THREE.Scene();
2、相机(camera)
(1)、创建透视相机,也称投影相机(PerspectiveCamera)–用来模拟人眼所看到的景象
/* * 2.创建相机(这里是 透视摄像机--用来模拟人眼所看到的景象) */const camera = new THREE.PerspectiveCamera(75, // 视野角度window.innerWidth / window.innerHeight, // 长宽比0.1, // 进截面1000 // 远截面);// 设置相机位置camera.position.set(0, 0, 10);scene.add(camera); // 将相机添加到场景中
分析:
const camera = new THREE.PerspectiveCamera(
fov : Number,
aspect : Number,
near : Number,
far : Number
)
fov — 摄像机视锥体垂直视野角度 (相机的拍摄视角大小)
aspect — 摄像机视锥体长宽比 (相机拍摄区域的长宽比)
near — 摄像机视锥体近端面 (相机拍摄范围近端距离)
far — 摄像机视锥体远端面 (相机拍摄范围远端距离)
常用:constcamera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );效果:相机拍摄的画面具有近大远小的特性,距离相机近的物体显示的大,距离相机远的物体显示的小。
ps:在 Three 中是没有「长度单位」这个概念的,它的数值都是根据比例计算得出,因此这里提到的 0.1 或 1000 都没有具体的含义,而是一种相对长度。
可以看到,通过配置透视相机的相关参数,最终被渲染到屏幕上的,是在near
到far
之间,根据fov
的值和物体远近确定渲染高度,再通过aspect
值来确定渲染宽度的。
或者更直观一点显示:
如图中所示,透视投影相机一共有4个参数:fov(视场,一个角度值), Horizonta Field of View(横向视场),Vertical Field of View(纵向视场),Near plane(近面), Far plane(远面);由这几个因素,构成一个六面体的封闭空间,在这个空间内的一切物体可见。在设置参数时,需满足:
横向视场 /纵向视场 = 浏览器窗口的宽/浏览器窗口的高。
(2)、创建正交相机
const camera = new THREE.OrthographicCamera(
left : Number,
right : Number,
top : Number,
bottom : Number,
near : Number,
far : Number
)
left — 摄像机视锥体左侧面。
right — 摄像机视锥体右侧面。
top — 摄像机视锥体上侧面。
bottom — 摄像机视锥体下侧面。
near — 摄像机视锥体近端面 (表示近平面与相机中心点的垂直距离)。
far — 摄像机视锥体远端面 (表示远平面与相机中心点的垂直距离)。效果:相机拍摄的画面是相机对物体的正投影,无论物体远近都不会影响它在相机画面中的大小。
各参数如图所示:
分析与ps:
1、由图可知正交透视相机总共有6个面,其具备的特点是:场景中远处的物体和近处的物体是一样大的,它并不像我们现实生活中看场景那样,远小近大,而是远近皆一样大;6个面分别为left(左面),right(右面),top(顶面),bottom(底面),near(近面),near(远面),这六个面组成一个封闭的矩形空间,在这个空间内的一切物体都可见。在设置其参数的时候,下面的关系式一定要成立:
|left/right|= 1,|top/buttom|= 1
2、由图可见near与far的值应为正值,且far>near。如果最后两个值是(0,0),也就是near和far值相同了,视景体深度没有了,整个视景体都被压成个平面了,就会显示不正确。
将摄像机聚焦在指定点
注意: 默认相机的位置在(0,0,0)点处,即场景中心,如果物体也在该点则拍摄不到,所以我们要适当调整相机的位置
使用camera.position.set(x, y, z)来设置摄像机的位置。
使用camera.lookAt(new THREE.Vector3(x, y, z))来改变摄像机的指向位置。
例如,指向场景中心:camera.lookAt(scene.position)
总结:
透视相机(PerspectiveCamera):近大远小
正交相机(OrthographicCamera):远近一致
3、渲染器(render)
有了场景和相机,我们还需要渲染器把对应的场景用对应的相机可见渲染出来,因此渲染器需要传入场景和相机参数。
/* *3.初始化渲染器-设置渲染器大小及挂载元素 */const renderer = new THREE.WebGL1Renderer();// 设置渲染器尺寸renderer.setSize(window.innerWidth, window.innerHeight);// 清除颜色,第二个参数为 0 表示完全透明,适用于需要透出背景的场景// renderer.setClearColor(0x000000, 0);// 将webgl渲染的canvas内容添加到bodydocument.body.appendChild(renderer.domElement);// 使用渲染器,通过相机将场景渲染出来renderer.render(scene, camera);
const renderer = new THREE.WebGLRenderer();
document.body.appendChild( renderer.domElement );
实例化一个渲染器,然后将它的dom元素插入到body中,即插入了一个canvas。也可以将其插入到其他元素中,比如html中有一个,就可以写成
document.getElementById(‘canvas’).appendChild( renderer.domElement );这样场景就在这个div中加载了。注意:设置渲染器的大小,常用的为renderer.setSize( window.innerWidth, window.innerHeight );,如果不设就是默认大小
现在 场景、相机、渲染器都有了,就差需要展示的对象了,下面就来介绍一下物体(几何体)对象
4、物体对象 — 包含 几何体(Geometry)、材质(Material)、网格对象(Mesh)
/* *添加物体 */const cubeGeometry = new THREE.BoxGeometry(1, 1, 1); // 创建几何体对象 -- 立方体const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // 创建材质const cube = new THREE.Mesh(cubeGeometry, cubeMaterial); //创建网格,网格=对象+材质scene.add(cube); // 将物体添加到场景中
几何体(Geometry)
Three 提供了多种类型的几何体,可以分为二维网格和三维网格。二维网格顾名思义只有两个维度,可以通过这种几何体创建简单的二维平面;三维网格允许你定义三维物体;在 Three 中定义一个几何体十分简单,只需要选择需要的几何体并传入相应参数创建即可。常用几何体如下:
更多几何体及相关用法请见官网:官网:Three.js – JavaScript 3D Library
三维:
构造函数 | 说明 |
---|---|
THREE.BoxGeometry | 立方几何体 |
THREE.SphereGeometry | 球几何体 |
HREE.ConeGeometry | 圆锥几何体 |
HREE.CylinderGeometry | 圆柱几何体 |
HREE.TorusGeometry | 圆环几何体 |
HREE.TextGeometry | 文本几何体 |
二维:
材质(Material)
定义材质可以帮助我们决定一个物体在各种环境情况下的具体表现。同样 Three 也提供了多种材质。下面列举几个常用的材质。
更多材质及相关用法请见官网:官网:Three.js – JavaScript 3D Library
创建材质:
const basicMaterial = new THREE.MeshBasicMaterial({ color: 0x666666 });const lambertMaterial = new THREE.MeshLambertMaterial({ color: 0x666666 });const phongMaterial = new THREE.MeshPhongMaterial({ color: 0x666666 });const wireMaterial = new THREE.MeshBasicMaterial({ wireframe: true, color: 0x666666 });
网格对象(Mesh)
需要将几何体和材质添加到场景中,还需要依赖 Mesh。Mesh 是用来定义材质和几何体之间是如何粘合的。
或者说 3d物体由点->线->面组成,mesh网格就是由大量的面组成的网格图,再加上材质就创建成物体,新建几何体和材质后,就可以将该物体对象添加到场景中了。
创建网格对象可以应用一个或多个材质和几何体。
网格 = 几何体 + 材质
创建几何体相同材质不同的网格对象:
const cube = new THREE.Mesh(cubeGeometry, basicMaterial);const cubePhong = new THREE.Mesh(cubeGeometry, phongMaterial);scene.add(cube, cubePhong);
创建材质相同几何体不同的网格对象:
const cube = new THREE.Mesh(cubeGeometry, basicMaterial);const sphere = new THREE.Mesh(sphereGeometry, basicMaterial);scene.add(cube, sphere);
创建拥有多个材质几何体的网格对象:
const phongMaterial = new THREE.MeshPhongMaterial({ color: 0x666666 });const cubeMeshPhong = new THREE.Mesh(cubeGeometry, cubePhongMaterial);const cubeMeshWire = new THREE.Mesh(cubeGeometry, wireMaterial);// 网格对象新增材质cubeMeshPhong.add(cubeMeshWire);scene.add(cubeMeshPhong);
有了场景、相机和渲染器,再加上物体,我们已经可以看到初步的效果了。但3D世界里,静止的物体多无趣啊。于是我们尝试加入动画效果.。
5、帧循环,游戏循环:
// 定义循环渲染方法function render() {// 物体动态移动位置 -- 沿x轴cube.position.x += 0.01;if (cube.position.x > 7) {cube.position.x = 0;}// 物体动态旋转cube.rotateX(0.01); //每次绕y轴旋转0.01弧度// cube.rotation.x += 0.01;renderer.render(scene, camera); // 执行渲染操作requestAnimationFrame(render); // 渲染下一帧的时候就会调用render函数}render();
6、灯光
1、灯光用来为场景添加光源,照明物体,虽然部分材质(如MeshBasicMaterial)不会受到灯光的影响,无需添加灯光也能看见,但是这种材质的物体没有质感,也没有明暗变化;如果想让物体更接近真实场景的物体,就需要可以被灯光影响的材质,如MeshLamberMaterial,如果不添加光源就无法看到物体。
2、灯光分为点光源,线光源,面光源等,可依据实际场景进行选择,正如真实场景的光源情况,three.js允许添加多个灯光从而对真实场景进行模拟。
添加灯光方法:
const light = new THREE.AmbientLight( 0x404040 );
scene.add( light );
7、辅助线
// 三维辅助线(长度)-- 三维坐标系const axisHelper = new THREE.AxisHelper(7);scene.add(axisHelper); // 网格辅助线 -- 地表格(网格宽度、等分数、中心线颜色,网格线颜色)const grid = new THREE.GridHelper(1000, 1000, 0xff0000, 0xff0000);scene.add(grid);
8、轨道控制器查看物体(鼠标控制)
// 导入轨道控制器import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";/** * 创建轨道控制器(OrbitControls) * 可以使得相机围绕目标进行轨道运动 */const controls = new OrbitControls(camera, renderer.domElement);controls.autoRotate = true; // 相机是否自动旋转controls.autoRotateSpeed = 3; // 自转速度// 设置控制器阻尼,让控制器更有真实效果,必须在动画循环render()中调用controls.update()()controls.enableDamping = true;
PS:设置控制器阻尼,必须在动画循环render()中调用controls.update()()
// 定义循环渲染方法function render() {// 动态移动位置cube.position.x += 0.01;if (cube.position.x > 7) {cube.position.x = 0;}//动态旋转cube.rotateX(0.01); //每次绕y轴旋转0.01弧度// cube.rotation.x += 0.01;renderer.render(scene, camera); // 执行渲染操作controls.update(); // controls控制特定属性时加requestAnimationFrame(render); // 渲染下一帧的时候就会调用render函数}render();
能把上面的看完,基本上自己就可以实现3d效果了,那下面就简单给出几个例子帮助理解一下!
七、拓展
根据尺寸变化实现自适应画面
// 监听尺寸变化实现自适应画面window.addEventListener("resize", () => {// console.log("画面变化了");// 更新摄像头camera.aspect = window.innerWidth / window.innerHeight;// 更新摄像机的投影矩阵camera.updateProjectionMatrix();// 更新渲染器renderer.setSize(window.innerWidth, window.innerHeight);// 设置渲染器的像素比renderer.setPixelRatio(window.devicePixelRatio);});
示例一:通过script标签来引入three.js比较简单,适合简单的练习,快速体验。
My First three.js app body{margin:0;} // 场景const scene = new THREE.Scene(); // 相机const camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,000); // 渲染器const renderer = new THREE.WebGLRenderer();renderer.setSize(window.innerWidth,window.innerHeight);document.body.appendChild(renderer.domElement); // 物体const geometry = new THREE.BoxGeometry(1,1,1);const material = new THREE.MeshBasicMaterial({color:0x00ff00});const cube = new THREE.Mesh(geometry,material);scene.add(cube); camera.position.z = 5;// 动画const animate = function(){ requestAnimationFrame(animate); cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render(scene,camera);//渲染};animate();
这是一张静态图片,动态效果自己运行一下就能出现啦!
示例二:通过script标签和CDN来引入three.min.js,实现一个旋转的球:
My First three.js app2 * {margin: 0;padding: 0;}//创建场景const scene = new THREE.Scene();//创建透视投影相机,视角45度,画幅比例 宽比高,近平面距离0.1,远平面1000const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);//创建渲染器const renderer = new THREE.WebGLRenderer();//渲染器canvas宽高设为与窗口一致renderer.setSize(window.innerWidth, window.innerHeight);//将渲染器对应的dom元素添加到body中document.body.appendChild(renderer.domElement);//定义一个几何体const geometry = new THREE.SphereGeometry(3, 30, 30);//定义一种材质,显示为线框const material = new THREE.MeshBasicMaterial({ color: 0xB3DD, wireframe: true });//网孔(Mesh)是用来承载几何模型的一个对象,可以把材料应用到它上面const ball = new THREE.Mesh(geometry, material);//把几何模型添加到场景中,对象被添加到原点(0,0,0)坐标。scene.add(ball);//移动相机位置camera.position.z = 8;function render() {//渲染循环,以每秒60次的频率来绘制场景requestAnimationFrame(render);//设置球体绕y轴旋转ball.rotation.y += 0.005;renderer.render(scene, camera);}render();
这是一张静态图片,动态效果自己运行一下就能出现啦!
示例三、通过npman安装,在vue2项目中使用:
import * as THREE from "three";// 导入轨道控制器import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";/**1. 创建场景*/const scene = new THREE.Scene();/** 2.创建相机(这里是 透视摄像机--用来模拟人眼所看到的景象)*/const camera = new THREE.PerspectiveCamera(75, // 视野角度window.innerWidth / window.innerHeight, // 长宽比0.1, // 进截面1000 // 远截面);// 设置相机位置camera.position.set(0, 0, 10);scene.add(camera); // 将相机添加到场景中/** 添加物体*/// 创建几何体对象const cubeGeometry = new THREE.BoxGeometry(1.5, 1.5, 1.5);// 设置材质const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });// 网格 = 对象 + 材质const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);scene.add(cube); // 将物体添加到场景中// 修改物体的位置(想实现动态的移动就定义在render()方法中)// cube.position.set(7, 0, 0);// 或者// cube.position.x = 7;// 缩放// cube.scale.set(3, 2, 1);// 或者// cube.scale.x = 3;// 旋转(参数有4个,前面是xyz,第四个可以控制旋转的顺序)--想实现动态的旋转就定义在render()方法中// cube.rotation.set(Math.PI / 4, 0, 0, "XZY");// 或者// cube.rotateX(Math.PI / 4);// 或者cube.rotation.x = Math.PI / 4;/*** 3.初始化渲染器*/const renderer = new THREE.WebGL1Renderer();// 设置渲染器尺寸renderer.setSize(window.innerWidth, window.innerHeight);// 将webgl渲染的canvas内容添加到bodydocument.body.appendChild(renderer.domElement);// 使用渲染器,通过相机将场景渲染出来// renderer.render(scene, camera);/*** 创建轨道控制器(OrbitControls)* 可以使得相机围绕目标进行轨道运动*/const controls = new OrbitControls(camera, renderer.domElement);controls.autoRotate = true; // 相机是否自动旋转controls.autoRotateSpeed = 3; // 自转速度/*** 辅助三维坐标系* 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.*/var axesHelper = new THREE.AxesHelper(7);scene.add(axesHelper);// 定义循环渲染方法function render() {// 动态移动位置cube.position.x += 0.01;if (cube.position.x > 7) {cube.position.x = 0;}//动态旋转cube.rotateX(0.01); //每次绕y轴旋转0.01弧度// cube.rotation.x += 0.01;renderer.render(scene, camera); // 执行渲染操作controls.update(); // controls控制特定属性时加requestAnimationFrame(render); // 渲染下一帧的时候就会调用render函数}render();// 监听尺寸变化实现自适应画面window.addEventListener("resize", () => {// console.log("画面变化了");// 更新摄像头camera.aspect = window.innerWidth / window.innerHeight;// 更新摄像机的投影矩阵camera.updateProjectionMatrix();// 更新渲染器renderer.setSize(window.innerWidth, window.innerHeight);// 设置渲染器的像素比renderer.setPixelRatio(window.devicePixelRatio);});
这是一张静态图片,动态效果自己运行一下就能出现啦!