Three.js介绍

例子:

Three.js概述

Three.js 是基于 WebGL 技术,用于浏览器中开发 3D 交互场景的 JS 引擎。它对WebGL提供的接口进行了封装,降低了学习成本并提升了开发效率。
notion image
Three.js解决了WebGL开发复杂的难题,它封装了场景、相机、几何、3D模型加载器、灯光、材质、着色器、动画、粒子、数学工具等概念。
默认 WebGL 只支持简单的 点、线、三角,Three.js 就是在此 WebGL 基础之上,封装出强大且使用起来简单的 JS 3D 类库。
目前主流现代浏览器都已支持 WebGL,也意味着支持 Three.js。
notion image

3大核心关键模块

场景(scene)

场景是所有物体的容器。

场景的几个概念

概念1:一个局部的相对空间,即为一个场景

例如太阳系就是一个空间(场景)

概念2:一个空间(场景) 又可能是由 几个子空间(场景) 组合而成

太阳系由 8 大行星构成
行星除了本身之外还包卫星,例如地球和月球
地球上又包含陆地和海洋
陆地上又包含中国,中国包含你此刻所处的空间

概念3:表面上添加某场景,但实际上执行的是合并场景

notion image

相机(camera)

决定场景中哪些角度的内容会显示出来。
Three.js中我们常用的有两种类型的相机:正交(orthographic)相机、透视(perspective)相机。一般情况下为了模拟人眼我们都是使用透视相机; 正交镜头的特点是,物品的渲染尺寸与它距离镜头的远近无关。也就是说在场景中移动一个物体,其大小不会变化。正交镜头适合2D游戏。 透视镜头则是模拟人眼的视觉特点,距离远的物体显得更小。透视镜头通常更适合3D渲染。
透视投影
透视投影
正交投影
正交投影
fovfield of view的缩写。 当前的情况是垂直方向为75度。 注意three.js中大多数的角用弧度表示,但因透视摄像机使用角度表示。
aspect指canvas的显示比例
nearfar代表摄像机前方将要被渲染的空间。 任何在这个范围前面或者后面的物体都将被裁剪(不绘制)。
notion image
notion image
notion image
notion image
notion image

渲染器(renderer)

将 相机 中的内容渲染到浏览器页面中。
const renderer = new WebGLRenderer({ canvas: canvasRef.current })
 

光源(Light)

指不同种类的光。
在Three.js中光源是必须的,如果一个场景你不设置灯光那么世界将会是一片漆黑。Three.js内置了多种光源以满足特定场景的需要。
notion image
 ambientLight 环境光
ambientLight 环境光
spotLight
spotLight
pointLight
pointLight
directional Light
directional Light
areaLight
areaLight
 

几何体(Geometry)

顾名思义,就是几何体,例如 球体、立方体、平面、以及自定义的几何体(汽车、动物、房子、数目等)。
在 Three.js 中,一个几何体的来源有 3 个:
  1. Three.js 中内置的一些基本几何体
  1. 自己创建自定义的几何体
  1. 通过文件加载进来的几何体

材质(Material)

几何体的表面属性,包括颜色、光亮程度。
光亮程度是指物体表面反射光的能力值。Three.js 内置了不同的材质,不同材质对应不同的光亮程度。
内置材质 MeshBasicMaterial 是一种不可以反射光的材质,光源以何种角度照射到该物体上,该物体都不显示 “光亮”,而仅仅以材质本身的颜色或纹理来显示。
材质(material) 即 线段属性或物体表面的一些颜色、贴图、光亮程度、反光特性、粗糙度等属性。
按照用途,所以材质大体上可以划分为:
  1. 点材质(应用来点、粒子上)
  1. 线性材质(应用在线段或虚线上)
  1. 基础材质(应用在面上的各种材质)
  1. 特殊用途的材质(例如阴影)
  1. 自定义材质
一个材质可以引用一个或多个纹理。
MeshBasicMaterial: 最基础的材质,不反射光,仅显示材质本身颜色
MeshStandardMaterial:拥有光泽度,还有粗糙度和金属度.
ShaderMaterial:着色器材质
 
function init() {
container = document.querySelector("#scene-container");
// Everything shifted to Functions.
createScene();
createCamera();
createControls();
createLights();
createMeshes();
createRenderer();
//
renderer.setAnimationLoop(() => {
update();
render();
});
// /
}

纹理(Texture)

纹理可以简单理解为一种图像或一张图片,用来包裹到几何体表面上。
材质(material) 更多是表达一个物体表面的物理特性和一些简单的外观颜色。而 纹理(Texture) 则专门来设置物体表面贴合的彩色图片的,具体做法就是:
  1. 使用纹理加载器 TextureLoader 加载外部图片(.jpg或.png)
  1. 通过设置 物体的 .map 属性,将加载得到的外部图片贴合在物体表面
    1. notion image
      notion image

