摘要:學(xué)習(xí)筆記使用粒子系統(tǒng)模擬時(shí)空隧道本例的運(yùn)行結(jié)果如圖時(shí)空隧道演示地址的粒子系統(tǒng)的粒子系統(tǒng)主要是依靠精靈體來創(chuàng)建的,要實(shí)現(xiàn)中的粒子系統(tǒng)創(chuàng)建,一般有兩種方式。
WebGL three.js學(xué)習(xí)筆記 使用粒子系統(tǒng)模擬時(shí)空隧道
本例的運(yùn)行結(jié)果如圖:
three.js的粒子系統(tǒng)主要是依靠精靈體來創(chuàng)建的,要實(shí)現(xiàn)three.js中的粒子系統(tǒng)創(chuàng)建,一般有兩種方式。
第一種是在場景中使用很多歌THREE.Sprite創(chuàng)建單個(gè)的精靈,這樣創(chuàng)建的每一個(gè)精靈體,我們都可以多帶帶對(duì)它們進(jìn)行操作,同時(shí)我們也可以用一個(gè)THREE.Group把他們放在一起,整合起來一起操作。具有很高的自主性。但同時(shí)也是需要大量的性能支持與開發(fā)上的不便利性,所以這里我選擇了第二種方式。
第二種創(chuàng)建粒子系統(tǒng)是依靠點(diǎn)云的方式,點(diǎn)云就是很多很多點(diǎn)組成的一個(gè)東西,點(diǎn)云里面的每一個(gè)頂點(diǎn)都可以看做一個(gè)粒子,而這個(gè)粒子我們就可以使用紋理去對(duì)它美化,或者是使用坐標(biāo)變化來變化出好看的粒子系統(tǒng),這種創(chuàng)建方式的缺點(diǎn)是不能對(duì)每一個(gè)粒子多帶帶進(jìn)行操作,但是相比第一種卻給我們提供了更多的方便。
搭建場景點(diǎn)云的創(chuàng)建方法和普通的幾何體差不多,首先需要一個(gè)材質(zhì)THREE.PointsMaterial,可以設(shè)置每個(gè)粒子的大小size,顏色color,透明transparent等等屬性。然后再用THREE.Points(geometry, material)這個(gè)方法就可以創(chuàng)建出點(diǎn)云了。
let cloud = new THREE.Points(geom, material);//創(chuàng)建點(diǎn)云
如果我們給了Points(),geometry這個(gè)參數(shù),這個(gè)點(diǎn)云會(huì)按照我們定義好的幾何體的頂點(diǎn)去創(chuàng)建粒子。
,比如geometry是一個(gè)Box,那么這個(gè)點(diǎn)云就會(huì)有8粒子,分別分布在正方體的8個(gè)頂點(diǎn)上。如果我們不用geometry,我們就需要手動(dòng)給點(diǎn)云創(chuàng)建很多的頂點(diǎn),包括定義它們的坐標(biāo),這里我們也是用一個(gè)定義好的幾何體去創(chuàng)建粒子。
//創(chuàng)建點(diǎn)云 function createPointCloud(geom,color) { let material = new THREE.PointsMaterial({ color: color, size: 3, transparent: true, blending: THREE.AdditiveBlending,//混合的模式,可以讓很多的粒子的背景得到很好的融合,而不是互相干擾 map: generateSprite()//取得漸變的canvas紋理 }); let cloud = new THREE.Points(geom, material);//創(chuàng)建點(diǎn)云 cloud.sortParticles = true;//可以讓所有粒子的Z軸得到正確擺放,不會(huì)互相遮擋 return cloud; }
函數(shù)形參傳過來的geom,我們使用的一個(gè)類似于管道的幾何體TorusGeometry
TorusGeometry的構(gòu)造函數(shù)如下:
THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments, arc)
**radius:圓環(huán)半徑
tube:管道半徑
radialSegments:徑向的分段數(shù)
tubularSegments:管的分段數(shù)
arc:圓環(huán)面的弧度,缺省值為Math.PI 2*
let geom = new THREE.TorusGeometry( controls.radius, controls.tube, Math.round(controls.radialSegments), Math.round(controls.tubularSegments) );//TorusGeometry幾何體,管道狀的幾何體,里面的參數(shù)設(shè)置都是菜單面板上面的參數(shù)
這里的參數(shù)主要就是我們要在菜單面板中去更改的值,
controls = new function () { this.radius = 100;//整個(gè)大圓隧道的半徑 this.tube = 10;//管道的半徑 this.radialSegments = 40;//管道的段數(shù),值越大,創(chuàng)造的物體更精細(xì),也更消耗性能 this.tubularSegments = 200;//整個(gè)大圓隧道的段數(shù),值越大,創(chuàng)造的物體更精細(xì),也更消耗性能 this.useParticle = true;//是否使用粒子系統(tǒng)創(chuàng)造幾何體 this.rotationSpeed = 0.003;//攝像機(jī)的速度 this.color = 0xffffff;//此顏色會(huì)與材質(zhì)中紋理本身的顏色做乘法,最后的結(jié)果就是渲染出來的顏色 }
如果我們要想創(chuàng)建一個(gè)好看的時(shí)空隧道還需要它的map屬性,去賦給它一個(gè)紋理,這樣每一個(gè)粒子都會(huì)比純色更美觀。紋理的話使用圖片也是可以的,在這里我選擇了制作一個(gè)漸變的畫布來當(dāng)做紋理,即generateSprite()這個(gè)函數(shù)的返回值。
generateSprite函數(shù)代碼(主要用到的是canvas的繪圖函數(shù),js的基礎(chǔ)部分):
function generateSprite() { let canvas = document.createElement("canvas"); canvas.width = 16; canvas.height = 16; let context = canvas.getContext("2d");//得到canvas的繪圖上下文 let gradient = context.createRadialGradient(canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, canvas.width / 2);//顏色漸變圖形 gradient.addColorStop(0, "rgba(255,255,255,1)");//從內(nèi)向外的第一漸變顏色,設(shè)置為白色 gradient.addColorStop(0.2, "rgba(0,125,125,1)");//從內(nèi)向外的第二漸變顏色,設(shè)置為淺藍(lán)色 gradient.addColorStop(0.5, "rgba(0,64,0,1)");//從內(nèi)向外的第三漸變顏色,設(shè)置為綠色 gradient.addColorStop(1, "rgba(0,0,0,0.1)");//最外層的漸變顏色,為背景色 context.fillStyle = gradient; context.fillRect(0, 0, canvas.width, canvas.height); let texture = new THREE.Texture(canvas);//將得到的畫好的canvas作為紋理圖片 texture.needsUpdate = true;//需要設(shè)置更新,否則會(huì)沒有效果 return texture; }
注意texture.needsUpdate = true這句話,否則是渲染不出來的。
到此,我們就可以開始繪制場景
this.draw = function () { cameraInit = true;//調(diào)用此函數(shù)后,對(duì)攝像機(jī)進(jìn)行一次初始化 if (obj) scene.remove(obj);//如果場景的隧道已經(jīng)存在,先移除 let geom = new THREE.TorusGeometry(controls.radius, controls.tube, Math.round(controls.radialSegments), Math.round(controls.tubularSegments));//TorusGeometry幾何體,管道狀的幾何體,里面的參數(shù)設(shè)置都是菜單面板上面的參數(shù) //使用粒子系統(tǒng)渲染幾何體 if (controls.useParticle) { obj = createPointCloud(geom,controls.color); obj.rotation.x = Math.PI/2;//旋轉(zhuǎn)90度以后,更加方便觀測 } else {//使用普通材質(zhì)系統(tǒng)渲染幾何體 obj = createMesh(geom); obj.rotation.x = Math.PI/2; } scene.add(obj); }
場景有了以后,攝像機(jī)還是不會(huì)動(dòng),沒有一種在時(shí)空隧道的感覺,所以這里想辦法讓攝像機(jī)在這個(gè)隧道的中間,沿著這個(gè)幾何體的形狀去移動(dòng)。
因?yàn)楣艿啦豢磞軸的話,其實(shí)還是一個(gè)圓形,所以可以使用圓形的參數(shù)方程來讓攝像機(jī)沿著這個(gè)函數(shù)去運(yùn)動(dòng)。讓y軸始終不變就可以。
let angle = 0;//初始角度 angle = angle + controls.rotationSpeed;//相機(jī)移動(dòng)的速度 camera.position.set(controls.radius*Math.sin(angle),0, controls.radius*Math.cos(angle));//讓相機(jī)按照一個(gè)圓形軌跡運(yùn)動(dòng) //可以理解為圓形的參數(shù)方程x=rsinα,y=rcosα,
即設(shè)置相機(jī)的x為rsinα,z為rcosα,y軸是一直都為0的。這里的r為整個(gè)隧道的半徑,α就是當(dāng)前移動(dòng)的角度。
雖然這樣可以讓相機(jī)開始移動(dòng)了,但是相機(jī)的目標(biāo)我們還沒有設(shè)置,我們需要讓相機(jī)在移動(dòng)的過程中,始終看向前方,這樣才有一種在時(shí)空隧道中漫游的感覺。但是three.js的相機(jī)運(yùn)動(dòng)軌跡插件似乎在這里不好用,所以就想到了用其他方式實(shí)現(xiàn)。
我們既然已經(jīng)用相機(jī)運(yùn)動(dòng)的圓的軌跡方程,也能很容易想到相機(jī)lookAt的方向其實(shí)就是沿著圓運(yùn)動(dòng)的切線方向。所以只需要求攝像機(jī)運(yùn)動(dòng)的當(dāng)前位置的切線就可以了。
這里用到的是向量的點(diǎn)乘,坐標(biāo)的點(diǎn)乘公式x1y2+x2y1,如果結(jié)果為0,就可以得到這個(gè)向量的垂直向量,我們要求的切線肯定就是垂直于半徑的。因?yàn)槲覀兊膟軸一直不變的,所以點(diǎn)乘公式的y我們變?yōu)閦。我們首先是讓相機(jī)的位置減去隧道的中心(0,0,0),得到指向中心的向量,也就是半徑,然后再用一個(gè)向量與它點(diǎn)乘為0,這個(gè)向量方向就是垂直于半徑的了,也就是切線的方向。
function look(){ let view = new THREE.Vector3(camera.position.x, camera.position.y, camera.position.z);//計(jì)算當(dāng)前攝像機(jī)位置點(diǎn)到世界中心點(diǎn)的向量 let vertical = (new THREE.Vector3(view.z, 0, -1.0 * view.x)).normalize(); //兩個(gè)向量的點(diǎn)積如果為0,則兩個(gè)向量垂直,公式為x1*y2+x2*y1=0, //這里的Y軸用Z軸代替。計(jì)算出垂直向量以后用normalize()化成單位向量 camera.lookAt(camera.position.x+vertical.x,0, camera.position.z+vertical.z);//camera.lookAt的值設(shè)置為 剛剛的單位向量加在當(dāng)前攝像機(jī)的位置 //這樣就實(shí)現(xiàn)了在攝像機(jī)在旋轉(zhuǎn)時(shí),一直朝前看。 }
最后得到的這個(gè)單位向量我們?cè)偌由袭?dāng)前相機(jī)的位置,就可以設(shè)置為相機(jī)lookAt的值。
注意我們?cè)诿看武秩镜臅r(shí)候都要去改變這個(gè)值,因?yàn)橄鄼C(jī)的位置一直都在變化的,所以我們要把它封裝成一個(gè)函數(shù),方便在渲染的時(shí)候調(diào)用。
其他的,相機(jī),場景的初始化代碼:
function initThree() { //渲染器初始化 renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(0x000000); document.getElementById("WebGL-output").appendChild(renderer.domElement);//將渲染添加到div中 //初始化攝像機(jī),這里使用透視投影攝像機(jī) camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000); camera.up.x = 0;//設(shè)置攝像機(jī)的上方向?yàn)槟膫€(gè)方向,這里定義攝像的上方為Y軸正方向 camera.up.y = 1; camera.up.z = 0; look();//計(jì)算攝像機(jī)在當(dāng)前位置應(yīng)該對(duì)準(zhǔn)的目標(biāo)點(diǎn),即camera.lookAt的設(shè)置 //初始化場景 scene = new THREE.Scene(); }
至此,場景基本已經(jīng)構(gòu)建完成了。
完整的代碼如下:Sprite Tunnel
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/103915.html
摘要:紋理貼圖的應(yīng)用以及實(shí)現(xiàn)一個(gè)太陽系的自轉(zhuǎn)公轉(zhuǎn)點(diǎn)擊查看演示地址中的紋理紋理貼圖是通過將圖像應(yīng)用到對(duì)象的一個(gè)或多個(gè)面,來為對(duì)象添加細(xì)節(jié)的一種方法。 紋理貼圖的應(yīng)用以及實(shí)現(xiàn)一個(gè)太陽系的自轉(zhuǎn)公轉(zhuǎn) 點(diǎn)擊查看demo演示 demo地址:https://nsytsqdtn.github.io/d... three.js中的紋理 紋理貼圖是通過將圖像應(yīng)用到對(duì)象的一個(gè)或多個(gè)面,來為3D對(duì)象添加細(xì)節(jié)的一種...
摘要:學(xué)習(xí)學(xué)習(xí)筆記點(diǎn)擊查看演示地址簡單網(wǎng)格材質(zhì)是一種不受渲染時(shí)使用的顏色影響的材質(zhì),它只與自己每一個(gè)面從內(nèi)到外的法向量有關(guān)。法向量在中用處十分廣泛,光的反射,以及三維圖形的紋理映射都與這個(gè)有關(guān)。 WebGL學(xué)習(xí)----Three.js學(xué)習(xí)筆記(5) 點(diǎn)擊查看demo演示 Demo地址:https://nsytsqdtn.github.io/d... 簡單網(wǎng)格材質(zhì) MeshNormalMat...
閱讀 1735·2021-11-02 14:47
閱讀 3682·2019-08-30 15:44
閱讀 1367·2019-08-29 16:42
閱讀 1763·2019-08-26 13:53
閱讀 962·2019-08-26 10:41
閱讀 3496·2019-08-23 17:10
閱讀 637·2019-08-23 14:24
閱讀 1752·2019-08-23 11:59