关于从入门 three.js 到做出 3d 地球这件事

04/06 08:40
阅读数 4.1K

转载自:lulu_up

https://segmentfault.com/a/1190000039647481

开篇介绍

如果你没接触过3d可视化技术, 你也许会认为可视化非常难, 光是一个物体的阴影要如何计算就相当复杂, 但是告诉你个好消息, 阴影的计算都是集成好的, 而我们只要设置好光源的位置,绘制好物体就可以了, 真的没有想象中那么复杂, 本文面向有前端基础,但零可视化基础的同学, 我会从最基础的入门知识说起。

学习可视化方面的技术会让我们对计算机, 对前端技术有更深的理解, 还可以做出更多有趣味的东西来, 本文是我踩了好多坑后总结出来的, 我更清楚一个初入门的小白哪里不懂。

three.jswebgl 的第三方库, 它更适合不太复杂的可视化项目, 而我们要做的3d地球项目使用它来做会更简单, 所以选择了它, 放心后面也会说webgl相关知识 。

当前效果如下:

一. 关于此系列文章

  1. 自食其力:不管是在公司还是网上都有类似的库, 但是当遇到bug或是缺少功能的情况时就会很麻烦, 例如我们公司的FGL库(一个内网绘制3d景象的技术), 它官网上的例子很多都是错的, 使用起来也是一堆问题, 比如无法精准选择某个国家, 点击事件消融等bug。还比如说 Echarts的地球, 它太注重真实感并且用起来有点卡, 以及交互做的不太好。
  2. 直指核心: 去年我通过看书、看文章、看视频认真的学习 three.js, 并做出了3d地球这个项目, 而这个系列文章将会直指做出3d地图的核心知识, 尽量不随意扩散知识面。
  3. 更好入门: 网上的教学文章千篇一律, 点进去阅读完感觉其对于一个 three.js零基础的同学来说都不太好懂, 教学视频里的知识点太广泛, 事无巨细的罗列, 而这个系列文章将更突出绘制3d地球这个重点。
  4. 同道中人: 我学习 three.js就是为了做出3d地球, 期间走了不少弯路, 被某些问题卡了很久, 所以我更懂一个刚入门的人困惑的点在哪里。
  5. 专注vue: 市面上较少专门针对 vue做到开箱即用的3d地球插件, 而我们就要编写这样一款产品。
  6. 不断学习: 编写文章也是我提高自己能力的一种方法, 死磕每个知识点让自己的理解更上一层楼。

二. 任务目标

  1. 入门 three.js技术。
  2. 绘制出3d地球。
  3. 做成专门 vue使用的库。
  4. 后期也会介绍 着色器的概念与基本的使用技巧。
  5. 会介绍少量 webgl的相关用法, 并且会有部分数学知识。

三. 文章主线剧情与支线任务

  • 主线剧情: 围绕着如何做出3d地球, 这部分在vue工程里面进行。
  • 支线任务: 每个分散的知识点, 可能与3d地球没关系, 但是它能帮助我们更好的理解3d技术, 而这些知识点我就不在vue项目里面演示了, 会单独创建一个html文件来演示说明。

四. 理解坐标系: 别着急写代码先有基本模型

像绘制图形这类技术, 最基本的概念就坐标系, 下图是二维坐标系, 我们的故事就从这个家伙开始。     我们用(0, 0)表示坐标的中心点, 绘制一条起点为中心点长度为1的线段可以使用 (0, 0) (1, 0)这两个点相连表示。

关于向量的概念后面需要用数学知识的时候再介绍, 前几篇文章就越通俗越好。

three.js中我们要打交道的就是下面这位三维坐标系     他的坐标原点就是(0, 0, 0), 绘制一条起点为中心点的长度为1的线段可以是 (0, 0, 0) (1, 0, 0)

这里要记住, three.js里面设置的默认坐标系就是这种形式x向右, y向上, z向前, 之所以说是默是因为它可以修改。

上图中, 观看这个三维坐标系的目光其实是在斜上方, 正常情况下在我们开发的时候z轴是正对着我们的眼睛的, 所以你只能看到z轴是一个点,

在开发与学习的时候, 最好先把坐标系绘制到页面上, 方便我们更好的绘制。

