摘要:首先,下載并在的中使用然后,我們需要準(zhǔn)備一個(gè)模型,在函數(shù)中,創(chuàng)建變量,用于導(dǎo)入模型導(dǎo)入模型的時(shí)候,接受兩個(gè)參數(shù),第一個(gè)表示模型路徑,第二個(gè)表示完成導(dǎo)入后的回調(diào)函數(shù),一般我們需要在這個(gè)回調(diào)函數(shù)中將導(dǎo)入的模型添加到場景中。
9. 動(dòng)畫
9.1 實(shí)現(xiàn)動(dòng)畫效果 9.1.1 動(dòng)畫原理在本章之前,所有畫面都是靜止的,本章將介紹如果使用Three.js進(jìn)行動(dòng)態(tài)畫面的渲染。此外,將會(huì)介紹一個(gè)Three.js作者寫的另外一個(gè)庫,用來觀測每秒幀數(shù)(FPS)。
在這里,我們將動(dòng)態(tài)畫面簡稱為動(dòng)畫(animation)。正如動(dòng)畫片的原理一樣,動(dòng)畫的本質(zhì)是利用了人眼的視覺暫留特性,快速地變換畫面,從而產(chǎn)生物體在運(yùn)動(dòng)的假象。而對于Three.js程序而言,動(dòng)畫的實(shí)現(xiàn)也是通過在每秒鐘多次重繪畫面實(shí)現(xiàn)的。
為了衡量畫面切換速度,引入了每秒幀數(shù)FPS(Frames Per Second)的概念,是指每秒畫面重繪的次數(shù)。FPS越大,則動(dòng)畫效果越平滑,當(dāng)FPS小于20時(shí),一般就能明顯感受到畫面的卡滯現(xiàn)象。
那么FPS是不是越大越好呢?其實(shí)也未必。當(dāng)FPS足夠大(比如達(dá)到60),再增加幀數(shù)人眼也不會(huì)感受到明顯的變化,反而相應(yīng)地就要消耗更多資源(比如電影的膠片就需要更長了,或是電腦刷新畫面需要消耗計(jì)算資源等等)。因此,選擇一個(gè)適中的FPS即可。
NTSC標(biāo)準(zhǔn)的電視FPS是30,PAL標(biāo)準(zhǔn)的電視FPS是25,電影的FPS標(biāo)準(zhǔn)為24。而對于Three.js動(dòng)畫而言,一般FPS在30到60之間都是可取的。
9.1.2 setInterval方法如果要設(shè)置特定的FPS(雖然嚴(yán)格來說,即使使用這種方法,JavaScript也不能保證幀數(shù)精確性),可以使用JavaScript DOM定義的方法:
setInterval(fn,mesc)
其中,fn是每過msec毫秒執(zhí)行的函數(shù),如果將fn定義為重繪畫面的函數(shù),就能實(shí)現(xiàn)動(dòng)畫效果。setInterval函數(shù)返回一個(gè)變量timer,如果需要停止重繪,需要使用clearInterval方法,并傳入該變量timer,具體的做法為:
1、首先,在init函數(shù)中定義每20毫秒執(zhí)行draw函數(shù)的setInterval,返回值記錄在全局變量timer中:
timer = setInterval(draw,20);
2、在draw函數(shù)中,我們首先設(shè)定在每幀中的變化(畢竟,如果每幀都是相同的,即使重繪再多次,還是不會(huì)有動(dòng)畫的效果),這里我們讓場景中的長方體繞y軸轉(zhuǎn)動(dòng)。然后,執(zhí)行渲染:
function draw() { // 每過20ms 就會(huì)執(zhí)行一次這個(gè)函數(shù),rotation.y就會(huì)加0.01 // 轉(zhuǎn)完360度就會(huì)進(jìn)行取余,所以就會(huì)一直轉(zhuǎn)下去 mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2); renderer.render(scene, camera); }
這樣,每20毫秒就會(huì)調(diào)用一次draw函數(shù),改變長方體的旋轉(zhuǎn)值,然后進(jìn)行重繪。最終得到的效果就是FPS為50的旋轉(zhuǎn)長方體。
3、我們在HTML中添加兩個(gè)按鈕,一個(gè)是按下后停止動(dòng)畫,另一個(gè)是按下后繼續(xù)動(dòng)畫:
4、對應(yīng)的stop和start函數(shù)為:
function stop() { if (timer !== null) { clearInterval(timer); timer = null; } } function start() { if (timer == null) { clearInterval(timer); timer = setInterval(draw, 20); } }
完整代碼:
動(dòng)畫效果
效果圖:
9.1.3 requestAnimationFrame方法大多數(shù)時(shí)候,我們并不在意多久重繪一次,這時(shí)候就適合用requestAnimationFrame方法了。它告訴瀏覽器在合適的時(shí)候調(diào)用指定函數(shù),通??赡苓_(dá)到60FPS。
requestAnimationFrame同樣有對應(yīng)的cancelAnimationFrame取消動(dòng)畫:
function stop() { if (timer !== null) { cancelAnimationFrame(timer); timer = null; } }
和setInterval不同的是,由于requestAnimationFrame只請求一幀畫面,因此,除了在init函數(shù)中需要調(diào)用,在被其調(diào)用的函數(shù)中需要再次調(diào)用requestAnimationFrame:
function draw() { mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2); renderer.render(scene, camera); timer = requestAnimationFrame(draw); }
因?yàn)?b>requestAnimationFrame較為“年輕”,因而一些老的瀏覽器使用的是試驗(yàn)期的名字:mozRequestAnimationFrame、webkitRequestAnimationFrame、msRequestAnimationFrame,為了支持這些瀏覽器,我們最好在調(diào)用之前,先判斷是否定義了requestAnimationFrame以及上述函數(shù):
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; window.requestAnimationFrame = requestAnimationFrame;
完整代碼:
動(dòng)畫效果
setInterval和requestAnimationFrame的區(qū)別:
setInterval方法與requestAnimationFrame方法的區(qū)別較為微妙。一方面,最明顯的差別表現(xiàn)在setInterval可以手動(dòng)設(shè)定FPS,而requestAnimationFrame則會(huì)自動(dòng)設(shè)定FPS;但另一方面,即使是setInterval也不能保證按照給定的FPS執(zhí)行,在瀏覽器處理繁忙時(shí),很可能低于設(shè)定值。當(dāng)瀏覽器達(dá)不到設(shè)定的調(diào)用周期時(shí),requestAnimationFrame采用跳過某些幀的方式來表現(xiàn)動(dòng)畫,雖然會(huì)有卡滯的效果但是整體速度不會(huì)拖慢,而setInterval會(huì)因此使整個(gè)程序放慢運(yùn)行,但是每一幀都會(huì)繪制出來;
總而言之,requestAnimationFrame適用于對于時(shí)間較為敏感的環(huán)境(但是動(dòng)畫邏輯更加復(fù)雜),而setInterval則可在保證程序的運(yùn)算不至于導(dǎo)致延遲的情況下提供更加簡潔的邏輯(無需自行處理時(shí)間)。
9.2 使用stat.js記錄FPSstat.js是Three.js的作者Mr.Doob的另一個(gè)有用的JavaScript庫。很多情況下,我們希望知道實(shí)時(shí)的FPS信息,從而更好地監(jiān)測動(dòng)畫效果。這時(shí)候,stat.js就能提供一個(gè)很好的幫助,它占據(jù)屏幕中的一小塊位置(如左上角),效果為:,單擊后顯示每幀渲染時(shí)間:。
首先,我們需要下載stat.js文件,可以在https://github.com/mrdoob/stats.js/blob/master/build/stats.min.js找到。下載后,將其放在項(xiàng)目文件夾下,然后在HTML中引用:
在頁面初始化的時(shí)候,對其初始化并將其添加至屏幕一角。這里,我們以左上角為例:
var stat = null; function init() { stat = new Stats(); stat.domElement.style.position = "absolute"; stat.domElement.style.left = "0px"; stat.domElement.style.top = "0px"; document.body.appendChild(stat.domElement); // Three.js init ... }
然后,在上一節(jié)介紹的動(dòng)畫重繪函數(shù)draw中調(diào)用stat.begin();與stat.end();分別表示一幀的開始與結(jié)束:
function draw() { stat.begin(); mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2); renderer.render(scene, camera); stat.end(); }
完整代碼:
9.3 彈球案例stats
本節(jié)我們將使用一個(gè)彈球的例子來完整地學(xué)習(xí)使用動(dòng)畫效果。
1、首先,我們把通用的框架部分寫好,按照之前的方法實(shí)現(xiàn)動(dòng)畫重繪函數(shù),并加入stat.js庫:
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; window.requestAnimationFrame = requestAnimationFrame; var stat; var renderer; var scene; var camera; var light; function init() { stat = new Stats(); stat.domElement.style.position = "absolute"; stat.domElement.style.left= "0px"; stat.domElement.style.top = "0px"; document.body.appendChild(stat.domElement); renderer = new THREE.WebGLRenderer({ canvas: document.getElementById("mainCanvas") }); scene = new THREE.Scene(); timer = requestAnimationFrame(draw); } function draw() { stat.begin(); renderer.render(scene, camera); timer = requestAnimationFrame(draw); stat.end(); } function stop() { if (timer !== null) { cancelAnimationFrame(timer); timer = null; } }
2、然后,為了實(shí)現(xiàn)彈球彈動(dòng)的效果,我們創(chuàng)建一個(gè)球體作為彈球模型,創(chuàng)建一個(gè)平面作為彈球反彈的平面。為了在draw函數(shù)中改變彈球的位置,我們可以聲明一個(gè)全局變量ballMesh,以及彈球半徑ballRadius。
var ballMesh; var ballRadius = 0.5;
3、在init函數(shù)中添加球體和平面,使彈球位于平面上,平面采用棋盤格圖像作材質(zhì):
// 加載貼圖 texture = THREE.ImageUtils.loadTexture("images/chess.png", {}, function() { renderer.render(scene, camera); }); texture.wrapS = texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(4, 4); // 平面模型 var plane = new THREE.Mesh(new THREE.PlaneGeometry(8, 8), new THREE.MeshLambertMaterial({ map: texture })); // 沿x軸旋轉(zhuǎn)-90度 plane.rotation.x = Math.PI / -2; scene.add(plane); // 球模型 ballMesh = new THREE.Mesh(new THREE.SphereGeometry(ballRadius, 40, 16), new THREE.MeshLambertMaterial({ color: 0xffff00 }));
4、為了記錄彈球的狀態(tài),我們至少需要位置、速度、加速度三個(gè)矢量,為了簡單起見,這里彈球只做豎直方向上的自由落體運(yùn)動(dòng),因此位置、速度、加速度只要各用一個(gè)變量表示。其中,位置就是ballMesh.position.y,不需要額外的變量,因此我們在全局聲明速度v和加速度a:
var v = 0; var a = -0.01;
這里,a = -0.01代表每幀小球向y方向負(fù)方向移動(dòng)0.01個(gè)單位。
5、一開始,彈球從高度為maxHeight(自己定義的一個(gè)高度)處自由下落,掉落到平面上時(shí)會(huì)反彈,并且速度有損耗。當(dāng)速度很小的時(shí)候,彈球會(huì)在平面上作振幅微小的抖動(dòng),所以,當(dāng)速度足夠小時(shí),我們需要讓彈球停止跳動(dòng)。因此,定義一個(gè)全局變量表示是否在運(yùn)動(dòng),初始值為false:
var isMoving = false;
6、在HTML中定義一個(gè)按鈕,點(diǎn)擊按鈕時(shí),彈球從最高處下落:
7、下面就是最關(guān)鍵的函數(shù)了,在draw函數(shù)中,需要判斷當(dāng)前的isMoving值,并且更新小球的速度和位置:
function draw() { stat.begin(); if (isMoving) { ballMesh.position.y += v; // a= -0.01 v += a; // 當(dāng)小球從定義的高度落到小球停在平面時(shí)的高度的時(shí)候 if (ballMesh.position.y <= ballRadius) { // 讓小球彈起來 v = -v * 0.9; } // 當(dāng)小球的速度小于設(shè)定值的時(shí)候 if (Math.abs(v) < 0.001) { // 讓它停下來 isMoving = false; ballMesh.position.y = ballRadius; } } renderer.render(scene, camera); requestAnimationFrame(draw); stat.end(); }
完整代碼:
彈彈彈
效果圖:
10. 外部模型前面我們了解到,使用Three.js創(chuàng)建常見幾何體是十分方便的,但是對于人或者動(dòng)物這樣非常復(fù)雜的模型使用幾何體組合就非常麻煩了。因此,Three.js允許用戶導(dǎo)入由3ds Max等工具制作的三維模型,并添加到場景中。
本章以3ds Max為例,介紹如何導(dǎo)入外部模型。
10.1 支持格式Three.js有一系列導(dǎo)入外部文件的輔助函數(shù),是在three.js之外的,使用前需要額外下載,在https://github.com/mrdoob/three.js/tree/master/examples/js/loaders可以找到,選擇對應(yīng)的模型加載器,系在下來。
*.obj是最常用的模型格式,導(dǎo)入*.obj文件需要OBJLoader.js;導(dǎo)入帶*.mtl材質(zhì)的*.obj文件需要MTLLoader.js以及OBJMTLLoader.js。另有PLYLoader.js、STLLoader.js等分別對應(yīng)不同格式的加載器,可以根據(jù)模型格式自行選擇。
目前,支持的模型格式有:
*.obj
*.obj, *.mtl
*.dae
*.ctm
*.ply
*.stl
*.wrl
*.vtk
10.2 無材質(zhì)的模型本節(jié)中,我們將將導(dǎo)出的沒有材質(zhì)的模型使用Three.js導(dǎo)入場景中。
首先,下載OBJLoader.js并在HTML的
中使用:然后,我們需要準(zhǔn)備一個(gè)*.obj模型,在init函數(shù)中,創(chuàng)建loader變量,用于導(dǎo)入模型:
var loader = new THREE.OBJLoader();
loader導(dǎo)入模型的時(shí)候,接受兩個(gè)參數(shù),第一個(gè)表示模型路徑,第二個(gè)表示完成導(dǎo)入后的回調(diào)函數(shù),一般我們需要在這個(gè)回調(diào)函數(shù)中將導(dǎo)入的模型添加到場景中。
loader.load("../lib/port.obj", function(obj) { //儲(chǔ)存到全局變量中 mesh = obj; scene.add(obj); });
可以看到一個(gè)沒有材質(zhì)的茶壺
我們在重繪函數(shù)中讓茶壺旋轉(zhuǎn):
function draw() { renderer.render(scene, camera); mesh.rotation.y += 0.01; if (mesh.rotation.y > Math.PI * 2) { mesh.rotation.y -= Math.PI * 2; } }
可以看到在某些角度時(shí),好像有些面片沒有被繪制出來,因而后方的茶嘴似乎穿越到前方了:
這是由于默認(rèn)的情況下,只有正面的面片被繪制,而如果需要雙面繪制,需要這樣設(shè)置:
var loader = new THREE.OBJLoader(); loader.load("port.obj", function(obj) { obj.traverse(function(child) { if (child instanceof THREE.Mesh) { child.material.side = THREE.DoubleSide; } }); mesh = obj; scene.add(obj); });
完整代碼:
效果圖:
10.3 有材質(zhì)的模型10.3.1 代碼中設(shè)置材質(zhì)模型的材質(zhì)可以有兩種定義方式,一種是在代碼中導(dǎo)入模型后設(shè)置材質(zhì),另一種是在建模軟件中導(dǎo)出材質(zhì)信息。下面,我們將分別介紹這兩種方法。
這種方法與上一節(jié)類似,不同之處在于回調(diào)函數(shù)中設(shè)置模型的材質(zhì):
var loader = new THREE.OBJLoader(); loader.load("port.obj", function(obj) { obj.traverse(function(child) { if (child instanceof THREE.Mesh) { /* 修改這里以下的代碼 */ child.material = new THREE.MeshLambertMaterial({ color: 0xffff00, side: THREE.DoubleSide }); /* 修改這里以上的代碼 */ } }); mesh = obj; scene.add(obj); });
效果圖:
10.3.2 建模軟件中設(shè)置材質(zhì)導(dǎo)出3D模型的時(shí)候,選擇導(dǎo)出port.obj模型文件以及port.mtl材質(zhì)文件。
現(xiàn)在,我們不再使用OBJLoader.js,而是使用MTLLoader.js與OBJMTLLoader.js,并且要按該順序引用:
調(diào)用的方法也略有不同:
var mtlLoader = new THREE.MTLLoader(); mtlLoader.setPath(""); mtlLoader.load("port.mtl", function(materials) { materials.preload(); // model loader var objLoader = new THREE.OBJLoader(); objLoader.setMaterials(materials); objLoader.setPath(""); objLoader.load("port.obj", function(object) { object.position.y = -95; // if has object, add to scene if (object.children.length > 0) { scene.add(object.children[0]); } }); });
完整代碼:
導(dǎo)出時(shí)自帶的效果圖:
11. 光與影圖像渲染的豐富效果很大程度上也要?dú)w功于光與影的利用。真實(shí)世界中的光影效果非常復(fù)雜,但是其本質(zhì)—光的傳播原理卻又是非常單一的,這便是自然界繁簡相成的又一例證。為了使計(jì)算機(jī)模擬豐富的光照效果,人們提出了幾種不同的光源模型(環(huán)境光、平行光、點(diǎn)光源、聚光燈等),在不同場合下組合利用,將能達(dá)到很好的光照效果。
在Three.js中,光源與陰影的創(chuàng)建和使用是十分方便的。在學(xué)會(huì)了如何控制光影的基本方法之后,如果能將其靈活應(yīng)用,將能使場景的渲染效果更加豐富逼真。在本章中,我們將探討四種常用的光源(環(huán)境光、點(diǎn)光源、平行光、聚光燈)和陰影帶來的效果,以及如何去創(chuàng)建使用光影。
11.1 環(huán)境光(AmbientLight)環(huán)境光是指場景整體的光照效果,是由于場景內(nèi)若干光源的多次反射形成的亮度一致的效果,通常用來為整個(gè)場景指定一個(gè)基礎(chǔ)亮度。因此,環(huán)境光沒有明確的光源位置,在各處形成的亮度也是一致的。
在設(shè)置環(huán)境光時(shí),只需指定光的顏色:
var light = new THREE.AmbientLight(hex); scene.add(light);
其中hex是十六進(jìn)制的RGB顏色信息,如紅色表示為0xff0000
但是,如果此時(shí)場景中沒有物體,只添加了這個(gè)環(huán)境光,那么渲染的結(jié)果仍然是一片黑。所以,我們添加兩個(gè)長方體看下效果:
// 創(chuàng)建一個(gè)綠色的正方體 var greenCube = new THREE.Mesh(new THREE.CubeGeometry(2, 2, 2), new THREE.MeshLambertMaterial({color: 0x00ff00})); greenCube.position.x = 3; scene.add(greenCube); // 創(chuàng)建一個(gè)白色的正方體 var whiteCube = new THREE.Mesh(new THREE.CubeGeometry(2, 2, 2), new THREE.MeshLambertMaterial({color: 0xffffff})); whiteCube.position.x = -3; scene.add(whiteCube);
效果如圖:
如果想讓環(huán)境光暗些,可以將其設(shè)置為new THREE.AmbientLight(0xcccccc)等,效果為:
11.2 點(diǎn)光源(PointLight)點(diǎn)光源是不計(jì)光源大小,可以看作一個(gè)點(diǎn)發(fā)出的光源。點(diǎn)光源照到不同物體表面的亮度是線性遞減的,因此,離點(diǎn)光源距離越遠(yuǎn)的物體會(huì)顯得越暗。
點(diǎn)光源的構(gòu)造函數(shù)是:
THREE.PointLight(hex, intensity, distance);
其中,hex是光源十六進(jìn)制的顏色值;intensity是亮度,缺省值為1,表示100%亮度;distance是光源最遠(yuǎn)照射到的距離,缺省值為0。
創(chuàng)建點(diǎn)光源并將其添加到場景中的完整做法是:
var light = new THREE.PointLight(0xffffff, 2, 100); light.position.set(0, 1.5, 2); scene.add(light);
效果圖:
注意,這里光在每個(gè)面上的亮度是不同的,對于每個(gè)三角面片,將根據(jù)三個(gè)頂點(diǎn)的亮度進(jìn)行插值。
11.3 平行光(DirectionalLight)我們都知道,太陽光常常被看作平行光,這是因?yàn)橄鄬Φ厍蛏衔矬w的尺度而言,太陽離我們的距離足夠遠(yuǎn)。對于任意平行的平面,平行光照射的亮度都是相同的,而與平面所在位置無關(guān)。
平行光的構(gòu)造函數(shù)是:
THREE.DirectionalLight(hex, intensity)
其中,hex是光源十六進(jìn)制的顏色值;intensity是亮度,缺省值為1,表示100%亮度。
此外,對于平行光而言,設(shè)置光源位置尤為重要。
var light = new THREE.DirectionalLight(); light.position.set(2, 5, 3); scene.add(light);
注意,這里設(shè)置光源位置并不意味著所有光從(2, 5, 3)點(diǎn)射出(如果是的話,就成了點(diǎn)光源),而是意味著,平行光將以矢量(-2, -5, -3)的方向照射到所有平面。因此,平面亮度與平面的位置無關(guān),而只與平面的法向量相關(guān)。只要平面是平行的,那么得到的光照也一定是相同的。
示例代碼:
效果圖:
11.4 聚光燈(SpotLight)可以看出,聚光燈是一種特殊的點(diǎn)光源,它能夠朝著一個(gè)方向投射光線。聚光燈投射出的是類似圓錐形的光線,這與我們現(xiàn)實(shí)中看到的聚光燈是一致的。
其構(gòu)造函數(shù)為:
THREE.SpotLight(hex, intensity, distance, angle, exponent)
相比點(diǎn)光源,多了angle和exponent兩個(gè)參數(shù)。angle是聚光燈的張角,缺省值是Math.PI / 3,最大值是Math.PI / 2;exponent是光強(qiáng)在偏離target的衰減指數(shù)(target需要在之后定義,缺省值為(0, 0, 0)),缺省值是10。
在調(diào)用構(gòu)造函數(shù)之后,除了設(shè)置光源本身的位置,一般還需要設(shè)置target:
light.position.set(x1, y1, z1); light.target.position.set(x2, y2, z2);
除了設(shè)置light.target.position的方法外,如果想讓聚光燈跟著某一物體移動(dòng)(就像真的聚光燈!),可以target指定為該物體:
var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), new THREE.MeshLambertMaterial({color: 0x00ff00})); var light = new THREE.SpotLight(0xffff00, 1, 100, Math.PI / 6, 25); light.target = cube;
示例代碼:
效果圖:
11.5 陰影明暗是相對的,陰影的形成也就是因?yàn)楸戎車@得的光照更少。因此,要形成陰影,光源必不可少。
在Three.js中,能形成陰影的光源只有THREE.DirectionalLight與THREE.SpotLight;而相對地,能表現(xiàn)陰影效果的材質(zhì)只有THREE.LambertMaterial與THREE.PhongMaterial。因而在設(shè)置光源和材質(zhì)的時(shí)候,一定要注意這一點(diǎn)。
下面,我們以聚光燈為例,在之前的基礎(chǔ)上增加陰影效果。
首先,我們需要在初始化時(shí),告訴渲染器渲染陰影:
renderer.shadowMapEnabled = true;
然后,對于光源以及所有要產(chǎn)生陰影的物體調(diào)用:
// 上面的案例,產(chǎn)生陰影的物體是正方體 cube.castShadow = true;
對于接收陰影的物體調(diào)用:
// 接收陰影的物體是平面 plan.receiveShadow = true;
比如場景中一個(gè)平面上有一個(gè)正方體,想要讓聚光燈照射在正方體上,產(chǎn)生的陰影投射在平面上,那么就需要對聚光燈和正方體調(diào)用castShadow = true,對于平面調(diào)用receiveShadow = true。
以上就是產(chǎn)生陰影效果的必要步驟了,不過通常還需要設(shè)置光源的陰影相關(guān)屬性,才能正確顯示出陰影效果。
對于聚光燈,需要設(shè)置shadowCameraNear、shadowCameraFar、shadowCameraFov三個(gè)值,類比我們在第二章學(xué)到的透視投影照相機(jī),只有介于shadowCameraNear與shadowCameraFar之間的物體將產(chǎn)生陰影,shadowCameraFov表示張角。
對于平行光,需要設(shè)置shadowCameraNear、shadowCameraFar、shadowCameraLeft、shadowCameraRight、shadowCameraTop以及shadowCameraBottom六個(gè)值,相當(dāng)于正交投影照相機(jī)的六個(gè)面。同樣,只有在這六個(gè)面圍成的長方體內(nèi)的物體才會(huì)產(chǎn)生陰影效果。
為了看到陰影照相機(jī)的位置,通??梢栽谡{(diào)試時(shí)開啟light.shadowCameraVisible = true。
如果想要修改陰影的深淺,可以通過設(shè)置shadowDarkness,該值的范圍是0到1,越小越淺。
另外,這里實(shí)現(xiàn)陰影效果的方法是Shadow Mapping,即陰影是作為渲染前計(jì)算好的貼圖貼上去的,因而會(huì)受到貼圖像素大小的限制。所以可以通過設(shè)置shadowMapWidth與shadowMapHeight值控制貼圖的大小,來改變陰影的精確度。
而如果想實(shí)現(xiàn)軟陰影的效果,可以通過renderer.shadowMapSoft = true;方便地實(shí)現(xiàn)。
完整代碼:
效果圖:
補(bǔ)充問題本地服務(wù)器
1、下載安裝node.js,因?yàn)閚ode.js自帶npm
2、打開電腦命令行工具,輸入npm install -g live-server 全局安裝
3、在需要運(yùn)行文件的文件夾下,按住shift鍵,點(diǎn)擊鼠標(biāo)右鍵在此處打開命令窗口
4、輸入live-server回車
nmp官方說明
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/90110.html
摘要:個(gè)人前端文章整理從最開始萌生寫文章的想法,到著手開始寫,再到現(xiàn)在已經(jīng)一年的時(shí)間了,由于工作比較忙,更新緩慢,后面還是會(huì)繼更新,現(xiàn)將已經(jīng)寫好的文章整理一個(gè)目錄,方便更多的小伙伴去學(xué)習(xí)。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 個(gè)人前端文章整理 從最開始萌生寫文章的想法,到著手...
摘要:一般說來,對于制圖建模軟通常使正交投影,這樣不會(huì)因?yàn)橥队岸淖兾矬w比例而對于其他大多數(shù)應(yīng)用,通常使用透視投影,因?yàn)檫@更接近人眼的觀察效果。 showImg(https://segmentfault.com/img/remote/1460000012581680?w=1920&h=1080); 1. 概述 1.1 什么是WebGL? WebGL是在瀏覽器中實(shí)現(xiàn)三維效果的一套規(guī)范 想要使用...
閱讀 1972·2021-09-27 13:35
閱讀 3499·2019-08-30 14:16
閱讀 2556·2019-08-30 10:52
閱讀 917·2019-08-29 16:35
閱讀 1465·2019-08-29 15:22
閱讀 3748·2019-08-23 18:21
閱讀 3203·2019-08-23 18:00
閱讀 3199·2019-08-23 16:50