网格(Mesh)

一种特定的 几何体和材质 绘制出的一个特定的几何体系。
网格包含的内容为:几何体、几何体的材质、几何体的自身网格坐标体系
同一个材质和几何体可以被多个mesh使用。
一个场景可以同时添加多个mesh。
在计算机的世界里,一条弧线是由有限个点构成的有限条线段连接得到的。当线段数量越多,长度就越短,当达到你无法察觉这是线段时,一条平滑的弧线就出现了。 计算机的三维模型也是类似的。只不过线段变成了平面,普遍用三角形组成的网格来描述。我们把这种模型称之为 Mesh 模型。 在 threeJs 的世界中,材质(Material)+几何体(Geometry)就是一个 mesh。Geometry就好像是骨架,材质则类似于皮。
 

Example:

手把手带你做一个地球

三步构建一个3D场景:

1.创建/加载所有场景所需的资产(asset)

2.将所有asset加入到场景中

3.渲染当前场景

 
 

1. 创建一个初始的场景

let container, camera, controls, renderer, scene, mesh; function init() { container = document.querySelector("#scene-container"); createScene(); createCamera(); createMeshes(); createRenderer(); //requestAnimationFrame renderer.setAnimationLoop(() => { render(); }); } function createScene() { scene = new THREE.Scene(); } function createCamera() { camera = new THREE.PerspectiveCamera( 75, container.clientWidth / container.clientHeight, 0.1, 1000 ); camera.position.set(0, 0, 10); } function createMeshes() { const geometry = new THREE.SphereBufferGeometry(2, 300, 300); const material = new THREE.MeshBasicMaterial({ color: "#333333" }); mesh = new THREE.Mesh(geometry, material); scene.add(mesh); } function createRenderer() { renderer = new THREE.WebGLRenderer({ container }); renderer.setSize(container.clientWidth, container.clientHeight); renderer.setPixelRatio(window.devicePixelRatio); container.appendChild(renderer.domElement); } function render() { renderer.render(scene, camera); } function onWindowResize() { // set the aspect ratio to match the new browser window aspect ratio camera.aspect = container.clientWidth / container.clientHeight; // update the camera's frustrum camera.updateProjectionMatrix(); // don't found any need // update the size of the renderer and the canvas renderer.setSize(container.clientWidth, container.clientHeight); } window.addEventListener("resize", onWindowResize); init();
 

2. 给地球打上光,并且加载纹理

function createLights() { const ambientLight = new THREE.AmbientLight(0xffffff, 30.0); scene.add(ambientLight); const mainLight = new THREE.DirectionalLight(0xffffff, 5); mainLight.position.set(10, 10, 10); scene.add(mainLight); } // 改写 material,添加texture function createMeshes() { var texture = new THREE.TextureLoader().load("earthmirror.jpg"); texture.encoding = THREE.sRGBEncoding; texture.wrapS = texture.wrapT = THREE.RepeatWrapping; texture.anisotropy = 16; const geometry = new THREE.SphereBufferGeometry(2, 300, 300); const material = new THREE.MeshStandardMaterial({ map: texture }); mesh = new THREE.Mesh(geometry, material); scene.add(mesh); }
 

3.添加地球自转,为光线添加辅助线,给宇宙添加背景,添加鼠标的控制

function createControls() { controls = new THREE.OrbitControls(camera, container); } // 添加辅助线 function createLights() { const ambientLight = new THREE.AmbientLight(0xffffff, 30.0); scene.add(ambientLight); const mainLight = new THREE.DirectionalLight(0xffffff, 5); mainLight.position.set(10, 10, 10); const lightHelper1 = new THREE.SpotLightHelper(mainLight); scene.add(mainLight); scene.add(lightHelper1); } function createScene() { scene = new THREE.Scene(); const loader = new THREE.TextureLoader(); var bgTexture = loader.load("universe3.jpg"); scene.background = bgTexture; bgTexture.wrapS = bgTexture.wrapT = THREE.RepeatWrapping; } function update() { mesh.rotation.z += 0.001; mesh.rotation.y -= 0.001; }

最终结果:

 

最后:

如果你对3d感兴趣,强烈推荐去学习一下计算机图形学的基础,从数学的角度去理解3d能对自己所做的内容有更深刻的理解。
如果想要做比较复杂的模型,推荐学习一门建模软件:Blender,Maya,C4D。新人建议学习Blender。
当你发现three.js已经无法满足你的设计需求了,恭喜你🎉,现在是时候向更深入的方向学习了:WebGL。
 
学习资料: