Three.js入门(二)——画星空

three.js是JavaScript编写的WebGL第三方库。提供了非常多的3D显示功能。Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象。
下载地址 : http://threejs.org/

首先创建一个 HTML 文件 , 引入 three.js 引擎包

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <title>Three.js实现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()函数中设置三个主要的对象:
1.scene(场景): 场景中包含了所有的3D对象数据
2.camera(相机): 相机有位置(position),旋转(rotation)和视野属性(field of view)
3.renderer(渲染器): 决定场景中的一个物体在照相机的视角看来是什么样子

function init() { 
    // 照相机参数 
    camera = new THREE.PerspectiveCamera(80, window.innerWidth/window.innerHeight, 1, 4000); 
    // 将相机向后(即屏幕外)移 
    camera.position.z = 1000;

Camera构造器的第一个参数是视野(field of view)。这是一个角度,越大,则表示虚拟的相机镜片越宽。
第二个参数是输出的宽和高之比。这个值必须与CanvasRenderer相一致。
相机只能看见一定范围之内的物体,这个范围是由near和far来确定的,在这里分别为1和4000。因而任何比1近的物体或者比4000远的物体是不会被渲染的。
在默认情况下相机位于3D世界的起始位置(origin)0,0,0(我的上一篇博客用CSS3绘制一个旋转的立方体中有关于origin的介绍)。但是你创建的3D物体也会放置在这一点,因而默认值用处并不大。我们需要将相机向后移动一点,即给其一个正的z值(由屏幕内指向外侧)。在这里将其移动1000。

场景(Scene)

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

注意必须将相机加入到场景中。

渲染器(Renderer)

// 加入CanvasRenderer,由渲染器决定场景中的物体看起来如何,并将其画出
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// 将渲染器的canvas domElement加入到body中
document.body.appendChild(renderer.domElement);

这里的CanvasRenderer创建了它自己的DOM元素,这是一个普通的2D canvas对象,我们需要把它加入到文件的body部分才能看见它。我们想让它充满整个浏览器窗口,所以将它的大小设置为window.innerWidth和window.innerHeight。

渲染循环

接下来我们需要做的是产生粒子,加入鼠标移动监听器(mousemove listener)来追踪鼠标位置,最后设定间隔每秒调用update
函数30次。

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

创建材质和粒子
在for循环中zpos以20为步长从-1000增加至1000。
在循环中,我们创建一个新的材质(material)然后新建一个粒子。粒子有一个position属性,它有x, y, z三个值。
我们给每一个粒子一个随机的x和y位置,将它的z位置设置为zpos。
接着将粒子加入到particles数组中,保证能够跟踪所有的粒子并在update循环中对它们进行操作。

function makeParticles() {
    var particle, material;
    
    // 将z坐标从-1000(最远处)逐步增加至1000(相机所在处)
    // 每一个位置加入一个随机的粒子
    for (var zpos = -1000; zpos < 1000; zpos += 20) {

        // 创建一个粒子材质,向其传入颜色及我们定义的粒子渲染函数
        material = new THREE.ParticleCanvasMaterial( {
            // color: 0xffffff,
            color:   getRandomColor(),
            program: particleRender
        });

        // 创建粒子
        particle = new THREE.Particle(material);
        
        // 赋给它一个位于-500至500之间的随机x和y值
        particle.position.x = Math.random() * 1000 - 500;
        particle.position.y = Math.random() * 1000 - 500;

        particle.position.z = zpos;

        // 将其放大一点
        particle.scale.x = particle.scale.y = 10;

        // 把它加入到场景中
        scene.add(particle);

        // 将粒子加入到我们的particles数组中   
        particles.push(particle);
    }
}

update函数

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

onMouseMouse函数

//鼠标移动时调用 
function onMouseMove(event){ 
   mouseX = event.clientX; 
   mouseY = event.clientY; 
}

画圆

three.js并没有内置圆形粒子材质,所以需要告诉它如何去画一个圆。我们是通过给particleRender
传递必要的canvas绘图API来做到这一点的。

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

把一个函数传递给材质(material)似乎有点奇怪,但这实际上是一个非常灵活的系统。它获得了canvas上下文的引用,所以我们可以画出任何想要的形状。同时canvas的坐标系统将会根据粒子进行缩放和平移,因此我们只需要以起点(origin)为中心画图即可。

移动粒子

现在来看一下updateParticles
函数,它将会在我们每个的更新周期中调用,就在我们进行渲染之前。

// 根据鼠标位置移动所有的粒子
function updateParticles() {
    
    // 对每个粒子进行迭代处理
    for (var i = 0; i < particles.length; i++) {
        particle = particles[i];

        // 根据mouseY值进行移动
        particle.position.z += mouseY * 0.1;

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

鼠标越低,mouseY值越大,粒子移动速度越快。

整合代码

<!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,

        //跟踪鼠标位置
        mouseX = 0, mouseY = 0 ,

        //一个数组,用于存储我们的粒子
        particles = [];

        // 初始化

        init();

        function init() {
            //照相机的参数
            camera = new THREE.PerspectiveCamera(80, window.innerWidth/window.innerHeight, 1,4000);

            //将相机屏幕外移动
            camera.position.z = 1000;

            //场景
            scene = new THREE.Scene();
            
            scene.add(camera);

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


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

            makeParticles();

            //设置鼠标移动监听
            document.addEventListener( 'mousemove', onMouseMove, false );

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


        }

        function update(){
            //作用是将粒子向前移动
            updateParticles();
            //从相机的视角渲染场景
            renderer.render(scene,camera);
        }   

        function makeParticles() {

            var particle, material; //创建粒子和材质

            for (var zpos = -1000; zpos < 1000; zpos += 20) {
                
                //创建材质
                material = new THREE.ParticleCanvasMaterial( {
                    
                    color: getRandomColor(),
                    // color: 0xffffff,
                    
                    program: particleRender, 

                });

                //创建粒子
                particle = new THREE.Particle(material);

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

                particle.position.z = zpos;

                //将其放大一点
                particle.scale.x = particle.scale.y = 10;

                //放入到场景中
                scene.add(particle)

                //将粒子加入到particles数组中
                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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容