运用three.js写的漂浮立方

来源:互联网 发布:气动快速接头 淘宝 编辑:程序博客网 时间:2024/06/10 08:43

这是本人在CSDN上的第一篇文章,因为刚刚写了一个three.js的Demo,就传上来了,参考的是国外Karim Maaloul这位大佬的飞机游戏,给了我比较大的启发,和小伙伴们分享下。


three.js

three.js是JavaScript编写的WebGL第三方库。提供了非常多的3D显示功能。 —— [ 百度百科 ]

个人感觉这个库还是比较给力的,运用了图形建模的许多思想,一些在3D建模里的相机、灯光、贴图都被运用到three里,他的引用库文件可以在GitHub上面找到并下载,格式是zip的,解压后在build文件夹的就是,一个压缩,一个没压缩,在练习阶段建议用没压缩的,点进去它的结构一步了然,方便理解原理.

Demo图

漂浮立方静态图

漂浮立方Demo展示

HTML

页面的HTML部分需要引入three.js的库文件:

<script type="text/javascript" src="js/three.js"></script>

下面是HTML部分完整的代码:

<!DOCTYPE html><html lang="en"><head>    <meta charset="utf-8">    <title>漂浮立方</title>    <style type="text/css">        *{            margin: 0;            padding: 0;        }        #world{            width:100%;            height: 100%;            position: absolute;            overflow: hidden;            background: linear-gradient(#A4D3EE,#AEEEEE);        }    </style></head><body><div id="world"></div></body><script type="text/javascript" src="js/three.min.js"></script></html>

上面的代码在body里嵌套了一层div,three会在这个div里自动添加canvas,我们所有效果就在这里面,在css样式中,使用了两种背景色,作为渐变效果

JavaScript

注意three.js有四大核心要素:场景、相机、渲染器、模型对象,这四样要素缺一不可

  • 场景
    场景可以理解为舞台,是所有元素运作的环境,所有事物添加到场景中最终才能被我们看到,新建一个场景代码如下:
var scene = new THREE.Scene();

在创建好每个模型后往往用如下方法将模型置入场景中:

scene.add(e);
  • 相机

相机有正交、透视、通用三种,可以理解为我们的眼睛,这里我们使用透视相机,代码如下:

var camera = new THREE.PerspectiveCamera(a1,a2,a3,a4);

四个参数:
视角:setFov(fov)设置视角的角度
宽高比:相机所看到的宽和高的比例
near:相机的近视角
far:表示相机的远视角

  • 渲染器
    三维空间里的物体映射到二维平面的过程被称为三维渲染。 一般来说我们都把进行渲染操作的软件叫做渲染器,渲染器需要加载到DOM节点中,创建渲染器代码如下:
//新建渲染器var renderer = new THREE.WebGLRenderer();//body中加载渲染引擎document.body.appendChild(renderer.domElement);
  • 模型对象
    对象模型是一种事物的组合:几何体、材质、网格,three使用网格系统来建立几何模型,我们创建一个物体通常先创建相应的几何体,再设置材质,把他们添加到网格中,形成物体,在下面会具体讲解

漂浮立方

首先我们创建一个初始化init()函数,它使得冗长的代码能够以清晰的结构呈现出来,我们主要的函数都会在init里加载

