点击查看官方文档javascript
下面是翻译的内容(稍做修改)html
先了解一下Three.js应用程序的结构。Three.js应用程序须要建立一堆对象并将它们链接在一块儿。下图表示一个小three.js应用程序的图。
java
关于上图的注意事项canvas
Renderer 渲染器,将一个场景和一个摄像机传递给渲染器,它就会将摄像机截锥内的3D场景部分做为2D图像呈现给画布。数组
有一个树状结构的场景图,其中包括各类物体,如Scene对象,多个 Mesh对象,Light对象,Group,Object3D,和Camera对象。一个Scene对象定义了场景图的根,而且包含诸如背景色和雾化度之类的属性。这些对象定义了分层的父/子树状结构,并表示对象出如今何处,以及如何定向。浏览器
注意,上图中的Camera相机,占场景图中的一半。这是为了表示在three.js中,与其余对象不一样。摄像机没必要在场景图中才能正常工做。与其余对象同样,Camera 做为其余对象的子对象,将相对于其父对象移动和定向。less
Mesh 网格对象,表示用特定的材质绘制特定的几何图形。Material对象和Geometry对象均可以被多个网格对象使用。例如,要在不一样的位置绘制2个蓝色立方体,咱们可能须要2个网格对象来表示每一个立方体的位置和方向。咱们只须要一个Geometry来保存一个立方体的顶点数据,一个Material来指定蓝色。两个网格对象能够引用相同的Geometry对象和相同的Material对象函数
Geometry 几何对象,表示某些几何形状的顶点数据,例如球体,立方体,平面,狗,猫,人,树,建筑物等。Three.js提供了许多内置的几何图元。同时也能够建立自定义几何图形以及,或者从文件加载几何图形(如***.obj文件)。动画
Material 表面材质对象,标识绘制几何图形的表面属性,包括要使用的颜色及其光泽程度等。1个Material还能够引用一个或多个Texture对象,这些对象能够用于例如将图像包装到几何图形的表面上。ui
Texture 纹理对象,一般表示从图像文件加载,从画布生成或从另外一个场景渲染的图像。
Light 灯光对象,表明不一样类型的灯光。
下面制做一个最小的“Cube”场景,以下所示
<script type="module"> // 1.直接使用线上资源 import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js'; // 2.直接使用本地 three.js import *as THREE from "./three.js/build/three.js" // 3.使用本地 three.module.js // 本地启服务(http-server等), import 才能访问本地 three.module.js import *as THREE from "./three.js/build/three.module.js" </script>
script标签中 type="module" 很重要。这使咱们可以使用import关键字来加载three.js。还有其余方法能够加载three.js,可是从r106版本开始,建议使用模块。模块的优势是能够轻松导入所需的其余模块。这使咱们没必要手动加载它们依赖的额外脚本。
<body> <canvas id="c"></canvas> </body>
< script type = "module" > import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js'; function main() { const canvas = document.querySelector('#c'); const renderer = new THREE.WebGLRenderer({ canvas }); ... } main(); </script>
在查找画布以后,咱们建立了一个WebGLRenderer。渲染器负责获取你提供的全部数据并将其呈现到画布上。过去有其余渲染器,好比CSSRenderer, CanvasRenderer,未来可能会有WebGL2Renderer或WebGPURenderer。目前使用WebGLRenderer使用WebGL渲染3D到画布。
注意这里有一些注意的细节。若是你没有传递一个画布到three.js,它会为你建立一个,但你必须把它添加到你的文档。在哪里添加它取决于你的用例,你必须改变你的代码,因此咱们发现传递一个画布给three.js感受更灵活。
我能够将画布放在任何地方,代码会找到它,就好像我有代码能够将画布插入到文档中,若是个人用例发生变化,我可能须要更改代码。
接下来,咱们须要一台照相机。下面代码将建立一个PerspectiveCamera(透视相机)。
const fov = 75; // 视场角 const aspect = 2; // canvas 默认的宽高比例 默认 300/150 = 2 const near = 0.1; const far = 5; const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
fov 是 field of view 的缩写,表示视场角。在这种状况下,垂直尺寸为75度。注意,Three.js中的大多数角度都以 弧度 表示,但因为某些缘由,透视相机取 度。下图(网上截的图)中的ω,就表示视场角。
aspect 是画布的显示方面。咱们将在另外一篇文章中详细介绍 ,但默认状况下,画布尺寸为300x150像素,因此宽高比为300/150=2。
near 和 far 表示将要渲染在相机前面的空间范围。该范围以前或以后的任何内容都会被裁剪(不绘制)。
fov、aspect、near、far四个值,定义了一个“视锥”。视锥是3d形状的名称,它的形状像一个金字塔,顶端被切掉。换句话说,将“视锥”这个词视为另外一种3D形状,例如球体,立方体,棱镜,视锥。
近平面和远平面的高度由视场肯定。两个平面的宽度由视场和外观肯定。
定义的视锥内部的全部内容都将被绘制。视锥外面的都不绘制。
相机默认是向下看-Z轴,向上看+Y轴。咱们把立方体放在原点,因此咱们须要把摄像机从原点日后移一点,这样才能看到任何东西。
camera.position.z = 2;
在上图中,咱们能够看到咱们的摄像机在z = 2处。它沿着-Z轴向下看。咱们的截锥从摄像机前面0.1个单位开始,到摄像机前面5个单位。由于在这个图中,咱们是向下看的,视野是受角度影响的。咱们的画布的宽度是它的高度的两倍,因此整个视野将比咱们指定的75度的垂直视野宽得多。
接下来建立一个scene。three.js中的一个场景,能够当作是一种场景图形式的根。任何你想要让three.js绘制的东西,都须要添加到场景中。
const scene = new THREE.Scene();
接下来,咱们建立一个包含盒子数据的BoxGeometry。
几乎任何咱们想要在Three.js中显示的东西,都须要定义构成3D对象的顶点的几何图形。
const boxWidth = 1; const boxHeight = 1; const boxDepth = 1; const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
而后咱们建立一个基本材质并设置它的颜色。颜色可使用标准的CSS样式6位十六进制颜色值来指定。
const material = new THREE.MeshBasicMaterial({ color: 0x44aa88 })
而后建立一个网格。在three.js中,网格表明了一个几何体(物体的形状)和一个材质(如何绘制物体,闪亮的仍是平坦的,什么颜色,应用什么纹理)的组合等。以及该对象在场景中的位置、方向和比例。
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
而后,咱们能够经过调用渲染器的render函数,并将场景和摄像机传递给它,来渲染场景
renderer.render(scene, camera);
沿着-Z轴观察的而立方体自己是与轴对齐的,因此咱们只能看到一个面。用动画让它旋转,就能够看清3D绘制的效果。
使用浏览器的window.requestAnimationFrame进行渲染。
function render(time) { time *= 0.001; // 转化一下时间 cube.rotation.x = time; // 绕x轴旋转网格 cube.rotation.y = time; // 绕y轴旋转网格 renderer.render(scene, camera); // renderer渲染 requestAnimationFrame(render); } requestAnimationFrame(render);
虽然好了一点,但仍是很难看到3d效果。添加一些照明会有帮助,因此让咱们添加一盏灯。three.js中有许多种类的light
const color = 0xFFFFFF; // 光的颜色 const intensity = 1; // 光照强度 const light = new THREE.DirectionalLight(color, intensity); light.position.set(-1, 2, 4); // 灯的位置 scene.add(light);
定向灯有一个位置和一个目标。二者都默认为0, 0, 0。在本例中,咱们将灯光的位置设置为-1, 2, 4,这样它的位置好,就稍微在相机的左边、上面和后面一些。定向灯的目标坐标仍然是(0, 0, 0),因此它会照向原点。
咱们还须要换Materail。基本的Material不受光线的影响。让咱们把它改为MeshPhongMaterial,它会受到光线的影响。
它如今应该是比较清晰的3D视图了。为了好玩,咱们再加两个方块。咱们让每一个立方体使用相同的几何图形,但制做不一样的材质,所以每一个立方体能够是不一样的颜色。
首先,将建立一个函数,用指定的颜色建立一个新的Material,而后加上指定的Geometry,建立一个Mesh,并将其添加到场景中,并设置其在x轴上的位置。
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轴位置调用三次函数, 将生成的Mesh实例存在一个数组中。
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 * .1; const rot = time * speed; cube.rotation.x = rot; cube.rotation.y = rot; }); ... }
<canvas id="c"></canvas>
<script type="module"> import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js'; function main() { const canvas = document.querySelector('#c'); const renderer = new THREE.WebGLRenderer({ canvas }); const scene = new THREE.Scene(); { const fov = 75; const aspect = 2; const near = 0.1; const far = 5; const camera = new THREE.PerspectiveCamera(fov, aspect, near, far); camera.position.z = 2; } { const color = 0xFFFFFF; const intensity = 1; const light = new THREE.DirectionalLight(color, intensity); light.position.set(-1, 2, 4); } scene.add(light); { const boxWidth = 1; const boxHeight = 1; const boxDepth = 1; const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth); } 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; } 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 * .1; const rot = time * speed; cube.rotation.x = rot; cube.rotation.y = rot; }); renderer.render(scene, camera); requestAnimationFrame(render); } requestAnimationFrame(render); } main(); </script>