Three.js基础

文章推薦指數: 80 %
投票人數:10人

这是Three.js系列文章的第一篇。

Three.js是一个尽可能简化在网页端获取3D 内容的库。

Three.js经常会和WebGL混淆, 但也并不总是,three.js其实是使用WebGL来绘制三维 ... English Русский 中文 内容列表 threejsfundamentals.org Three.js基础 这是Three.js系列文章的第一篇。

Three.js是一个尽可能简化在网页端获取3D 内容的库。

Three.js经常会和WebGL混淆, 但也并不总是,three.js其实是使用WebGL来绘制三维效果的。

WebGL是一个只能画点、线和三角形的非常底层的系统. 想要用WebGL来做一些实用的东西通常需要大量的代码, 这就是Three.js的用武之地。

它帮我们处理了像场景、灯光、阴影、材质、贴图、空间运算、几乎所有你需要自己通过WebGL来实现的东西。

这套教程假设你已经了解了JavaScript,因为大部分内容我们将会 用到ES6的语法。

点击这里查看你需要提前掌握的东西。

大部分支持Three.js的浏览器都会自动更新,所以部分用户应该都能运行本套教程的代码。

如果你想在非常老的浏览器上运行此代码, 你需要一个像Babel一样的语法编译器。

当然使用非常老的浏览器的用户可能根本不能运行Three.js。

人们在学习大多数编程语言的时候第一件事就是让电脑打印"HelloWorld!"。

对于三维来说第一件事往往是创建一个三维的立方体。

所以我们从"HelloCube!"开始。

首先我们需要一个canvas标签。

Three.js将会使用这个canvas标签所以我们要先获取它然后传给three.js。

注意这里有一些细节。

如果你没有给three.js传canvas,three.js会自己创建一个 ,但是你必须手动把它添加到文档中。

在哪里添加可能会不一样这取决你怎么使用, 我发现给three.js传一个canvas会更灵活一些。

我可以将canvas放到任何地方, 代码都会找到它,假如我有一段代码是将canvas插入到文档中,那么当需求变化时, 我很可能必须去修改这段代码。

在查找到canvas之后我们创建一个WebGLRenderer。

渲染器负责拿到你提供的所有数据, 然后将它渲染到canvas上。

之前还有其他渲染器,比如CSSRenderer、CanvasRenderer。

在将来可能还会有WebGL2Renderer或者WebGPURenderer。

目前的话是WebGLRenderer使用WebGL将三维渲染到canvas上。

接下来我们需要一个摄像机。

constfov=75; constaspect=2;//thecanvasdefault constnear=0.1; constfar=5; constcamera=newTHREE.PerspectiveCamera(fov,aspect,near,far); fov是fieldofview的缩写。

当前的情况是垂直方向为75度。

注意three.js中大多数的角用弧度表示,但是因为某些原因透视摄像机使用角度表示。

aspect指canvas的显示比例。

我们将在别的文章详细讨论,但是在默认情况下 canvas是300x150像素,所以aspect为300/150或者说2.。

near和far代表摄像机前方将要被渲染的空间。

任何在这个范围前面或者后面的物体都将被裁剪(不绘制)。

这四个参数定义了一个"frustum"。

一个frustum是指一个像被削去顶部 的金字塔形状。

换句话说把"frustum"想象成其他形状比如球体、 立方体、棱柱体、截椎体。

近平面和远平面的高度由fov决定。

两个平面的宽度由fov和aspect决定。

截椎体内部的物体将被绘制,截椎体外的东西将不会被绘制。

摄像机默认指向Z轴负方向,上方向朝向Y轴正方向。

我们将会把立方体 放置在坐标原点,所以我们需要往后移动摄像机才能看到物体。

camera.position.z=2; 下面是我们想要达到的效果。

上面的示意图中我们能看到摄像机的位置在z=2。

它朝向 Z轴负方向。

我们的截椎体从摄像机前方的0.1到5。

因为这张图是俯视图, fov会受到aspect的影响。

canvas的宽度是高度的两倍, 所以水平视角会比我们设置的垂直视角75要大。

然后我们创建一个Scene(场景)。

Scene是three.js最基本的组成. 需要three.js绘制的东西都需要加入到scene中。

我们将会 在scene是如何工作的中详细讨论。

constscene=newTHREE.Scene(); 然后我们创建一个包含盒子信息的BoxGeometry。

几乎所有的希望在three.js中显示的物体都需要一个定义了组成三维物体的顶点的几何体。

constboxWidth=1; constboxHeight=1; constboxDepth=1; constgeometry=newTHREE.BoxGeometry(boxWidth,boxHeight,boxDepth); 然后我们创建一个基本的材质并设置它的颜色.Colors的值可以 用css方式和十六进制来表示。

constmaterial=newTHREE.MeshBasicMaterial({color:0x44aa88}); 然后创建一个Mesh.Mesh代表了Geometry(物体的形状)和Material(怎么 绘制物体,光滑还是平整,什么颜色,什么贴图等等)的组合, 以及物体在场景中的位置、朝向、和缩放。

constcube=newTHREE.Mesh(geometry,material); 最后我们将mesh添加到场景中。

scene.add(cube); 然后我们通过将scene和camera传递给renderer的render方法 来渲染整个场景。

renderer.render(scene,camera); 这里有一个实例。

点击在新标签页中打开 很难看出来这是一个三维的立方体,因为我们 直视Z轴的负方向并且立方体和坐标轴是对齐的, 所以我们只能看到一个面。