window.addEventListener('load',init,false);function init(){    //场景布局    createScene();    //灯光    createLight();    //场中的对象    //海    createEarth();    //带云的天空    createSky();    //循环函数    loop();}
  • 场景布局

场景,相机,渲染器都是在createScene函数中创建,这是我们着手构造“世界”的准备工作,代码如下:

var scene,renderer,camera;function createScene(){    //新建场景    scene = new THREE.Scene();    //新建渲染器(允许透明度、抗锯齿、允许投影)    renderer = new THREE.WebGLRenderer({alpha: true,antialias:true,shadowMapEnabled:true});    //重置画板大小    renderer.setSize(window.innerWidth,window.innerHeight);    //相机(60度角,满屏宽高,视区1到10000)    camera = new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,1,10000);    camera.position.set(0,300,600);    camera.lookAt(Sea);    camera.lookAt(scene.position);    scene.add(camera);    //在场景中添加雾效果    scene.fog = new THREE.Fog(0xf7d9aa, 100, 950);    //world中加载渲染引擎    var world = document.getElementById('world');    world.appendChild(renderer.domElement);    //屏幕监听,在调整视窗时更新渲染器及相机    window.addEventListener('resize',handleWindowResize,false);}

我们通过一个handleWindowResize函数来使得渲染器自适应窗口的大小,在改变视窗时渲染器更新渲染,代码如下:

function handleWindowResize(){    renderer.setSize(window.innerWidth,window.innerHeight);    camera.aspect = window.innerWidth/window.innerHeight;    camera.updateProjectionMatrix();}
  • 灯光

灯光使得我们的物体能被看见,同样使用createLight函数来创建灯光,代码如下:

var hemisphereLight,shadowLight;function createLight(){    //创建半球灯对象    hemisphereLight = new THREE.HemisphereLight(0xaaaaaa,0x000000,0.9);    //创建平行光对象    shadowLight = new THREE.DirectionalLight(0xffffff,0.9);    shadowLight.position.set(150,350,350);    shadowLight.castShadow = true;    scene.add(hemisphereLight);    scene.add(shadowLight);}

记得将创建好的组件都添加到场景scene中,有时候我们在检查效果时,往往发现组件并没有被使用,相机的位置与视角也是影响效果的重要因素,我们事先都需要设置好

  • 场中的对象
    我们使用构造函数Earth来构造出场景中的地球类,现在我们开始构造,代码如下:
Earth = function(){    //创建圆(上下半径600、高800、半径数40、垂直段数10)    var geom = new THREE.SphereGeometry(600,80,60);    //在x轴上旋转圆柱    geom.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI/2));    //创造材质    var mat = new THREE.MeshPhongMaterial({        color:'#104E8B',        transparent:true,        opacity:0.8,        shading:THREE.FlatShading    });    //创建网格    this.mesh = new THREE.Mesh(geom,mat);    //允许接受阴影    this.mesh.receiveShadow = true;    //通过合并定点保证波的连续性    geom.mergeVertices();    //得到顶点数量    var l = geom.vertices.length;    //建立一个数组储存与每个顶点相关的新数据    this.waves = [];    for (var i = 0;i<l;i++){        //得到每个顶点        var v = geom.vertices[i];        //存储与之相关的数据        this.waves.push({            x: v.x,            y: v.y,            z: v.z,            //随机角            ang:Math.random()*Math.PI*2,            //随机距离            amp:5 + Math.random()*15,            //0.016到0.048间速度            speed:0.016 + Math.random*0.032        });    }}

为了使得地球看上去更加有意思一点我们使它的的表面凹凸不平,所以我们为它增加一个方法moveWaves,这里用到了原型继承的知识,代码如下:

Earth.prototype.moveWaves = function(){    //得到顶点    var verts = this.mesh.geometry.vertices;    var l = verts.length;    for (var i=0; i<l; i++){        var v = verts[i];        //获取相关的波数据        var vprops = this.waves[i];        //更新顶点的位置        v.x = vprops.x + Math.cos(vprops.ang)*vprops.amp;        v.y = vprops.y + Math.sin(vprops.ang)*vprops.amp;        //增加下一帧的角度        vprops.ang += vprops.speed;    }    earth.mesh.rotation.z += 0.0005;}

moveWaves方法会在循环函数中调用,一次来对每一帧进行渲染,就能模拟出地球表面凹凸不平的效果

下面我们使用createEarth函数来实例化地球类,并设置其位置,代码如下:

var earth;function createEarth(){    earth = new Earth();    //让海洋圆柱在屏幕下方位置    earth.mesh.position.y = -600;    earth.mesh.position.z = 500;    scene.add(earth.mesh);}

我们用简单的随机立方体来模拟漂浮在天空中的云朵,接下来我们创建Cloud的构造函数,来创建云朵类,代码如下:

Cloud = function(){    //创建一个3D对象用作容器    this.mesh = new THREE.Object3D();    //创建立体几何,用来复制云    var geom = new THREE.BoxGeometry(20,20,20);    //材质    var mat = new THREE.MeshPhongMaterial({        color:'#FFFFFF'    });    //复制几何,次数随机,造云    //最大6、最小3    var nBlocs = 3 + Math.floor(Math.random()*3);    for (var i = 0;i<nBlocs;i++){        //复制网格,用来创建几何图形        var m = new THREE.Mesh(geom,mat);        //设置每个方块的位置和旋转量,随机        m.position.x = i*15;        m.position.y = Math.random()*10;        m.position.z = Math.random()*10;        m.rotation.z = Math.random()*Math.PI*2;        m.rotation.y = Math.random()*Math.PI*2;        //设置方块大小,随机        var s = 0.2 + Math.random()*0.3;        m.scale.set(s,s,s);        //允许每个方块投影并接受阴影        m.castShadow = true;        m.receiveShadow = true;        this.mesh.add(m);    }}

云朵在天空中是看似随机有规律的的运动着的,我们还需要创建一个天空的构造函数来对云进行分布,代码如下:

Sky = function(){    //3D容器    this.mesh = new THREE.Object3D();    //在天空中选择一些云    this.nClouds = 20;    //规定统一角度放置云    var stepAngle = Math.PI*2 / this.nClouds;    //创建云    for (var i = 0;i<this.nClouds;i++){        //实例化一个云对象        var c = new Cloud();        //设置每片云的位置和旋转        //利用简单的三角学知识        var a = stepAngle*i;//云的最后一个角度        var h = 650 + Math.random()*100;//轴中心和云的距离(云高),700到950之间        //运用三角函数知识(以轴心为原点r边为h,y = sin(x轴夹角度)*h)        //把极坐标转换成笛卡尔坐标        c.mesh.position.y = Math.sin(a)*h;        c.mesh.position.x = Math.cos(a)*h;        //根据位置旋转云        c.mesh.rotation.z = a + Math.PI/2;        //随机云的深度        c.mesh.position.z = -400 - Math.random()*400;        //为每片云设置随机规模比例        var s = 1 + Math.random()*2;        c.mesh.scale.set(s,s,s);        this.mesh.add(c.mesh);    }}

从上面的代码中我们使用了三角函数的知识,这需要我们有良好的数学基础,实际上我们在编写的算法就是数学运算,如果在这方面有所欠缺,应该要补一补啦,但建议缺哪里补哪里

下面对天空的构造函数进行实例化,使我们的漂浮立方呈现出来,代码如下:

function createSky(){    sky = new Sky();    sky.mesh.position.y = -600;    sky.mesh.position.z = 700;    scene.add(sky.mesh);}

写到这里我们的工作基本完成的差不多了,现在我们的立方还不是真正意义上的“漂浮”,我们需要场景里的对象运动起来,所以我们要编写一个loop函数来使得渲染器循环渲染我们的每一帧,我们要是用一个requestAnimationFrame函数来调用渲染器的循环渲染,相当于递归,代码如下:

function loop(){    earth.moveWaves();    sky.mesh.rotation.z += 0.0008;    //渲染    renderer.render(scene,camera);    //调用循环渲染函数    requestAnimationFrame(loop);}

现在我们的所有的代码已经完成了,具体的效果点击链接可以查看>漂浮立方

这是我在CSDN里的第一篇微博文章,不知道有什么地方写的不好,有很多细节可能都没有写的清除,可以留言我,其实写这篇微博体现最多的就是我们在编写three的效果时的一些实现思路和结构思路,个人认为这是非常重要的事情,这能让我在编写实现时不会发生思维混乱,以至于写着写着就不知道写到哪里,结构化的编程思路是我们需要学习的一种思维,这会让我在理解实现上受益不菲。