摘要:一般說來(lái),對(duì)于制圖建模軟通常使正交投影,這樣不會(huì)因?yàn)橥队岸淖兾矬w比例而對(duì)于其他大多數(shù)應(yīng)用,通常使用透視投影,因?yàn)檫@更接近人眼的觀察效果。
1. 概述 1.1 什么是WebGL?
WebGL是在瀏覽器中實(shí)現(xiàn)三維效果的一套規(guī)范
想要使用WebGL原生的API來(lái)寫3D效果的話,很吃力。three.js是WebGL的一個(gè)開源框架,它省去了很多麻煩的細(xì)節(jié)。
1.2 初識(shí)three.js什么是threejs,很簡(jiǎn)單,你將它理解成three+js就可以了。three表示3D的意思,js表示javascript的意思。那么合起來(lái),three.js就是使用javascript 來(lái)寫3D程序的意思。
Javascript是運(yùn)行在網(wǎng)頁(yè)端的腳本語(yǔ)言,那么毫無(wú)疑問Three.js也是運(yùn)行在瀏覽器上的。
1.3 前期準(zhǔn)備 1.3.1 下載地址three.js 下載地址
1.3.2 目錄結(jié)構(gòu)Build目錄: 包含兩個(gè)文件,three.js 和three.min.js 。這是three.js最終被引用的文件。一個(gè)已經(jīng)壓縮,一個(gè)沒有壓縮的js文件。
Docs目錄: 這里是three.js的幫助文檔,里面是各個(gè)函數(shù)的api,可惜并沒有詳細(xì)的解釋。試圖用這些文檔來(lái)學(xué)會(huì)three.js是不可能的。
Editor目錄: 一個(gè)類似3D-max的簡(jiǎn)單編輯程序,它能創(chuàng)建一些三維物體。
Examples目錄: 一些很有趣的例子demo,可惜沒有文檔介紹。對(duì)圖像學(xué)理解不深入的同學(xué),學(xué)習(xí)成本非常高。
Src目錄: 源代碼目錄,里面是所有源代碼。
Test目錄: 一些測(cè)試代碼,基本沒用。
Utils目錄: 存放一些腳本,python文件的工具目錄。例如將3D-Max格式的模型轉(zhuǎn)換為three.js特有的json模型。
.gitignore文件: git工具的過濾規(guī)則文件,沒有用。
CONTRIBUTING.md文件: 一個(gè)怎么報(bào)bug,怎么獲得幫助的說明文檔。
LICENSE文件: 版權(quán)信息。
README.md文件: 介紹three.js的一個(gè)文件,里面還包含了各個(gè)版本的更新內(nèi)容列表。
1.3.3 配置開發(fā)環(huán)境瀏覽器: 推薦使用高版本的瀏覽器,谷歌、火狐、360等,對(duì)于前端開發(fā)者來(lái)說,chrome是不二的選擇
js 開發(fā)工具: VS-code、Webstorm 都可以,為了方便下面的學(xué)習(xí),這里使用Webstorm
Three.js 調(diào)試: 利用谷歌瀏覽器的調(diào)試窗口,使用斷點(diǎn)調(diào)試的方法
2. 開始使用Three.js使用Three.js之前,首先在部分,需要引入外部文件Three.js。
WebGL 的渲染是需要HTML5 中的Canvas元素的,你可以手動(dòng)在HTML的部分中使用canvas標(biāo)簽,或者讓Three.js幫你生成。這兩種選擇,一般沒有多大差別。我們先手動(dòng)定義一個(gè)canvas標(biāo)簽:
在js里面定義一個(gè)函數(shù),將所有執(zhí)行的代碼放在函數(shù)里,在html加載完成后,執(zhí)行該函數(shù)
function init{ // 所有需要執(zhí)行的代碼 }
一個(gè)典型的Three.js程序,至少應(yīng)該包括四個(gè)部分:渲染器(renderer)、場(chǎng)景(scene)、相機(jī)(camera)、以及場(chǎng)景中創(chuàng)建的物體。
2.1 渲染器(renderer)渲染器決定了渲染的結(jié)果應(yīng)該畫在頁(yè)面的什么元素上面,并且以怎樣的方式來(lái)繪制。渲染器將會(huì)和canvas元素進(jìn)行綁定,如果之前標(biāo)簽中,定義了id為canvasId的canvas標(biāo)簽,那么renderer可以這樣寫:
var renderer = new THREE.WebGLRenderer({ canvas : document.getElementById("canvasId"); });
如果想要Three.js生成Canvas元素的時(shí)候,在html中就不需要在定義一個(gè)canvas標(biāo)簽了,直接在javascript代碼中寫道:
var renderer = new THREE.WebGLRenderer(); renderer.setSize = (800,600); document.body.appendChild(renderer.domElement);
上面的代碼setSize是為canvas元素設(shè)置寬高,document.body.appendChild(renderer.domElement)是將渲染器對(duì)應(yīng)的Canvas元素添加到body中。
我們可以使用下面的代碼(用于清除畫面的顏色)將背景色設(shè)置為黑色:
renderer.setClearColor(0x000000);2.2 場(chǎng)景(scene)
在Three.js中添加物體都是添加到場(chǎng)景中的,因此它相當(dāng)于一個(gè)大容器。一般說,場(chǎng)景里沒有很復(fù)雜的操作,只要new一個(gè)對(duì)象就可以了,然后將物體添加到場(chǎng)景中即可。
var scene = new THREE.Scene();2.3 照相機(jī)(camera)
在介紹照相機(jī)之前,我們先來(lái)介紹一下坐標(biāo)系。
three.js中使用的是右手坐標(biāo)系,X軸水平向右,y軸垂直向上,Z軸的方向就是屏幕由里往外的方向
這里我們定義一個(gè)透視相機(jī)(相機(jī)也需要添加到場(chǎng)景中):
var camera = new THREE.PerspectiveCamera(45, 4 / 3, 1, 1000); // 設(shè)置相機(jī)的位置 camera.position.set(0,0,5); // 將相機(jī)添加到場(chǎng)景中 scene.add(camera);2.4 創(chuàng)建一個(gè)物體
這里我們先介紹一個(gè)長(zhǎng)方體,創(chuàng)建一個(gè)x、y、z方向長(zhǎng)度分別為1、2、3的長(zhǎng)方體,并設(shè)置為紅色。
var geometry = new THREE.CubeGeometry(1,2,3); var material = new THREE.MeshBasicMaterial({ color: 0xff0000; }); var cube = new THREE.Mesh(geometry,material); scene.add(cube);
new THREE.CubeGeometry(); 表示調(diào)用一個(gè)幾何體
Cube : 立方體 Geometry : 幾何;
CubeGeometry是一個(gè)正方體或者長(zhǎng)方體,究竟是什么,由它的3個(gè)參數(shù)所決定
CubeGeometry(width, height, depth, segmentsWidth, segmentsHeight, segmentsDepth, materials, sides)
width:立方體x軸的長(zhǎng)度 height:立方體y軸的長(zhǎng)度 depth:立方體z軸的深度,也就是長(zhǎng)度 想一想大家就明白,以上3個(gè)參數(shù)就能夠確定一個(gè)立方體。 剩下的幾個(gè)參數(shù)就要費(fèi)解和復(fù)雜一些了,不過后面我們會(huì)自己來(lái)寫一個(gè)立方體,到時(shí)候,你會(huì)更明白這些參數(shù)的意義,這里你可以將這些參數(shù)省略。
new THREE.MeshBasicMaterial(); 表示的是物體的材質(zhì)
你可以在里面設(shè)置物體的顏色
var material = new THREE.MeshBasicMaterial({ color: 0xff0000; });
一定不要忘了,將物體添加到場(chǎng)景
2.5 渲染在定義了場(chǎng)景中的物體,設(shè)置好的照相機(jī)之后,渲染器就知道如何渲染出二維的結(jié)果了。這時(shí)候,我們只需要調(diào)用渲染器的渲染函數(shù),就能使其渲染一次了。
renderer.render(scene, camera);2.6 完整代碼
長(zhǎng)方體
效果圖
canvas元素的默認(rèn)寬高為300/150
下面介紹下Three.js官網(wǎng)文檔中的一些重要的對(duì)象,在你需要尋求幫助時(shí),就能夠知道關(guān)鍵詞是什么。
Cameras(照相機(jī),控制投影方式)
Camera
OrthographicCamera
PerspectiveCamera
Core(核心對(duì)象)
BufferGeometry
Clock(用來(lái)記錄時(shí)間)
EventDispatcher
Face3
Face4
Geometry
Object3D
Projector
Raycaster(計(jì)算鼠標(biāo)拾取物體時(shí)很有用的對(duì)象)
Lights(光照)
Light
AmbientLight
AreaLight
DirectionalLight
HemisphereLight
PointLight
SpotLight
Loaders(加載器,用來(lái)加載特定文件)
Loader
BinaryLoader
GeometryLoader
ImageLoader
JSONLoader
LoadingMonitor
SceneLoader
TextureLoader
Materials(材質(zhì),控制物體的顏色、紋理等)
Material
LineBasicMaterial
LineDashedMaterial
MeshBasicMaterial
MeshDepthMaterial
MeshFaceMaterial
MeshLambertMaterial
MeshNormalMaterial
MeshPhongMaterial
ParticleBasicMaterial
ParticleCanvasMaterial
ParticleDOMMaterial
ShaderMaterial
SpriteMaterial
Math(和數(shù)學(xué)相關(guān)的對(duì)象)
Box2
Box3
Color
Frustum
Math
Matrix3
Matrix4
Plane
Quaternion
Ray
Sphere
Spline
Triangle
Vector2
Vector3
Vector4
Objects(物體)
Bone
Line
LOD
Mesh(網(wǎng)格,最常用的物體)
MorphAnimMesh
Particle
ParticleSystem
Ribbon
SkinnedMesh
Sprite
Renderers(渲染器,可以渲染到不同對(duì)象上)
CanvasRenderer
WebGLRenderer(使用WebGL渲染,這是本書中最常用的方式)
WebGLRenderTarget
WebGLRenderTargetCube
WebGLShaders(著色器,在最后一章作介紹)
Renderers / Renderables
RenderableFace3
RenderableFace4
RenderableLine
RenderableObject
RenderableParticle
RenderableVertex
Scenes(場(chǎng)景)
Fog
FogExp2
Scene
Textures(紋理)
CompressedTexture
DataTexture
Texture
Extras
FontUtils
GeometryUtils
ImageUtils
SceneUtils
Extras / Animation
Animation
AnimationHandler
AnimationMorphTarget
KeyFrameAnimation
Extras / Cameras
CombinedCamera
CubeCamera
Extras / Core
Curve
CurvePath
Gyroscope
Path
Shape
Extras / Geometries(幾何形狀)
CircleGeometry
ConvexGeometry
CubeGeometry
CylinderGeometry
ExtrudeGeometry
IcosahedronGeometry
LatheGeometry
OctahedronGeometry
ParametricGeometry
PlaneGeometry
PolyhedronGeometry
ShapeGeometry
SphereGeometry
TetrahedronGeometry
TextGeometry
TorusGeometry
TorusKnotGeometry
TubeGeometry
Extras / Helpers
ArrowHelper
AxisHelper
CameraHelper
DirectionalLightHelper
HemisphereLightHelper
PointLightHelper
SpotLightHelper
Extras / Objects
ImmediateRenderObject
LensFlare
MorphBlendMesh
Extras / Renderers / Plugins
DepthPassPlugin
LensFlarePlugin
ShadowMapPlugin
SpritePlugin
Extras / Shaders
ShaderFlares
ShaderSprite
我們看到,Three.js功能是十分豐富的,一時(shí)間想全部掌握有些困難。在接下來(lái)的章節(jié)中,我們將會(huì)先詳細(xì)介紹照相機(jī)、幾何形狀、材質(zhì)、物體等入門級(jí)知識(shí);然后介紹使用動(dòng)畫、模型導(dǎo)入、加入光照等功能;最后,對(duì)于學(xué)有余力的讀者,我們將介紹著色器,用于更高級(jí)的圖形渲染。
4. 照相機(jī)本章將介紹照相機(jī)的概念,以及如何使用Three.js設(shè)置相應(yīng)的參數(shù)。4.1 什么是照相機(jī)?
在圖形學(xué)中,照相機(jī)可沒有生活中的照相機(jī)那么簡(jiǎn)單
我們使用的Three.js創(chuàng)建的場(chǎng)景是三維的,而通常情況下顯示器是二維的,那么三維的場(chǎng)景怎么在二維的顯示器上顯示呢?照相機(jī)就是一個(gè)抽象,它定義了三維空間到二維屏幕投影的方式,用“照相機(jī)”這樣一個(gè)類比,可以使我們直觀地理解這一投影方式。
而針對(duì)投影方式的不同,照相機(jī)又分為正交投影照相機(jī)與透視投影照相機(jī)。我們需要為自己的程序選擇合適的照相機(jī)。這兩者分別是什么,以及兩者有何差異,我們將在下節(jié)中作介紹。
4.2 正交投影和透視投影舉個(gè)簡(jiǎn)單的例子來(lái)說明正交投影與透視投影照相機(jī)的區(qū)別。使用透視投影照相機(jī)獲得的結(jié)果是類似人眼在真實(shí)世界中看到的有“近大遠(yuǎn)小”的效果(如下圖中的(a));而使用正交投影照相機(jī)獲得的結(jié)果就像我們?cè)跀?shù)學(xué)幾何學(xué)課上老師教我們畫的效果,對(duì)于三維空間內(nèi)平行的線,投影到二維空間中也一定是平行的(如下圖中的(b))。
一般說來(lái),對(duì)于制圖、建模軟通常使正交投影,這樣不會(huì)因?yàn)橥队岸淖兾矬w比例;而對(duì)于其他大多數(shù)應(yīng)用,通常使用 透視投影,因?yàn)檫@更接近人眼的觀察效果。當(dāng)然,照相機(jī)的選擇并沒有對(duì)錯(cuò)之分,你可以更具應(yīng)用的特性,選擇一個(gè)效果更佳的照相機(jī)。
4.3 正交投影照相機(jī) 4.3.1 參數(shù)介紹正交投影照相機(jī)(Orthographic Camera)
THREE.OrthographicCamera(left, right, top, bottom, near, far)
這六個(gè)參數(shù)分別代表正交投影照相機(jī)拍攝到的空間的六個(gè)面的位置,這六個(gè)面圍成一個(gè)長(zhǎng)方體,我們稱其視景體(Frustum)。只有在視景體內(nèi)部(下圖中的灰色部分)的物體才可能顯示在屏幕上,而視景體外的物體會(huì)在顯示之前被裁減掉。
為了保持照相機(jī)的橫豎比例,需要保證(right - left)與(top - bottom)的比例與Canvas寬度與高度的比例(800/600)一致。
// [2-(-2)] / [1.5-(-1.5)] = canvas.width/canvas.height var camera = new THREE.OrthographicCamera(-2, 2, 1.5, -1.5, 1, 10) // left right top bottom near far
near與far都是指到照相機(jī)位置在深度平面的位置,而照相機(jī)不應(yīng)該拍攝到其后方的物體,因此這兩個(gè)值應(yīng)該均為正值。為了保證場(chǎng)景中的物體不會(huì)因?yàn)樘蛱h(yuǎn)而被照相機(jī)忽略,一般near的值設(shè)置得較小,far的值設(shè)置得較大,具體值視場(chǎng)景中物體的位置等決定。
4.3.2 示例代碼下面我們通過一個(gè)具體的例子來(lái)了解正交投影照相機(jī)的設(shè)置
基本設(shè)置
設(shè)置照相機(jī):
var camera = new THREE.OrthographicCamera(-2, 2, 1.5, -1.5, 1, 10); camera.poaition.set(0,0,5); scene.add(camera);
在原點(diǎn)處創(chuàng)建一個(gè)邊長(zhǎng)為1的正方體,為了和透視效果做對(duì)比,這里我們使用wireframe而不是實(shí)心的材質(zhì),以便看到正方體后方的邊:
var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true }) ); scene.add(cube);
效果圖:
我們看到正交投影的結(jié)果是一個(gè)正方形,后面的邊與前面完全重合了,這也就是正交投影與透視投影的區(qū)別所在。
長(zhǎng)寬比例
這里,我們的Canvas寬度是800px,高度是600px,照相機(jī)水平方向距離4,垂直方向距離3,因此長(zhǎng)寬比例保持不變。為了試驗(yàn)長(zhǎng)寬比例變化時(shí)的效果,我們將照相機(jī)水平方向的距離減小為2(right-left = 2):
var camera = new THREE.OrthographicCamera(-1, 1, 1.5, -1.5, 1, 10);
效果圖(此時(shí)水平方向的距離就被拉長(zhǎng)了):
照相機(jī)位置
接下來(lái),我們來(lái)看看照相機(jī)位置對(duì)渲染結(jié)果的影響。在之前的例子中,我們將照相機(jī)設(shè)置在(0, 0, 5)位置,而由于照相機(jī)默認(rèn)是面向z軸負(fù)方向放置的,所以能看到在原點(diǎn)處的正方體?,F(xiàn)在,如果我們將照相機(jī)向右移動(dòng)1個(gè)單位:
var camera = new THREE.OrthographicCamera(-2, 2, 1.5, -1.5, 1, 10); // 向右移動(dòng)一個(gè)單位的位置 camera.position.set(1, 0, 5);
效果圖(物體看上去向左移動(dòng)了)
其實(shí)照相機(jī)就好比人的眼睛,當(dāng)我們身體往右移動(dòng)的時(shí)候,看到的物體就好像向左移了。
正交投影攝像機(jī)在設(shè)置時(shí),是否需要保證left 和 right 互為相反數(shù)呢?
下面,我們將原本的參數(shù)(-2, 2, 1.5, -1.5, 1, 10)改為(-1, 1, 1.5, -1.5, 1, 10),即,將視景體設(shè)置得更靠右:
var camera = new THREE.OrthographicCamera(-1, 3, 1.5, -1.5, 1, 10); camera.position.set(0, 0, 5);
效果圖(與之前相機(jī)向右的效果是一樣的)
換個(gè)角度
到目前為止,我們使用照相機(jī),都是沿著Z軸負(fù)方向觀察的,因此看到的都是一個(gè)正方形,現(xiàn)在我們嘗試一下仰望這個(gè)正方體,改變照相機(jī)的位置:
// x軸:4; y軸:-3; z軸:5 camera.position.set(4, -3, 5);
照相機(jī)默認(rèn)是沿著z軸的負(fù)方向觀察的,因此觀察不到正方體,只看到一片黑。我們可以通過lookAt函數(shù)指定它看著原點(diǎn)方向:
camera.lookAt(new THREE.Vector3(0, 0, 0));
效果圖:
注意:lookAt函數(shù)接收的是一個(gè)THREE.Vector3的實(shí)例千萬(wàn)不能寫成camera.lookAt(0,0,0)。
4.4 透視投影照相機(jī) 4.4.1 參數(shù)介紹透視投影照相機(jī)(Perspective Camera)
THREE.PerspectiveCamera(fov, aspect, near, far)
讓我們通過一張透視照相機(jī)投影的圖來(lái)了解這些參數(shù)。
透視圖中,灰色的部分是視景體,是可能被渲染的物體所在的區(qū)域。fov是視景體豎直方向上的張角(是角度制而非弧度制),如側(cè)視圖所示。
aspect等于width / height,是照相機(jī)水平方向和豎直方向長(zhǎng)度的比值,通常設(shè)為Canvas的橫縱比例。
near和far分別是照相機(jī)到視景體 最近、最遠(yuǎn)的距離,均為正值,且far應(yīng)大于near。
4.4.2 示例代碼下面我們通過一個(gè)例子來(lái)學(xué)習(xí)透視投影照相機(jī)
基本設(shè)置
設(shè)置透視投影照相機(jī),這里Canvas長(zhǎng)800px,寬600px,所以aspect設(shè)為800 / 600:
var camera = new THREE.PerspectiveCamera(45, 800 / 600, 1, 10); camera.position.set(0, 0, 5); scene.add(camera);
設(shè)置一個(gè)在原點(diǎn)處的邊長(zhǎng)為1的正方體:
var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true }) ); scene.add(cube);
效果圖:
對(duì)比正交透視照相機(jī)下正方形的效果,透視投影可以看到全部的12條邊,而且有近大遠(yuǎn)小的效果,這也就是與正交投影的區(qū)別。
豎直張角
接下來(lái),我們來(lái)看下fov的改變對(duì)渲染效果的影響。我們將原來(lái)的45改為60:
var camera = new THREE.PerspectiveCamera(60, 800 / 600, 1, 10); camera.position.set(0, 0, 5); scene.add(camera);
效果圖:
為什么正方體顯得更小了呢?我們從下面的側(cè)視圖來(lái)看,雖然正方體的實(shí)際大小并未改變,但是將照相機(jī)的豎直張角設(shè)置得更大時(shí),視景體變大了,因而正方體相對(duì)于整個(gè)視景體的大小就變小了,看起來(lái)正方形就顯得變小了。
注意,改變fov并不會(huì)引起畫面橫豎比例的變化,而改變aspect則會(huì)改變橫豎比例。
5. 點(diǎn)、線、面 5.1 3D世界的組成在計(jì)算機(jī)世界里,3D世界由點(diǎn)組成,兩個(gè)點(diǎn)能組成一條直線,三個(gè)不在一條直線上的點(diǎn),就能組成一個(gè)三角面,無(wú)數(shù)的三角面就能組成各種各樣的物體,如下圖:
我們通常把這種網(wǎng)絡(luò)模型叫做Mesh模型。給物體貼上皮膚,或者專業(yè)點(diǎn)就叫做紋理,那么這個(gè)物體就活靈活現(xiàn)了。最后無(wú)數(shù)的物體就組成了我們的3D世界。
5.2 在Three.js中定義一個(gè)點(diǎn)在三維空間中的某一個(gè)點(diǎn)可以用一個(gè)坐標(biāo)點(diǎn)來(lái)表示。一個(gè)坐標(biāo)點(diǎn)由x,y,z三個(gè)分量構(gòu)成。在three.js中,點(diǎn)可以在右手坐標(biāo)系中表示:空間幾何中,點(diǎn)可以用一個(gè)向量來(lái)表示,在Three.js中也是用一個(gè)向量來(lái)表示的
THREE.Vector3 = function ( x, y, z ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; };
我們來(lái)分析這段代碼:前面我們已經(jīng)知道了THREE是Three.js引擎的一個(gè)全局變量。只要你想用它,就可以在任何地方用它。
那么THREE.Vector3呢,就是表示Vector3是定義在THREE下面的一個(gè)類。以后要用Vector3,就必須要加THREE前綴。當(dāng)然Three.js的設(shè)計(jì)者,也可以不加THREE這個(gè)前綴,但是他們預(yù)見到,Three.js引擎中會(huì)有很多類型,最好給這些類型加一個(gè)前綴,以免與開發(fā)者的代碼產(chǎn)生沖突。
THREE.Vector3被賦值為一個(gè)函數(shù)。這個(gè)函數(shù)有3個(gè)參數(shù),分別代表x坐標(biāo),y坐標(biāo)和z坐標(biāo)的分量。函數(shù)體內(nèi)的代碼將他們分別賦值給成員變量x,y,z??纯瓷厦娴拇a,中間使用了一個(gè)“||”(或)運(yùn)算符,就是當(dāng)x=null或者undefine時(shí),this.x的值應(yīng)該取0。
5.3 點(diǎn)的操作在3D世界中點(diǎn)可以用THREE.Vector3D來(lái)表示。
現(xiàn)在來(lái)看看怎么定義個(gè)點(diǎn),假設(shè)有一個(gè)點(diǎn)x=4,y=8,z=9。你可以這樣定義它:
var point1 = new THREE.Vecotr3(4,8,9);
另外你也可以使用set方法,代碼如下:
var point1 = new THREE.Vector3(); point1.set(4,8,9);5.4 繪制一條線段
兩個(gè)不重合的點(diǎn)能夠決定一條直線。在three.js中,也可以通過定義兩個(gè)點(diǎn),來(lái)畫一條直線。
1、首先,聲明一個(gè)幾何體geometry
幾何體里面有個(gè)vertices變量,可以用來(lái)存放點(diǎn)
var geometry = new THREE.Geometry(); // 幾何體里面有個(gè)vertices變量,可以用來(lái)存放點(diǎn)
2、定義一種線條的材質(zhì),使用THREE.LineBasicMaterial類型來(lái)定義,它接受一個(gè)集合作為參數(shù),其原型如下:
THREE.LineBasicMaterial(parameters);
parameters 是定義材質(zhì)外觀的對(duì)象,它包含多個(gè)屬性來(lái)定義材質(zhì),這些屬性是:
Color 線條的顏色,用16進(jìn)制表示,默認(rèn)都是白色
Linewidth 線條的寬度,默認(rèn)是1個(gè)單位寬度
Linecap 線條兩端的外觀,默認(rèn)是圓角端點(diǎn),當(dāng)線條較粗的時(shí)候才能看到效果
Linejoin 兩個(gè)線條的連接點(diǎn)處的外觀,默認(rèn)是“round”,表示圓角。
VertexColors 定義線條材質(zhì)是否使用頂點(diǎn)顏色,這是一個(gè)boolean值。意思是,線條各部分的顏色會(huì)根據(jù)頂點(diǎn)的顏色來(lái)進(jìn)行插值。
Fog 定義材質(zhì)的顏色是否受全局霧效的影響。
我們這里使用了頂點(diǎn)顏色 vertexColors: THREE.VertexColors,就是線條的顏色會(huì)根據(jù)頂點(diǎn)來(lái)計(jì)算。
var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
注意: 關(guān)于線寬的坑,WebGLRender渲染方式是不之持繪制線寬的,要想支持,需要將渲染方式設(shè)置為CanvasRenderer
3、接下來(lái),定義兩種顏色,分別表示線條兩個(gè)端點(diǎn)的顏色,
var color1 = new THREE.Color( 0x444444 ), color2 = new THREE.Color( 0xFF0000 );
4、定義2個(gè)頂點(diǎn)的位置,并放到geometry中,代碼如下:
var p1 = new THREE.Vector3(-100,0,100); var p2 = new THREE.Vector3(100,0,-100); geometry.vertices.push(p1); geometry.vertices.push(p2);
5、為4中定義的2個(gè)頂點(diǎn),設(shè)置不同的顏色,代碼如下所示:
geometry.colors.push( color1, color2 );
geometry中colors表示頂點(diǎn)的顏色,必須材質(zhì)中vertexColors等于THREE.VertexColors時(shí),顏色才有效,如果vertexColors等于THREE.NoColors時(shí),顏色就沒有效果了。那么就會(huì)去取材質(zhì)中color的值,這個(gè)很重要。
6、定義一條線。
定義線條,使用THREE.Line類,代碼如下所示:
var line = new THREE.Line( geometry, material, THREE.LinePieces );
第一個(gè)參數(shù)是幾何體geometry,里面包含了2個(gè)頂點(diǎn)和頂點(diǎn)的顏色。
第二個(gè)參數(shù)是線條的材質(zhì),或者是線條的屬性,表示線條以哪種方式取色。
第三個(gè)參數(shù)是一組點(diǎn)的連接方式。
7、然后,將這條線加入到場(chǎng)景中,代碼如下:
scene.add(line);
8、整體代碼:
效果圖:
5.5 線條的深度理解在Threejs中,一條線由點(diǎn),材質(zhì)和顏色組成。
點(diǎn)由THREE.Vector3表示,Threejs中沒有提供多帶帶畫點(diǎn)的函數(shù),它必須被放到一個(gè)THREE.Geometry形狀中,這個(gè)結(jié)構(gòu)中包含一個(gè)數(shù)組vertices,這個(gè)vertices就是存放無(wú)數(shù)的點(diǎn)(THREE.Vector3)的數(shù)組。
1、為了繪制一條直線,首先我們需要定義兩個(gè)點(diǎn)
var p1 = new THREE.Vector3( -1, 0, 1 ); var p2 = new THREE.Vector3( 1, 0, -1 );
2、聲明一個(gè)THREE.Geometry,并把點(diǎn)加進(jìn)去
var geometry = new THREE.Geometry(); geometry.vertices.push(p1); geometry.vertices.push(p2);
geometry.vertices的能夠使用push方法,是因?yàn)?b>geometry.vertices是一個(gè)數(shù)組。這樣geometry中就有了2個(gè)點(diǎn)了。
3、然后我們需要給線加一種材質(zhì),THREE.LineBasicMaterial。
var material = new THREE.LineBasicMaterial();
4、最終我們通過THREE.Line繪制了一條線:
var line = new THREE.Line( geometry, material, THREE.LinePieces );5.6 繪制網(wǎng)格線
我們要畫一個(gè)網(wǎng)格的坐標(biāo),那么我們就應(yīng)該找到線的點(diǎn)。把網(wǎng)格虛擬成正方形,在正方形邊界上找到幾個(gè)等分點(diǎn),用這些點(diǎn)兩兩連接,就能夠畫出整個(gè)網(wǎng)格來(lái)。
1、定義兩個(gè)點(diǎn)
// 在x軸上定義兩個(gè)點(diǎn)p1(-500,0,0),p2(500,0,0)。 geometry.vertices.push( new THREE.Vector3( - 500, 0, 0 )); geometry.vertices.push( new THREE.Vector3( 500, 0, 0 ));
2、算法
這兩個(gè)點(diǎn)決定了x軸上的一條線段,將這條線段復(fù)制20次,分別平行移動(dòng)到z軸的不同位置,就能夠形成一組平行的線段。
同理,將p1p2這條線先圍繞y軸旋轉(zhuǎn)90度,然后再?gòu)?fù)制20份,平行于z軸移動(dòng)到不同的位置,也能形成一組平行線。
for ( var i = 0; i <-= 20; i ++ ) { var line = new THREE.Line( geometry, new THREE.LineBasicMaterial({ color: 0x000000, opacity: 0.2 })); line.position.z = ( i * 50 ) - 500; scene.add( line ); var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } )); line.position.x = ( i * 50 ) - 500; line.rotation.y = 90 * Math.PI / 180; // 旋轉(zhuǎn)90度 scene.add( line ); }
3、完整代碼
效果圖:
6. 幾何形狀在創(chuàng)建物體時(shí),需要傳入兩個(gè)參數(shù),一個(gè)是幾何形狀(Geometry),另一個(gè)是材質(zhì)(Material),這一章將著重介紹幾何形狀的創(chuàng)建,第6章介紹材質(zhì),第7章介紹如何使用兩者創(chuàng)建網(wǎng)格。
幾何形狀(Geometry)最主要的功能是儲(chǔ)存了一個(gè)物體的頂點(diǎn)信息。WebGL需要程序員指定每個(gè)頂點(diǎn)的位置,而在Three.js中,可以通過指定一些特征來(lái)創(chuàng)建幾何形狀,例如使用半徑創(chuàng)建一個(gè)球體,從而省去程序員一個(gè)個(gè)指定頂點(diǎn)的工作量。
本章節(jié)將分別介紹立方體、平面、球體、圓柱體、四面體、八面體等幾何形狀,以及以三維文字作為幾何形狀的方法。本節(jié)還會(huì)介紹通過手動(dòng)定義 頂點(diǎn)位置和面片信息組成幾何形狀。
6.1 基本幾何形狀 6.1.1 立方體雖然這形狀的名字叫做立方體(CubeGeometry),但其實(shí)是長(zhǎng)方體,也就是長(zhǎng)寬高可以設(shè)置不同的值:
new THREE.CubeGeometry(width, height, depth, widthSegments, heightSegments, depthSegments)
這里,width是x方向上的長(zhǎng)度;height是y方向上的長(zhǎng)度;depth是z方向上的長(zhǎng)度;后三個(gè)參數(shù)分別是在三個(gè)方向上的分段數(shù),如widthSegments為3的話,代表x方向上水平分為三份。一般情況下不需要分段的話,可以不設(shè)置后三個(gè)參數(shù),后三個(gè)參數(shù)的缺省值為1。其他幾何形狀中的分段也是類似的,下面不做說明。
長(zhǎng)寬高
創(chuàng)建立方體直觀簡(jiǎn)單,如:new THREE.CubeGeometry(1, 2, 3);可以創(chuàng)建一個(gè)x方向長(zhǎng)度為1,y方向長(zhǎng)度為2,z方向長(zhǎng)度為3的立方體。
// 調(diào)用渲染器 var renderer = new THREE.WebGLRenderer(); renderer.setSize(800, 600); document.body.appendChild(renderer.domElement); renderer.setClearColor(0x000000); // 調(diào)用場(chǎng)景 var scene = new THREE.Scene(); // 調(diào)用相機(jī) var camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100); camera.position.set(25, 25, 25); camera.lookAt(new THREE.Vector3(0, 0, 0)); scene.add(camera); // 新建一個(gè)幾何體(長(zhǎng)方體) var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true })); scene.add(cube);
為了更好地表現(xiàn)參數(shù)效果,我們?cè)趫?chǎng)景中用長(zhǎng)度為3的紅、綠、藍(lán)線段分別表示x、y、z三個(gè)軸(這里不需要深究,后面會(huì)詳細(xì)介紹):
// 封裝一個(gè)坐標(biāo)系函數(shù) function drawAxes(scene) { // x-axis var xGeo = new THREE.Geometry(); xGeo.vertices.push(new THREE.Vector3(0, 0, 0)); xGeo.vertices.push(new THREE.Vector3(3, 0, 0)); var xMat = new THREE.LineBasicMaterial({ color: 0xff0000 }); var xAxis = new THREE.Line(xGeo, xMat); scene.add(xAxis); // y-axis var yGeo = new THREE.Geometry(); yGeo.vertices.push(new THREE.Vector3(0, 0, 0)); yGeo.vertices.push(new THREE.Vector3(0, 3, 0)); var yMat = new THREE.LineBasicMaterial({ color: 0x00ff00 }); var yAxis = new THREE.Line(yGeo, yMat); scene.add(yAxis); // z-axis var zGeo = new THREE.Geometry(); zGeo.vertices.push(new THREE.Vector3(0, 0, 0)); zGeo.vertices.push(new THREE.Vector3(0, 0, 3)); var zMat = new THREE.LineBasicMaterial({ color: 0x00ccff }); var zAxis = new THREE.Line(zGeo, zMat); scene.add(zAxis); } // 在init 函數(shù)里調(diào)用這個(gè)函數(shù) 即可在屏幕上顯示一個(gè)坐標(biāo)系了 drawAxes(scene);
在設(shè)置材質(zhì),并添加到場(chǎng)景之后具體的效果是:
物體的默認(rèn)位置是原點(diǎn),對(duì)于立方體而言,是其幾何中心在原點(diǎn)的位置。
分段
根據(jù)THREE.CubeGeometry(width, height, depth, widthSegments, heightSegments, depthSegments),的后三個(gè)參數(shù),為這個(gè)長(zhǎng)方體分段:
// x軸分兩段 y軸分兩段 z軸分三段 new THREE.CubeGeometry(1, 2, 3, 2, 2, 3)
效果圖:
注意這個(gè)分段是對(duì)六個(gè)面進(jìn)行分段,而不是對(duì)立方體的體素分段,因此在立方體的中間是不分段的,只有六個(gè)側(cè)面被分段。
6.1.2 平面這里的平面(PlaneGeometry)其實(shí)是一個(gè)長(zhǎng)方形,而并非是數(shù)學(xué)意義上無(wú)限大的平面:
new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
其中,width是x方向上的長(zhǎng)度;height是y方向上的長(zhǎng)度;后兩個(gè)參數(shù)同樣表示分段。
new THREE.PlaneGeometry(2, 4);創(chuàng)建的平面在x軸和y軸所在平面內(nèi):
var plane = new THREE.Mesh( new THREE.PlaneGeometry(2, 4), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(plane);
效果圖:
如果需要?jiǎng)?chuàng)建的平面在x軸和z軸所在的平面內(nèi),可以通過物體的旋轉(zhuǎn)來(lái)實(shí)現(xiàn),具體的做法將在下面章節(jié)介紹到。
6.1.3 球體球體(SphereGeometry)的構(gòu)造函數(shù)是:
new THREE.SphereGeometry(radius, segmentsWidth, segmentsHeight, phiStart, phiLength, thetaStart, thetaLength)
其中,radius是半徑;segmentsWidth表示經(jīng)度上的切片數(shù);segmentsHeight表示緯度上的切片數(shù);phiStart表示經(jīng)度開始的弧度;phiLength表示經(jīng)度跨過的弧度;thetaStart表示緯度開始的弧度;thetaLength表示緯度跨過的弧度。
分段
首先,我們來(lái)理解下segmentsWidth和segmentsHeight。使用var sphere = new THREE.SphereGeometry(2, 8, 6)可以創(chuàng)建一個(gè)半徑為2,經(jīng)度劃分成8份,緯度劃分成6份的球體:
var sphere = new THREE.Mesh( new THREE.SphereGeometry(2, 8, 6), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(sphere);
效果圖:
new THREE.SphereGeometry(2, 8, 16)的效果如圖:
new THREE.SphereGeometry(3, 18, 12)的效果如圖:
segmentsWidth相當(dāng)于經(jīng)度被切成了幾瓣,而segmentsHeight相當(dāng)于緯度被切成了幾層。因?yàn)樵趫D形底層的實(shí)現(xiàn)中,并沒有曲線的概念,曲線都是由多個(gè)折線近似構(gòu)成的。對(duì)于球體而言,當(dāng)這兩個(gè)值較大的時(shí)候,形成的多面體就可以近似看做是球體了。
經(jīng)度弧度
new THREE.SphereGeometry(2, 8, 6, Math.PI / 2, Math.PI / 3)表示起始經(jīng)度為Math.PI / 6,經(jīng)度跨度為Math.PI / 3。
var sphere = new THREE.Mesh( new THREE.SphereGeometry(2, 8, 6, Math.PI / 2, Math.PI / 3), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(sphere);
效果圖:
值得注意的是,這里的SegmentsWidth為8意味著對(duì)于經(jīng)度從Math.PI / 2跨過Math.PI / 3的區(qū)域內(nèi)劃分為8塊,而不是整個(gè)球體的經(jīng)度劃分成8塊后再判斷在此經(jīng)度范圍內(nèi)的部分。
緯度弧度
理解了經(jīng)度之后,緯度可以同理理解。new THREE.SphereGeometry(2, 8, 6, 0, Math.PI * 2, Math.PI / 6, Math.PI / 3)意味著緯度從Math.PI / 6跨過Math.PI / 3:
var sphere = new THREE.Mesh( // 經(jīng)度起始弧度為0度,經(jīng)度跨度為 180*2 new THREE.SphereGeometry(2, 8, 6, 0, Math.PI * 2, Math.PI / 6, Math.PI / 3), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(sphere);
效果圖:
我們?cè)賮?lái)看一個(gè)經(jīng)度緯度都改變了起始位置和跨度的例子:new THREE.SphereGeometry(2, 8, 6, Math.PI / 2, Math.PI, Math.PI / 6, Math.PI / 2):
var sphere = new THREE.Mesh( new THREE.SphereGeometry(2, 8, 6, Math.PI / 2, Math.PI, Math.PI / 6, Math.PI / 2), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(sphere);
效果圖:
6.1.4 圓形圓形(CircleGeometry)可以創(chuàng)建圓形或者扇形,其構(gòu)造函數(shù)是:
new THREE.CircleGeometry(radius, segments, thetaStart, thetaLength)
這里的參數(shù)跟繪制圓是一樣的,我們?cè)賮?lái)熟悉一下。radius是半徑;segments表示切片數(shù);thetaStart表示緯度開始的弧度;thetaLength表示緯度跨過的弧度。
看個(gè)例子: new THREE.CircleGeometry(3, 18, Math.PI / 3, Math.PI / 3 * 4)可以創(chuàng)建一個(gè)在x軸和y軸所在平面的三分之二圓的扇形:
var circle = new THREE.Mesh( new THREE.CircleGeometry(2, 18, Math.PI / 3, Math.PI / 3 * 4), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(circle);
效果圖:
6.1.5 圓柱體圓柱體(CylinderGeometry)的構(gòu)造函數(shù)是:
new THREE.CylinderGeometry(radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded)
其中,radiusTop與radiusBottom分別是頂面和底面的半徑,由此可知,當(dāng)這兩個(gè)參數(shù)設(shè)置為不同的值時(shí),實(shí)際上創(chuàng)建的是一個(gè)圓臺(tái);height是圓柱體的高度;radiusSegments與heightSegments可類比球體中的分段,一個(gè)表示底面、頂面的分段,另一個(gè)表示環(huán)面的分段;openEnded是一個(gè)布爾值,表示是否沒有頂面和底面,缺省值為false,表示有頂面和底面。
標(biāo)準(zhǔn)圓柱體
new THREE.CylinderGeometry(1.5, 1.5, 3, 18, 3)創(chuàng)建一個(gè)頂面與底面半徑都為2,高度為4的圓柱體:
var cylinder = new THREE.Mesh( new THREE.CylinderGeometry(1.5, 1.5, 3, 18, 3), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(cylinder);
效果圖:
圓臺(tái)
頂面、底面半徑不一致的時(shí)候,即是一個(gè)圓臺(tái)。將底面半徑設(shè)為2創(chuàng)建一個(gè)圓臺(tái):new THREE.CylinderGeometry(1.5, 2, 3, 18, 3):
var cylinder = new THREE.Mesh( new THREE.CylinderGeometry(1.5, 2, 3, 18, 3), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(cylinder);
效果圖:
無(wú)底面、頂面
openEnded為true的時(shí)候,將無(wú)底面、頂面。new THREE.CylinderGeometry(1.5, 1.5, 3, 18, 3, true)將創(chuàng)建一個(gè)沒有頂面與底面的圓柱:
var cylinder = new THREE.Mesh( new THREE.CylinderGeometry(1.5, 1.5, 3, 18, 3, true), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(cylinder);
效果圖:
6.1.6 正四面體、正八面體、正二十面體正四面體(TetrahedronGeometry)、正八面體(OctahedronGeometry)、正二十面體(IcosahedronGeometry)的構(gòu)造函數(shù)較為類似,分別為:
// 正四面體 new THREE.TetrahedronGeometry(radius, detail) // 正八面體 new THREE.OctahedronGeometry(radius, detail) // 正二十面體 new THREE.IcosahedronGeometry(radius, detail)
其中,radius是半徑;detail是細(xì)節(jié)層次(Level of Detail)的層數(shù),對(duì)于大面片數(shù)模型,可以控制在視角靠近物體時(shí),顯示面片數(shù)多的精細(xì)模型,而在離物體較遠(yuǎn)時(shí),顯示面片數(shù)較少的粗略模型。這里我們不對(duì)detail多作展開,一般可以對(duì)這個(gè)值缺省。
正四面體
new THREE.TetrahedronGeometry(2.5)創(chuàng)建一個(gè)半徑為2.5的正四面體:
var tetrahedron = new THREE.Mesh( new THREE.TetrahedronGeometry(2.5), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(tetrahedron);
效果圖:
正八面體
new THREE.OctahedronGeometry(2.5)創(chuàng)建一個(gè)半徑為2.5的正八面體:
var octahedron = new THREE.Mesh( new THREE.OctahedronGeometry(2.5), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(octahedron);
效果圖:
正二十面體
new THREE.IcosahedronGeometry(2.5)創(chuàng)建一個(gè)半徑為2.5的正二十面體:
var icosahedron = new THREE.Mesh( new THREE.IcosahedronGeometry(2.5), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(icosahedron);
效果圖:
6.1.7 圓環(huán)面圓環(huán)面(TorusGeometry)就是甜甜圈的形狀,其構(gòu)造函數(shù)是:
new THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments, arc)
其中,radius是圓環(huán)半徑;tube是管道半徑;radialSegments與tubularSegments分別是兩個(gè)分段數(shù),詳見上圖;arc是圓環(huán)面的弧度,缺省值為Math.PI * 2。
粗糙圓環(huán)面
new THREE.TorusGeometry(2, 0.7, 4, 8)創(chuàng)建一個(gè)粗糙的圓環(huán)面:
var torus = new THREE.Mesh( new THREE.TorusGeometry(2, 0.7, 4, 8), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(torus);
效果圖:
精細(xì)圓環(huán)面
new THREE.TorusGeometry(2, 0.7, 12, 18)創(chuàng)建一個(gè)較為精細(xì)的圓環(huán)面:
var torus = new THREE.Mesh( new THREE.TorusGeometry(2, 0.7, 12, 18), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(torus);
效果圖:
部分圓環(huán)面
new THREE.TorusGeometry(2, 0.7, 4, 8, Math.PI / 3 * 2)創(chuàng)建部分圓環(huán)面:
var torus = new THREE.Mesh( new THREE.TorusGeometry(2, 0.7, 4, 8, Math.PI / 3 * 2), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(torus);
效果圖:
6.1.8 圓環(huán)結(jié)如果說圓環(huán)面是甜甜圈,那么圓環(huán)結(jié)(TorusKnotGeometry)就是打了結(jié)的甜甜圈,其構(gòu)造參數(shù)為:
new THREE.TorusKnotGeometry(radius, tube, radialSegments, tubularSegments, p, q, heightScale)
前四個(gè)參數(shù)在圓環(huán)面中已經(jīng)有所介紹,p和q是控制其樣式的參數(shù),一般可以缺省,如果需要詳細(xì)了解,請(qǐng)學(xué)習(xí)圓環(huán)結(jié)的相關(guān)知識(shí);heightScale是在z軸方向上的縮放。
new THREE.TorusKnotGeometry(2, 0.5, 32, 8) 默認(rèn)樣式的圓環(huán)結(jié):
var torus = new THREE.Mesh( new THREE.TorusKnotGeometry(1.6, 0.4, 32, 8), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(torus);
效果圖:
6.2 文字形狀文字形狀(TextGeometry)可以用來(lái)創(chuàng)建三維的文字形狀。6.2.1 下載使用
使用文字前,需要下載和引用額外的字體庫(kù)。字體庫(kù)在three.js Github master/examples/fonts目錄下,下載里面的json文件,放在你的目錄下,然后加載。
這里,我們就以helvetiker字體為例。我們?cè)趧倓偟淖煮w庫(kù)目錄下,下載helvetiker_regular.typeface.json文件放在你的目錄下,然后用以下方法加載:
// 調(diào)用一個(gè)字體加載函數(shù) var loader = new THREE.FontLoader(); loader.load("helvetiker_regular.typeface.json", function(font) { var mesh = new THREE.Mesh( new THREE.TextGeometry("Hello", { font: font, size: 1, height: 1 }), new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true }) ); scene.add(mesh); // 寫在loader函數(shù)里面 否則不顯示 renderer.render(scene,camera); });
注意:
之前用的73dev版本的three.js,執(zhí)行代碼的時(shí)候發(fā)現(xiàn)報(bào)錯(cuò),可能是還沒有添加這個(gè)功能,所以建議去下載最新版本的three.js。
json配置文件,需要在本地服務(wù)器打開,推薦使用webstorm編輯器,因?yàn)樗蜷_html文件時(shí),就是以本地服務(wù)器的方式打開的?;蛘咴赾md命令行中輸入live-server,但需要配置,具體方法請(qǐng)點(diǎn)這里。
6.2.2 參數(shù)介紹創(chuàng)建文字形狀的流程和之前介紹的基本幾何形狀是類似的,其構(gòu)造函數(shù)是:
new THREE.TextGeometry(text, parameters)
其中,text是要顯示的文字字符串,parameters是以下參數(shù)組成的對(duì)象:
size:字號(hào)大小,一般為大寫字母的高度
height:文字的厚度
curveSegments:弧線分段數(shù),使得文字的曲線更加光滑
font:字體,默認(rèn)是"helvetiker",需對(duì)應(yīng)引用的字體文件
weight:值為"normal"或"bold",表示是否加粗
style:值為"normal"或"italics",表示是否斜體
bevelEnabled:布爾值,是否使用倒角,意為在邊緣處斜切
bevelThickness:倒角厚度
bevelSize:倒角寬度
6.2.3 示例代碼創(chuàng)建一個(gè)三維文字new THREE.TextGeometry("hello", {size: 1, height: 1})
hello
效果圖:
我們可以改變材質(zhì)和添加光照來(lái)改變顯示效果(燈光、材質(zhì)不必深究,后面會(huì)細(xì)講)
// 將材質(zhì)改為lambert材質(zhì) var material = new THREE.MeshLambertMaterial({ color: 0xffff00 }); // 加上一束方向光 var light = new THREE.DirectionalLight(0xffffff, 1); light.position.set(1, 0, 0.5); scene.add(light);
效果圖:
這里只是給大家看了一個(gè)效果,具體材質(zhì)、燈光的原理不要去深究,直接跳過,看下面的知識(shí)點(diǎn)。
6.3 自定義形狀對(duì)于Three.js沒有提供的形狀,可以通過自定義形狀來(lái)創(chuàng)建。
由于自定義形狀需要手動(dòng)指定每個(gè)頂點(diǎn)位置,以及頂點(diǎn)連接情況,如果該形狀非常復(fù)雜,程序員計(jì)算量就會(huì)比較大。這種情況,建議使用建模工具,創(chuàng)建好之后,再通過three.js導(dǎo)入到場(chǎng)景中,這樣會(huì)十分高效、方便。
自定義形狀使用的是Geometry類,它是其他如CubeGeometry、SphereGeometry等幾何形狀的父類,其構(gòu)造函數(shù)是:
new THREE.Geometry()
我們以創(chuàng)建一個(gè)梯臺(tái)為例,首先,初始化一個(gè)幾何形狀,然后設(shè)置頂點(diǎn)位置以及頂點(diǎn)連接情況。
頂面創(chuàng)建4個(gè)點(diǎn),底面創(chuàng)建4個(gè)點(diǎn),按照順時(shí)針的順序逐個(gè)創(chuàng)建
geometry創(chuàng)建點(diǎn)的時(shí)候都是push到數(shù)組vertices里面的
所以這8個(gè)點(diǎn),按照順序都有一個(gè)對(duì)應(yīng)的索引值
利用Face3的方法將3點(diǎn)連成一個(gè)三角面
看代碼
// 初始化幾何形狀 var geometry = new THREE.Geometry(); // 設(shè)置頂點(diǎn)的位置 // 頂部4個(gè)點(diǎn) geometry.vertices.push(new THREE.Vector3(-1, 2, -1)); geometry.vertices.push(new THREE.Vector3(1, 2, -1)); geometry.vertices.push(new THREE.Vector3(1, 2, 1)); geometry.vertices.push(new THREE.Vector3(-1, 2, 1)); // 底部4頂點(diǎn) geometry.vertices.push(new THREE.Vector3(-2, 0, -2)); geometry.vertices.push(new THREE.Vector3(2, 0, -2)); geometry.vertices.push(new THREE.Vector3(2, 0, 2)); geometry.vertices.push(new THREE.Vector3(-2, 0, 2)); // 設(shè)置頂點(diǎn)連接情況 // 頂面 geometry.faces.push(new THREE.Face3(0, 1, 3)); geometry.faces.push(new THREE.Face3(1, 2, 3)); // 底面 geometry.faces.push(new THREE.Face3(4, 5, 6)); geometry.faces.push(new THREE.Face3(5, 6, 7)); // 四個(gè)側(cè)面 geometry.faces.push(new THREE.Face3(1, 5, 6)); geometry.faces.push(new THREE.Face3(6, 2, 1)); geometry.faces.push(new THREE.Face3(2, 6, 7)); geometry.faces.push(new THREE.Face3(7, 3, 2)); geometry.faces.push(new THREE.Face3(3, 7, 0)); geometry.faces.push(new THREE.Face3(7, 4, 0)); geometry.faces.push(new THREE.Face3(0, 4, 5)); geometry.faces.push(new THREE.Face3(0, 5, 1));
效果圖:
總結(jié):
需要注意的是,new THREE.Vector3(-1, 2, -1)創(chuàng)建一個(gè)矢量,作為頂點(diǎn)位置追加到geometry.vertices數(shù)組中。
而由new THREE.Face3(0, 1, 3)創(chuàng)建一個(gè)三個(gè)頂點(diǎn)組成的面片,追加到geometry.faces數(shù)組中。三個(gè)參數(shù)分別是四個(gè)頂點(diǎn)在geometry.vertices中的序號(hào)。
7. 材質(zhì)材質(zhì)(material),是獨(dú)立于物體頂點(diǎn)信息之外的與渲染效果相關(guān)的屬性。通過設(shè)置材質(zhì)可以改變物體顏色、紋理貼圖、光照模式等。
下面將會(huì)為大家介紹基本材質(zhì)、兩種基于光照模型材質(zhì)、法向量作為材質(zhì)、 圖像作為材質(zhì)。
7.1 基本材質(zhì)使用基本材質(zhì)(BasicMaterial)的物體,渲染后物體的顏色,始終為該材質(zhì)的顏色,不會(huì)由于光照產(chǎn)生明暗、陰影效果。如果沒有指定材質(zhì)的顏色,則顏色是隨機(jī)的,構(gòu)造函數(shù)如下:
new THREE.MeshBasicMaterial(opt)
其中參數(shù)opt可以缺省,或者為包含各屬性的值。如,為一個(gè)黃色正方體添加一個(gè)1不透明度 (opacity):
new THREE.MeshBasicMaterial({ color: 0xffff00, opacity: 0.75 });
示例代碼:
基本材質(zhì)