我们来让立方体旋转起来,希望 能看出是三维的。

为了让它动起来我们需要在渲染循环函数中使用 requestAnimationFrame. 这是我们的渲染循环函数。

functionrender(time){ time*=0.001;//converttimetoseconds cube.rotation.x=time; cube.rotation.y=time; renderer.render(scene,camera); requestAnimationFrame(render); } requestAnimationFrame(render); requestAnimationFrame会告诉浏览器你有那些东西想要做动画。

传入一个函数作为回调函数。

我们这里的函数是render。

浏览器 会调用你的函数然后如果你更新了跟页面显示有关的东西, 浏览器就会重新渲染页面。

我们这里是调用three.js的 renderer.render函数来绘制我们的场景。

requestAnimationFrame会传入从页面加载到 我们函数的时间.传入的时间是毫秒数。

我发现 用秒会更简单所以我们把它转换成秒。

然后我们把立方体的X轴和Y轴方向的旋转角度设置成当前时间。

这些旋转角度 是弧度制。

一圈的弧度 为2Π所以我们的立方体在每个方向旋转一周的时间为6.28 秒。

然后渲染我们的场景并且调用另一帧动画来继续我们的循环。

在循环的未免我们调用一次requestAnimationFrame来开始循环渲染. 点击在新标签页中打开 效果好了一些但还是很难看出是三维的。

添加灯光会有帮助, 所以我们来添加一盏灯光。

three.js中有很多种类型的灯光, 我们将在后期文章中详细讨论。

现在我们先创建一盏平行光。

{ constcolor=0xFFFFFF; constintensity=1; constlight=newTHREE.DirectionalLight(color,intensity); light.position.set(-1,2,4); scene.add(light); } 平行光有一个位置和目标点。

默认值都为0,0,0。

我们这里 设置灯光的位置为-1,2,4所以它位于摄像机前面的 稍微左上方一点。

目标点还是0,0,0所以它朝向 坐标原点。

我们还需要改变材质。

MeshBasicMaterial材质不会受到灯光的 影响。

我们将他改成会受灯光影响的MeshPhongMaterial材质。

-constmaterial=newTHREE.MeshBasicMaterial({color:0x44aa88});//greenishblue +constmaterial=newTHREE.MeshPhongMaterial({color:0x44aa88});//greenishblue 下面开始生效了。

点击在新标签页中打开 现在应该可以很清楚的看出是三维的。

为了更有乐趣我们再添加两个立方体。

每个立方体将会使用同一个几何体但是不同的材质, 这样每个立方体将会是不同的颜色。

首先我们创建一个根据指定的颜色生成新材质的函数。

然后函数会根据指定的几何体生成一个mesh, 最后将他添加进场景并设置X轴的位置。

functionmakeInstance(geometry,color,x){ constmaterial=newTHREE.MeshPhongMaterial({color}); constcube=newTHREE.Mesh(geometry,material); scene.add(cube); cube.position.x=x; returncube; } 然后我们将使用三种不同的颜色和X轴位置调用三次函数, 将生成的网格实例存在一个数组中。

constcubes=[ makeInstance(geometry,0x44aa88,0), makeInstance(geometry,0x8844aa,-2), makeInstance(geometry,0xaa8844,2), ]; 最后我们将在渲染函数中旋转三个立方体。

我们 给每个立方体设置了稍微不同的旋转角度。

functionrender(time){ time*=0.001;//converttimetoseconds cubes.forEach((cube,ndx)=>{ constspeed=1+ndx*.1; constrot=time*speed; cube.rotation.x=rot; cube.rotation.y=rot; }); ... 这里是结果. 点击在新标签页中打开 如果你对比上面的示意图可以看到 效果符合我们的预想。

位置为X=-2和X=+2 的立方体有一部分在我们的截椎体外面。

他们大部分是被 包裹的,因为水平方向的视角非常大。

希望这个简短的介绍能帮助你起步。

Nextupwe'llcover makingourcoderesponsivesoitisadaptabletomultiplesituations. English Русский 中文 基础 基础 响应式设计 先决条件 设置 Solutions Loadan.OBJfile Loada.GLTFfile AddaBackgroundorSkybox HowtoDrawTransparentObjects MultipleCanvases,MultipleScenes PickingObjectswiththemouse PostProcessing ApplyingaLUTFileforeffects UsingShadertoyshaders AligningHTMLElementsto3D UsingIndexedTexturesforPickingandColor UsingACanvasforDynamicTextures BillboardsandFacades FreeingResources MakingVoxelGeometry(Minecraft) StartmakingaGame. WebVR WebVR-Basics WebVR-LookToSelect WebVR-PointToSelect Optimization OptimizingLotsofObjects OptimizingLotsofObjectsAnimated UsingOffscreenCanvasinaWebWorker Tips RenderingOnDemand DebuggingJavaScript DebuggingGLSL Takingascreenshot PreventtheCanvasBeingCleared GetKeyboardInputFromaCanvas MaketheCanvasTransparent Usethree.jsasBackgroundinHTML Fundamentals Primitives Scenegraph Materials Textures Lights Cameras Shadows Fog RenderTargets CustomGeometry CustomBufferGeometry Reference MaterialTable github three.js three.jsdocs Questions?Askonstackoverflow. Suggestion?Request?Issue?Bug? commentspoweredbyDisqus


請為這篇文章評分?