五. 相机的概念

假设现在我们的正前方有一个三维坐标系的全息投影, 那么此时你的眼睛就相当于一架相机, 你看到的 坐标系景象取决于你站的位置。

three.js中就有这样一个对象, 他就是负责从哪个角度观察我们绘制的3d世界, 也就是相机这个概念的由来。

相机分为两种, 正投影相机和透视投影相机, 正投影相机就是你站的多远你看到的物体的大小都不变, 透视投影相机就是物体会近大远小, 下面是张引用图 (图片来自网络)。

正投影相机可以用在工程制图上, 或者可以做一些视觉欺骗小游戏。

本文主要目的是绘制3d地球所以主要使用透视投影相机

六. 绘制坐标系, 安放摄像机 (代码安排上)

引入three.js, 可以把包下载到本地, 也可以直接获取在cdn上的资源, 引入之后全局会出现THREE对象, 我们就可以开始编程之旅了。

<script src="https://cdn.bootcdn.net/ajax/libs/three.js/r122/three.min.js"></script>

一个普普通通的html空文件的script标签里面, 发生着这样的故事: 让我们逐句解析

第一步:创建场景, 也就是虚拟的空间

我们之后绘制的3d物体都要放入这个空间里面, 你可以把它当做一个鸿蒙空间神器, 里面有一个小世界, 而我们是掌控者(很中二)。

const scene = new THREE.Scene();
第二步:创建相机

相机的概念上面讲述过了, PerspectiveCamera这个类就是透视投影相机, 我们来逐个攻破他参数的意思。

  1. 35: 视角也就是我们左眼与右眼可以看到的横向角度, 其越小物体则越大, 因为目光变狭窄会突出物体, 你可以做一个实验, 聚精会神的盯着看一个物体, 你就会发现此时你左右两边本来靠余光可以看到的物体你现在看不清, 这个就是你的视角变小了, 变小视角还可以使目标物体比例变大, 我们知道这些就够理解这个数字了, 后期可以利用这个原理做一些令人惊讶的动画特效。
  2. window.innerWidth / window.innerHeight: 纵横比 宽/高, 这里宽高不会去写 px这种单位, 坐标系里面是一种抽象的长度单位, 所以要告诉浏览器咱们当前显示图像的区域的宽高比例(可以当它是百分比布局, 就像我们写css布局时使用 vh vw为单位)。
  3. 1: 近平面, 简单理解就是当一个 图像距离 相机的距离小于1的时候, 就不显示这个图像了。
  4. 1000: 远平面, 简单理解就是当一个 图像距离 相机的距离大于1000的时候, 就不显示这个图像了。
  5. camera.position.z = 10; 相机的坐标不设置的话, 默认就是(0, 0, 0)坐标原点, 这样类似脑袋在坐标轴原点上看坐标轴, 所以这里要设置距离坐标中心有一定距离, 也就是远距离观察这个坐标系。
const camera = new THREE.PerspectiveCamera(35window.innerWidth / window.innerHeight, 11000);
camera.position.z = 10;
  • 无聊的知识: 我们在玩 3d游戏的时候, 是不是有时候与另一个游戏人物距离太近了就会出现 人物中空的效果, 这些很可能就是他的某些部分距离你相机的距离, 小于了 近平面的距离导致的。
  • 物体距离眼睛越近越大, 越远越小, 因为一个物品无限大与无限远没有意义, 显示起来浪费性能, 所以才会设置近平面与远平面。

第三步:生成渲染实例
  1. WebGLRenderer生成一个渲染实例, 用来渲染我们所有的3d效果。
  2. setSize设置场景的宽高。
  3. setClearColor设置背景色, 这个背景色不是平面的, 是全方位的, 你可以想想成你在一个屋子里, 这个颜色就是屋子墙壁、地板、天花板的颜色(.5是透明度)。
  4. renderer.domElement生成的渲染的实例, 这个要放到对应的dom容器里面(是个canvas标签)。
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x00FFFF.5)
document.body.appendChild(renderer.domElement);
  • 知识点: setClearColor不写就是黑色
  • 知识点: setClearColor可以直接写"red"这种, 不用必须16进制。
