什么是三维可视化页面
我们常见的网页, 比如掘金知乎微博等都是平面的, 只有长宽两个维度三维可视化页面增加了一个新的维度 -- 深度, 利用 html5 的 webGL 接口, 通过 JavaScript 编程, 在网页中展示三维模型
三维可视化页面的魅力
三维模型相比文字描述或者二维图片, 更加符合日常生活中人们观察世界的感受, 能给用户提供浸入式的产品体验一些复杂的模型及数据, 通过三维可视化的手段, 也能更加准确完整的传递所要表达的信息
一个精彩的 Demo:Solar-System-Motion-Simulator
什么是 Three.js
Three.js 是一个 JavaScript 3D 库, 它将底层的 WebGL 进行了封装简单调用 Three.js 的接口, 就可以快速方便的构建三维可视化页面
我相信你肯定听说过 WebGL, 也听说过 canvas 如果没有听过这两个名词, 可以去 MDN 简单了解一下, 不熟悉 WebGL 和 canvas 不会对学习 Three.js 产生任何阻碍
同样的, 不了解图形学的知识也不会阻碍你使用 Three.js
学习 Three.js 需要什么知识
什么都不需要准确的说, 只要你能看懂中文 (因为本文是中文写作的), 还记得三角函数的知识(不记得你也可以查), 会一点点 JavaScript(因为我也只会一点点 JavaScript) 就够了, 不需要了解 Webpacksass vuereact 等工具和框架的任何知识
开始学习吧
我知道你已经迫不及待地想要敲上几行代码, 一览 Three.js 的魅力了但在这之前, 我们先要弄明白两个关键问题:
三维场景有什么
如何将三维场景展示在二维平面中
想像一下拍摄上图时的样子: 我们先要找到一个拍摄的场地, 另外还得有一台相机然后准备一些棋子一个棋盘, 把棋子和棋盘摆放在场地中, 准备好灯光, 拿起摄像机, 调整好摄像机的位子和角度按下快门
三维可视化网页的创建和这个步骤完全重合
- let scene = new THREE.Scene(); // 准备拍摄的场地
- let camera = new THREE.PerspectiveCamera(fov, aspectRatio, nearPlane, farPlane); // 买了个相机
- camera.position.set(x, y, z) // 把照相机放到 (x,y,z) 处
- camera.lookAt(new THREE.Vector3(0, 0, 0) // 照相机看向坐标(0,0,0)
- let light = new THREE.PointLight(0xff0000, 1, 100); //Light 在有些时候并不是必须的
- let chessman = createChessman(); // 准备好棋子
- chessman.position(a, b, c) // 把棋子放到坐标 (x,y,z) 处
- let chessboard = createChessboard(); // 准备好棋盘
- chessboard.position(e, d, f) // 把棋子放到坐标 (e,d,f) 处
- scene.add(chessman); // 把棋子放到空间中
- scene.add(chessboard); // 把棋盘放到空间中
- let renderer = new THREE.WebGLRenderer(); // 一个渲染器, 不要深究于此
- renderer.render(scene, camera); // 按下快门, 咔嚓~ 拍照完成!
上面的代码中肯定有许多你不明白的地方, 不要担心, 现在只需要感性的认识到搭建一个 Three.js 场景和现实生活中照相的步骤是一样的就可以了
Three.js 的 hello world
学习编程的第一步都是 Hello WorldThree.js 的 Hello World 是在网页中生成一个立方体
现在, 打开你的编辑器, 一步一步跟着做吧
承载场景的元素
- <script src="three.js"><script>
- <div id="container"></div>
先引入 three.js 并在 HTML 中创建一个承载场景的元素 #container
把 Three.js 下载到本地, 或者使用 CDN (https://cdn.bootCSS.com/three.js/90/three.js) 引入
如果你熟悉 NPM 等工具的话, 可以参考这个 import-via-module
生成场景
let scene = new THREE.Scene();
一行代码生成 Three.js 的场景
生成照相机
Three.js 中的照相机有四种: CubeCameraOrthographicCameraPerspectiveCamera 和 StereoCamera 其中常用的是 PerspectiveCamera 和 OrthographicCamera
OrthographicCamera 是正交相机
正交相机拍摄的物体不会被缩放, 平行线永远保持平行多用在物理建模等需要精确尺寸的模型中
PerspectiveCamera 是投影相机
投影相机和人眼一样有近大远小的效果, 平行线不一定平行多用在模拟现实场景的模型中(本文中的模型都会采用投影相机)
关注一下 PerspectiveCamera 的四个参数: fovaspectnearfar
fov 是上图中的 θ , 常用的数字是 45 和 60
aspect 是宽长比, 多用 #container 的比例
near 是离摄影机近的面到摄影机的距离一般为 1 等很小的数字
far 是离摄影机远的面到摄影机的距离一般设置为 100010000 等较大的数字
通过这 4 个参数, 就可以确定一个四棱台在四棱台内的物体会被渲染, 而超出四棱台范围内的物体就被裁剪掉了
let camera = new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,1,1000);
three.js 的坐标系
在生成 camera 的代码中, 你应该注意到 near 被设置为了 1,far 被设置为了 1000 这并不是一个错误, 千万不要写成 1px,1000pxthree.js 的坐标系中表示的距离并不是真实的距离, 而是一种比例关系, 1 和 1000 表示的是 far 比 near 距 camera 远 1000 倍
three.js 的坐标系符合右手定则
可以按照上图去比一下试试
我是看着电脑屏幕记住的: 电脑屏幕向右是 x 正半轴, 电脑屏幕向上是 y 正半轴, 垂直屏幕向外是 z 正半轴
还有一点要记住的是, 新生成的任何物体, 默认初始位置都是几何中心位于(0,0,0)camera 默认看向 z 轴负半轴
camera.position.set(0, 0, 10);
把 camera 往 x 轴正半轴挪一点不然 camera 默认在(0,0,0), 看向 z 轴负半轴是拍不到位于 (0,0,0) 的物体的
先搞个渲染器
渲染器这一块比较复杂, 还好 three.js 做了十分彻底的封装我们只需要用就 ok 了在 Three.js 中, 你只会用到 WebGLRenderer
- var renderer = new THREE.WebGLRenderer();
- renderer.setSize(window.innerWidth,window.innerHeight);
把 renderer 的大小设置为与 scene 一致是一个不错的选择 如果你在 MDN 查了 WebGL 相关的资料的话, 一定知道需要在 < canvas > 元素中使用 WebGL
可以借由 Three.js 在 DOM 中引入 < canvas>,
document.getElementById('container').appendChild(renderer.domElement);
也可以显式的在 HTML 页面插入 < canvas> 我比较喜欢前一种, 简单一些
准备就绪, 开始产生立方体
如果前面的步骤都没问题了, 生成一个立方体就是一瞬间的事情
- let cubeGeometry = new THREE.BoxGeometry(1,1,1);
- let cubeMaterial = new THREE.MeshBasicMaterial({
- color:0xff0000;
- });
- let cube = new THREE.Mesh(cubeGeometry,cubeMaterial);
- scene.add(cube);
- renderer.render(scene,camera);
- super fast !!!
注意 .BoxGeometry, 你可能会在较老的资料上看到是 .CubeGeometry, 那是很久以前的事情了现在生成长方体 / 立方体用 .BoxGeometry
在 Three.js 中生成一个物体只需要三步:
设置物体的形状
设置物体的材质
把形状和材质结合起来
最后要把生成的物体通过 scene 的 add()方法添加到 scene 中 renderer 直接渲染 scene 和 camera 就 OK 了
至于什么是形状, 什么是材质, 如何把形状和材质结合起来, 又是很长一部分内容我会放在其它部分详细讲解
一些可能遇到的问题
1. 页面没有效果检查浏览器是否支持 WebGL
在 JS 文件中引入
- if (Detector.webgl) {
- // Initiate function or other initializations here
- animate();
- } else {
- var warning = Detector.getWebGLErrorMessage();
- document.getElementById('container').appendChild(warning);
- }
2. 页面没有效果检查浏览器 console 是否报错
3. 页面没有效果检查 Three.js 资源是否成功引用
来源: https://juejin.im/post/5ab07d186fb9a028b92cf79d