Three.js入門(mén)(二)——畫(huà)星空

three.js是JavaScript編寫(xiě)的WebGL第三方庫(kù)誊辉。提供了非常多的3D顯示功能。Three.js 是一款運(yùn)行在瀏覽器中的 3D 引擎邀跃,你可以用它創(chuàng)建各種三維場(chǎng)景蛙紫,包括了攝影機(jī)坑傅、光影、材質(zhì)等各種對(duì)象蒜茴。
下載地址 : http://threejs.org/

首先創(chuàng)建一個(gè) HTML 文件 , 引入 three.js 引擎包

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <title>Three.js實(shí)現(xiàn)3D空間粒子效果</title>
    <style type="text/css">
    body {
        background-color: #000000;
        margin: 0px;
        overflow: hidden;
    }
    </style>
    <script src="scripts/three.js"></script>
</head>
<body> 
</body>
</html>

變量

定義全局變量:

// the main three.js components
 var camera, scene, renderer, 
// to keep track of the mouse position 
 mouseX = 0, mouseY = 0,  
// an array to store our particles in
 particles = [];  
// let's get going! 
 init();
初始化three.js

為了使用three.js粉私,我們需要在init()函數(shù)中設(shè)置三個(gè)主要的對(duì)象:
1.scene(場(chǎng)景): 場(chǎng)景中包含了所有的3D對(duì)象數(shù)據(jù)
2.camera(相機(jī)): 相機(jī)有位置(position),旋轉(zhuǎn)(rotation)和視野屬性(field of view)
3.renderer(渲染器): 決定場(chǎng)景中的一個(gè)物體在照相機(jī)的視角看來(lái)是什么樣子