第四步:插入坐标系实例
  1. AxisHelper: 用于生成辅助坐标实例, 2代表这个坐标系的长度, 因为我们不一定需要多长的辅助线。
  2. scene: 老朋友 场景, 它的 add方法就是把某某某加入到场景中来。
const axisHelper = new THREE.AxisHelper(2)
scene.add(axisHelper)
第五步:渲染出来
  1. 第一个参数是 场景, 第二个参数是 相机
renderer.render(scene, camera);

下面是效果图, z轴正对着我们所以看不到:

在斜上方看到是如下的效果, 之后的章节会说如何调整相机的位置与角度

完整的代码如下

<html>
<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/three.js/r122/three.min.js"></script>
    <script>
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(35window.innerWidth / window.innerHeight, 11000);
        camera.position.z = 10;
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setClearColor(0x00FFFF.5)
        document.body.appendChild(renderer.domElement);
        const axisHelper = new THREE.AxisHelper(2)
        scene.add(axisHelper)
        renderer.render(scene, camera);
    </script>

</body>
</html>

七. 第一个立方体

不画一个立方体感觉对不起 第一篇这个题目, 要注意了在three.js中你可以理解为绘制一个几何体需要两部分, 一个是几何体本身, 比如这个几何体的长宽高, 另一个就是材质可以简单理解为表面的颜色样式。     geometry这个单词我们会经常打交道的, 来一起记下它吧。

BoxGeometry 长方体

const geometry = new THREE.BoxGeometry(1, 2, 3);

  1. 1: '长', 也可以理解为在不设置坐标的时候在x轴上的长度。
  2. 2: '高', 也可以理解为在不设置坐标的时候在y轴上的长度。
  3. 3: '宽', 也可以理解为在不设置坐标的时候在z轴上的长度。

new出来的实例上面会有这个几何体的点的信息, 面的信息等等, 这个后面再详细说这次主要入门。

MeshBasicMaterial 材质

颜色与上面设置setClearColor一样, 什么写法都行的, 下面是我设置了一个红色的材质。const material = new THREE.MeshBasicMaterial({ color: 'red' });

生成'网格' Mesh

const cube = new THREE.Mesh(geometry, material);网格上含有位置信息、旋转信息、缩放信息等等, 他需要用几何体材质两个参数, 但其实并不像网上说的必须要有材质, 不传材质也能显示。

放入场景

也就是场景对象scene本身有个add方法。scene.add(cube);

右上方视角

放入场景的几种方式

1: 我直接放入geometryscene.add(geometry); 会报错了, 可以理解为不是网格对象所以报错了。以后遇到这类报错一定要考虑类型问题。

2: 未设置材质

const cube = new THREE.Mesh(geometry);

scene.add(cube);

白白的一片, 并且控制台没有报错。

八. 全部代码

<html>
<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/three.js/r122/three.min.js"></script>
    <script src="./utils/OrbitControls.js"></script>
    <script>
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(35window.innerWidth / window.innerHeight, 11000);
        camera.position.z = 10;
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setClearColor(0x00FFFF.5)
        document.body.appendChild(renderer.domElement);
        const axisHelper = new THREE.AxisHelper(2)
        scene.add(axisHelper)

        const geometry = new THREE.BoxGeometry(123);
        const material = new THREE.MeshBasicMaterial({ color'red' });
        const cube = new THREE.Mesh(geometry, material);
        scene.add(cube);

        renderer.render(scene, camera);
    </script>

</body>

</html>

end

第一篇写的内容并不多, 等基本知识储备够了就可以开始编写3d地球了, 那里将会很有意思。希望与你一起进步。

最后

欢迎关注【前端瓶子君】✿✿ヽ(°▽°)ノ✿
回复「 算法 」,加入前端算法源码编程群,每日一刷(工作日),每题瓶子君都会很认真的解答哟!
回复「交流」,吹吹水、聊聊技术、吐吐槽!
回复「 阅读 」,每日刷刷高质量好文!
如果这篇文章对你有帮助,在看」是最大的支持
》》面试官也在看的算法资料《《
“在看和转发” 就是最大的支持

本文分享自微信公众号 - 前端瓶子君(pinzi_com)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
2
2 收藏
分享
加载中
更多评论
打赏
0 评论
2 收藏
2
分享
返回顶部
顶部