function init() { 
    // 照相機(jī)參數(shù) 
    camera = new THREE.PerspectiveCamera(80, window.innerWidth/window.innerHeight, 1, 4000); 
    // 將相機(jī)向后(即屏幕外)移 
    camera.position.z = 1000;

Camera構(gòu)造器的第一個(gè)參數(shù)是視野(field of view)毡鉴。這是一個(gè)角度,越大憎瘸,則表示虛擬的相機(jī)鏡片越寬幌甘。
第二個(gè)參數(shù)是輸出的寬和高之比痊项。這個(gè)值必須與CanvasRenderer相一致鞍泉。
相機(jī)只能看見(jiàn)一定范圍之內(nèi)的物體,這個(gè)范圍是由near和far來(lái)確定的边器,在這里分別為1和4000托修。因而任何比1近的物體或者比4000遠(yuǎn)的物體是不會(huì)被渲染的睦刃。
在默認(rèn)情況下相機(jī)位于3D世界的起始位置(origin)0,0,0(我的上一篇博客用CSS3繪制一個(gè)旋轉(zhuǎn)的立方體中有關(guān)于origin的介紹)。但是你創(chuàng)建的3D物體也會(huì)放置在這一點(diǎn)际长,因而默認(rèn)值用處并不大工育。我們需要將相機(jī)向后移動(dòng)一點(diǎn)郁轻,即給其一個(gè)正的z值(由屏幕內(nèi)指向外側(cè))好唯。在這里將其移動(dòng)1000。

場(chǎng)景(Scene)

// the scene contains all the 3D object data
scene = new THREE.Scene();
scene.add(camera);

注意必須將相機(jī)加入到場(chǎng)景中蜕提。

渲染器(Renderer)

// 加入CanvasRenderer靶端,由渲染器決定場(chǎng)景中的物體看起來(lái)如何凛膏,并將其畫(huà)出
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// 將渲染器的canvas domElement加入到body中
document.body.appendChild(renderer.domElement);

這里的CanvasRenderer創(chuàng)建了它自己的DOM元素猖毫,這是一個(gè)普通的2D canvas對(duì)象须喂,我們需要把它加入到文件的body部分才能看見(jiàn)它坞生。我們想讓它充滿(mǎn)整個(gè)瀏覽器窗口,所以將它的大小設(shè)置為window.innerWidth和window.innerHeight又兵。

渲染循環(huán)

接下來(lái)我們需要做的是產(chǎn)生粒子沛厨,加入鼠標(biāo)移動(dòng)監(jiān)聽(tīng)器(mousemove listener)來(lái)追蹤鼠標(biāo)位置升熊,最后設(shè)定間隔每秒調(diào)用update
函數(shù)30次级野。

makeParticles(); 
// add the mouse move listener 
document.addEventListener('mousemove', onMouseMove, false); 
// 每秒渲染30次 
setInterval(update, 1000/30);

創(chuàng)建材質(zhì)和粒子
在for循環(huán)中zpos以20為步長(zhǎng)從-1000增加至1000蓖柔。
在循環(huán)中风纠,我們創(chuàng)建一個(gè)新的材質(zhì)(material)然后新建一個(gè)粒子。粒子有一個(gè)position屬性镐捧,它有x, y, z三個(gè)值懂酱。
我們給每一個(gè)粒子一個(gè)隨機(jī)的x和y位置誊抛,將它的z位置設(shè)置為zpos拗窃。
接著將粒子加入到particles數(shù)組中,保證能夠跟蹤所有的粒子并在update循環(huán)中對(duì)它們進(jìn)行操作九默。

function makeParticles() {
    var particle, material;
    
    // 將z坐標(biāo)從-1000(最遠(yuǎn)處)逐步增加至1000(相機(jī)所在處)
    // 每一個(gè)位置加入一個(gè)隨機(jī)的粒子
    for (var zpos = -1000; zpos < 1000; zpos += 20) {

        // 創(chuàng)建一個(gè)粒子材質(zhì)驼修,向其傳入顏色及我們定義的粒子渲染函數(shù)
        material = new THREE.ParticleCanvasMaterial( {
            // color: 0xffffff,
            color:   getRandomColor(),
            program: particleRender
        });

        // 創(chuàng)建粒子
        particle = new THREE.Particle(material);
        
        // 賦給它一個(gè)位于-500至500之間的隨機(jī)x和y值
        particle.position.x = Math.random() * 1000 - 500;
        particle.position.y = Math.random() * 1000 - 500;

        particle.position.z = zpos;

        // 將其放大一點(diǎn)
        particle.scale.x = particle.scale.y = 10;

        // 把它加入到場(chǎng)景中
        scene.add(particle);

        // 將粒子加入到我們的particles數(shù)組中   
        particles.push(particle);
    }
}

update函數(shù)

function update() { 
    updateParticles(); 
    renderer.render( scene, camera );
 }

onMouseMouse函數(shù)

//鼠標(biāo)移動(dòng)時(shí)調(diào)用 
function onMouseMove(event){ 
   mouseX = event.clientX; 
   mouseY = event.clientY; 
}

畫(huà)圓

three.js并沒(méi)有內(nèi)置圓形粒子材質(zhì)勉躺,所以需要告訴它如何去畫(huà)一個(gè)圓觅丰。我們是通過(guò)給particleRender
傳遞必要的canvas繪圖API來(lái)做到這一點(diǎn)的妇萄。

function particleRender(context) {
    context.beginPath();
    context.arc(0, 0, 1, 0, 2*Math.PI, true);
    context.fill();
}

把一個(gè)函數(shù)傳遞給材質(zhì)(material)似乎有點(diǎn)奇怪,但這實(shí)際上是一個(gè)非常靈活的系統(tǒng)轻掩。它獲得了canvas上下文的引用唇牧,所以我們可以畫(huà)出任何想要的形狀聚唐。同時(shí)canvas的坐標(biāo)系統(tǒng)將會(huì)根據(jù)粒子進(jìn)行縮放和平移,因此我們只需要以起點(diǎn)(origin)為中心畫(huà)圖即可扮惦。

移動(dòng)粒子

現(xiàn)在來(lái)看一下updateParticles
函數(shù)崖蜜,它將會(huì)在我們每個(gè)的更新周期中調(diào)用客峭,就在我們進(jìn)行渲染之前桃笙。

// 根據(jù)鼠標(biāo)位置移動(dòng)所有的粒子
function updateParticles() {
    
    // 對(duì)每個(gè)粒子進(jìn)行迭代處理
    for (var i = 0; i < particles.length; i++) {
        particle = particles[i];

        // 根據(jù)mouseY值進(jìn)行移動(dòng)
        particle.position.z += mouseY * 0.1;

        // 如果粒子過(guò)近,將其移至后面
        if (particle.position.z > 1000)
            particle.position.z -= 2000;
    }
}

鼠標(biāo)越低闪檬,mouseY值越大购笆,粒子移動(dòng)速度越快同欠。

整合代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>drawStar</title>
    <style type="text/css">
        body{background-color: #000;margin: 0;overflow: hidden;}
    </style>
</head>
<body>
    <script src="three.min.js"></script>
    <script type="text/javascript">
    //全局變量
        //three的三要素 
        var camera,scene,renderer,

        //跟蹤鼠標(biāo)位置
        mouseX = 0, mouseY = 0 ,

        //一個(gè)數(shù)組样傍,用于存儲(chǔ)我們的粒子
        particles = [];

        // 初始化

        init();

        function init() {
            //照相機(jī)的參數(shù)
            camera = new THREE.PerspectiveCamera(80, window.innerWidth/window.innerHeight, 1,4000);

            //將相機(jī)屏幕外移動(dòng)
            camera.position.z = 1000;

            //場(chǎng)景
            scene = new THREE.Scene();
            
            scene.add(camera);

            //渲染器
            renderer = new THREE.CanvasRenderer();
            //設(shè)置2Dcanvas的大小
            renderer.setSize(window.innerWidth,window.innerHeight);


            //將渲染器的canvas domElement加入到body中
            document.body.appendChild(renderer.domElement);

            makeParticles();

            //設(shè)置鼠標(biāo)移動(dòng)監(jiān)聽(tīng)
            document.addEventListener( 'mousemove', onMouseMove, false );

            //每秒渲染30次
            setInterval(update,1000/30);


        }

        function update(){
            //作用是將粒子向前移動(dòng)
            updateParticles();
            //從相機(jī)的視角渲染場(chǎng)景
            renderer.render(scene,camera);
        }   

        function makeParticles() {

            var particle, material; //創(chuàng)建粒子和材質(zhì)

            for (var zpos = -1000; zpos < 1000; zpos += 20) {
                
                //創(chuàng)建材質(zhì)
                material = new THREE.ParticleCanvasMaterial( {
                    
                    color: getRandomColor(),
                    // color: 0xffffff,
                    
                    program: particleRender, 

                });

                //創(chuàng)建粒子
                particle = new THREE.Particle(material);

                particle.position.x = Math.random() * 1000 - 500;
                particle.position.y = Math.random() * 1000 - 500;

                particle.position.z = zpos;

                //將其放大一點(diǎn)
                particle.scale.x = particle.scale.y = 10;

                //放入到場(chǎng)景中
                scene.add(particle)

                //將粒子加入到particles數(shù)組中
                particles.push(particle)

            }
        }

        function particleRender( context ) {
            context.beginPath();
            context.arc( 0, 0, 1, 0, Math.PI * 2,true );
            context.fill();
        };

        function getRandomColor() {

            var r = 255*Math.random()|0,
                g = 255*Math.random()|0,
                b = 255*Math.random()|0,
            // console.log( parseInt(r, 16) );
            return '0x' + parseInt(r, 16) + parseInt(g, 16) + parseInt( b, 16);
        }

        function updateParticles() {

            for (var i = 0; i < particles.length; i++) {
                particle = particles[i];

                particle.position.z += mouseY * 0.1;

                if (particle.position.z>1000) 

                    particle.position.z -=2000;
            }
        }

        function onMouseMove( event ) {
            mouseX = event.cilentX;
            mouseY = event.clientY;
        }
    </script>
</body>
</html>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市铺遂,隨后出現(xiàn)的幾起案子衫哥,更是在濱河造成了極大的恐慌,老刑警劉巖襟锐,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撤逢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡粮坞,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)莫杈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)互例,“玉大人,你說(shuō)我怎么就攤上這事筝闹∠边叮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵丁存,是天一觀的道長(zhǎng)肩杈。 經(jīng)常有香客問(wèn)我柴我,道長(zhǎng)解寝,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任艘儒,我火速辦了婚禮聋伦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘界睁。我一直安慰自己觉增,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布翻斟。 她就那樣靜靜地躺著逾礁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪访惜。 梳的紋絲不亂的頭發(fā)上嘹履,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天腻扇,我揣著相機(jī)與錄音,去河邊找鬼砾嫉。 笑死幼苛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的焕刮。 我是一名探鬼主播舶沿,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼配并!你這毒婦竟也來(lái)了括荡?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤溉旋,失蹤者是張志新(化名)和其女友劉穎一汽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體低滩,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡召夹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恕沫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片监憎。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖婶溯,靈堂內(nèi)的尸體忽然破棺而出鲸阔,到底是詐尸還是另有隱情,我是刑警寧澤迄委,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布褐筛,位于F島的核電站,受9級(jí)特大地震影響叙身,放射性物質(zhì)發(fā)生泄漏渔扎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一信轿、第九天 我趴在偏房一處隱蔽的房頂上張望晃痴。 院中可真熱鬧,春花似錦财忽、人聲如沸倘核。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)紧唱。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間漏益,已是汗流浹背酬凳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遭庶,地道東北人宁仔。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像峦睡,于是被迫代替她去往敵國(guó)和親翎苫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容