[WebAR和WebVR学习之路]从Three.js开始掌握WebAR和WebVR的开发

《为什么要学习Three.js》

Three.js是JavaScript编写的WebGL第三方库。提供了非常多的3D显示功能。
Three.js对WebGL进行了封装,省去了很多WebGL底层的代码,使得编写Web的3D程序十分方便。
现如今,WebAR和WebVR逐渐进入人们的视野。由于Web不需要下载程序的简便性使得更多的人倾向于在Web页面开发VR/AR应用(快速捕捉客户)。因此,学习WebGL的开发使得一个VR/AR开发者更具有竞争力。
今天,我们将简单的从几个方面来讲解Three.js,已达到快速入门的目的。

知识需求:

简单的Web前端知识(HTML、CSS和JS)
简单的OpenGL的知识(包括OpenGL ES和WebGL)

《一个简单的Three.js页面框架》

由于Three.js需要一个简单的web服务器来测试,所以我们需要搭建一个简单的HTTP服务器。
对于大多数人来说,搭建web服务器是一个十分简单的事情,但对一些新手我们可以使用集成环境来快速搭建web服务器来测试(像是eclipse中集成的tomcat服务器一样)。
在这里我推荐使用Web Storm软件,搞前端开发的程序员对此软件并不陌生,其扩展性高使用十分便捷。
下面我们开始搭建一个简单的Three.js页面的框架。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Example 01.01 - Bacis skeleton</title>
    <script type="text/javascript"
            src="../libs/three.js"></script>
    <script type="text/javascript"
            src="../libs/jquery-3.1.1.js"></script>
    <style>
        body{
            /*设置页面的样式*/
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<!—用于显示WebGL输出的div-->
<div id="WebGL-output">

</div>
<!—以下为Three.js的具体代码-->
<script type="text/javascript">
    //在加载过后自动运行的函数
    $(function () {
        //在此输入Three.js代码
    });
</script>

</body>
</html>

《渲染并创建一个三维对象》

在上文的注释 在此输入Three.js代码的地方删除该注释,输入以下代码。
运行测试一下,效果如图。



后面我们将会讲解这一段简单的代码。

$(function () {
        var scene=new THREE.Scene();

        var camera=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000);

        var renderer=new THREE.WebGLRenderer();
        renderer.setClearColor(0xEEEEEE,0.5);
        renderer.setSize(window.innerWidth,window.innerHeight);

        var axes=new THREE.AxisHelper(20);
        scene.add(axes);

        var planeGeometry = new THREE.PlaneGeometry(60,20,1,1);
        var planeMaterial = new THREE.MeshBasicMaterial({color:0xcccccc});
        var plane=new THREE.Mesh(planeGeometry,planeMaterial);

        plane.rotation.x=-0.5*Math.PI;
        plane.position.x=15;plane.position.y=0;plane.position.z=0;

        scene.add(plane);

        var cubeGeometry = new THREE.CubeGeometry(4,4,4);
        var cubeMaterial = new THREE.MeshBasicMaterial({color:0xff0000,wireframe:true});
        var cube=new THREE.Mesh(cubeGeometry,cubeMaterial);

        cube.position.x=-4;cube.position.y=3;cube.position.z=0;

        scene.add(cube);

        var sphereGeometry=new THREE.SphereGeometry(4,20,20);
        var sphereMaterial=new THREE.MeshBasicMaterial({color:0x7777ff,wireframe:true});
        var sphere=new THREE.Mesh(sphereGeometry,sphereMaterial);

        sphere.position.x=20;sphere.position.y=4;sphere.position.z=2;

        scene.add(sphere);

        camera.position.x=-30;camera.position.y=40;camera.position.z=30;
        camera.lookAt(scene.position);
        $("#WebGL-output").append(renderer.domElement);
        renderer.render(scene,camera);
    });
var scene=new THREE.Scene();

var camera=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000);

var renderer=new THREE.WebGLRenderer();

在本章的一开始,定义了scene、camera和renderer(场景、相机和渲染器)。
scene是一个容器,保存并跟踪想要渲染的物体,在后面创建了几何体之后(如立方体和球体等)会添加到scene变量中。
camera变量定义了在渲染好的scene里看到什么。
最后是renderer对象,renderer对象负责计算指定的相机角度下的浏览器中scene的呈现样子。
在以上实例中,我们创建了一个WebGLRenderer对象,使用计算机的图形卡来渲染场景。

renderer.setClearColor(0xEEEEEE,0.5);
renderer.setSize(window.innerWidth,window.innerHeight);

接下来通过renderer的setClearColor函数来设置renderer的背景色为接近白色的颜色(0xEEEEEE),并通过setSize()函数来告诉renderer将scene渲染为多大的尺寸。
通过以上的代码,我们有了一个空白的scene、一个renderer和一个camera。
下面我们来创建一个坐标轴对象,并添加到场景中。

var axes=new THREE.AxisHelper(20);
scene.add(axes);

通过THREE的AxisHelper函数我们创建了一个长度为20的坐标轴,并通过scene的add函数将其添加到了场景中。
下面我们再创建一个平面(plane)并修改其旋转和位置。

var planeGeometry = new THREE.PlaneGeometry(60,20,1,1);
var planeMaterial = new THREE.MeshBasicMaterial({color:0xcccccc});
var plane=new THREE.Mesh(planeGeometry,planeMaterial);

plane.rotation.x=-0.5*Math.PI;
plane.position.x=15;plane.position.y=0;plane.position.z=0;

scene.add(plane);

首先通过THREE.PlaneGeometry(60,20)来定义该平面的尺寸,在该章节中,我们将平面的尺寸设置为宽60,高20。在创建完几何体之后我们还需要给plane指定材质,在这里我们使用MeshBasicMaterial()方法来创建一个基本的材质,其颜色为0xcccccc。接下来将两个对象合并到一个名为plane的Mesh(网格)对象中。在将其放入场景之前还要修改其旋转和位置,先将其绕着x轴旋转90度,然后修改其position的x、y和z分量,最后将其加入scene中。
最后

camera.position.x=-30;camera.position.y=40;camera.position.z=30;
camera.lookAt(scene.position);
$("#WebGL-output").append(renderer.domElement);
renderer.render(scene,camera);

我们通过修改camera的位置和使用LookAt函数(对Unity和OpenGL熟悉的应该对此也不陌生)来修改camera的位置和朝向。在这里,我们的lookAt函数指向scene的中心。
最后,我们应该将renderer的输出挂接到HTML页面框架中的<div>元素中;在这里我们使用jQuery来选择正确的输出元素,并告诉 renderer用我们提供的相机来渲染scene。

《添加材质、灯光和阴影》

由于线框的渲染模式不会对灯光产生反应,因此我们需要修改cube和sphere的材质。
先将上一节的例子复制,并改名。
在创建完renderer之后,我们开始创建灯光:

var spotLight=new THREE.SpotLight(0xffffff);
spotLight.position.set(-40,60,-10);
scene.add(spotLight);

上述通过SpotLight()方法来创建的一个探照灯光源,并通过spotLight对象的position.set(-40,60,-10)位置来照射我们的场景。
修改Material的类型为Lambert材质:

var planeGeometry = new THREE.PlaneGeometry(60,20,1,1);
var planeMaterial = new THREE.MeshLambertMaterial({color:0xffffff});

同时修改cube和sphere的材质类型
同理,我们也可以使用Phong光照模型来产生光照效果,MeshPhongMaterial();
最后,我们开始添加阴影。因为阴影的计算比较消耗计算资源,因此默认情况下Three.js不会渲染阴影。
我们在renderer下面加入

renderer.shadowMapEnabled=true;

在plane创建完成之后加入

plane.receiveShadow=true;
cube.castShadow=true;
sphere.castShadow=true;

spotLight.castShadow=true;

来使其接收阴影。
最后产生的效果如下图:


该节的整体代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Example 01.03 - Light and shadow</title>
    <script type="text/javascript"
            src="../libs/three.js"></script>
    <script type="text/javascript"
            src="../libs/jquery-3.1.1.js"></script>
    <style>
        body{
            /*set margin to 0 and overflow to hidden,to use the complete page*/

            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<!--Div which will hold the Output-->
<div id="WebGL-output">

</div>
<!--Javascript code that runs out Three.js examples-->
<script type="text/javascript">
    //once everything is loaded, we run out Three.js stuff.
    $(function () {
        var scene=new THREE.Scene();

        var camera=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000);

        var renderer=new THREE.WebGLRenderer();

        renderer.setClearColor(0xEEEEEE,0.5);
        renderer.setSize(window.innerWidth,window.innerHeight);
        renderer.shadowMapType=THREE.PCFSoftShadowMap;
        renderer.shadowMapEnabled=true;

        var spotLight=new THREE.SpotLight(0xffffff);
        spotLight.position.set(-40,60,-10);

        spotLight.castShadow=true;
        spotLight.shadowMapHeight=4096;
        spotLight.shadowMapWidth=4096;
        scene.add(spotLight);

        var ambientLight=new THREE.AmbientLight(0xffffff);
        ambientLight.intensity=0.3;
        scene.add(ambientLight);

        var axes=new THREE.AxisHelper(20);
        scene.add(axes);

        var planeGeometry = new THREE.PlaneGeometry(60,20,1,1);
        var planeMaterial = new THREE.MeshPhongMaterial({color:0xffffff});
        var plane=new THREE.Mesh(planeGeometry,planeMaterial);

        plane.rotation.x=-0.5*Math.PI;
        plane.position.x=15;plane.position.y=0;plane.position.z=0;

        plane.receiveShadow=true;

        scene.add(plane);

        var cubeGeometry = new THREE.CubeGeometry(4,4,4);
        var cubeMaterial = new THREE.MeshPhongMaterial({color:0xff0000});
        var cube=new THREE.Mesh(cubeGeometry,cubeMaterial);

        cube.position.x=-4;cube.position.y=3;cube.position.z=0;

        cube.castShadow=true;

        scene.add(cube);

        var sphereGeometry=new THREE.SphereGeometry(4,20,20);
        var sphereMaterial=new THREE.MeshPhongMaterial({color:0x7777ff});
        var sphere=new THREE.Mesh(sphereGeometry,sphereMaterial);

        sphere.position.x=20;sphere.position.y=4;sphere.position.z=2;

        sphere.castShadow=true;

        scene.add(sphere);

        camera.position.x=-30;camera.position.y=40;camera.position.z=30;
        camera.lookAt(scene.position);
        $("#WebGL-output").append(renderer.domElement);
        renderer.render(scene,camera);
    });
</script>

</body>
</html>

《简单的动画和帧率监控》

在Three.js中,使用requestAnimationFrame()方法可以指定一个渲染函数,按照浏览器的时间间隔(通常是每秒60帧)来调用该函数。在指定的渲染函数中,可以对场景中的物体的位置 、旋转和缩放等进行更新。
那么在进行动画之前,我们先来引入帧率检测的代码。首先在HTML页面引入stats.js这个库。

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

然后再添加一个用于展示统计信息的div

<div id="Stats-output"></div>

最后编写初始化stats的代码:

function initStats() {
    var stats=new Stats();
    stats.setMode(0);
    stats.domElement.style.position='absolute';
    stats.domElement.style.left='0px';
    stats.domElement.style.top='0px';
    document.getElementById("Stats-output").appendChild(stats.domElement);

     return stats;
}

并在主体代码的开头引入stats对象

var stats=initStats();

下面,我们来编写每一帧需要调用的渲染函数

function renderScene() {
    stats.update();

    //animate cube
    cube.rotation.x+=0.02;cube.rotation.y+=0.02;cube.rotation.z+=0.02;

    //animate sphere
    step+=0.04;
    sphere.position.x=20+(10*Math.cos(step));
    sphere.position.y=2+(10*Math.abs(Math.sin(step)));

    requestAnimationFrame(renderScene);
    renderer.render(scene,camera);
}

该渲染函数在每次调用会更新stats信息,更新方块的旋转和球体的运动位置。最后通过requestAnimationFrame函数来指定renderScene函数为指定时间间隔渲染的函数。最后调用renderer渲染器的render函数重新渲染。
最后我们在代码的主体加入一次对renderScene函数的调用。
本节所有代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Example 01.04 - Simple Animation</title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/jquery-3.1.1.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <style>
        body{
            /*set margin to 0 and overflow to hidden,to use the complete page*/
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output"></div>
<!--Div which will hold the Output-->
<div id="WebGL-output"></div>

<!--Javascript code that runs out Three.js examples-->
<script type="text/javascript">
    //once everything is loaded, we run out Three.js stuff.
    function init() {
        var scene=new THREE.Scene();

        var camera=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000);

        var renderer=new THREE.WebGLRenderer();

        var stats=initStats();

        renderer.setClearColor(0xEEEEEE,0.5);
        renderer.setSize(window.innerWidth,window.innerHeight);
        renderer.shadowMapType=THREE.PCFSoftShadowMap;
        renderer.shadowMapEnabled=true;

        var spotLight=new THREE.SpotLight(0xffffff);
        spotLight.position.set(-40,60,-10);

        spotLight.castShadow=true;
        spotLight.shadowMapHeight=4096;
        spotLight.shadowMapWidth=4096;
        scene.add(spotLight);

        var ambientLight=new THREE.AmbientLight(0xffffff);
        ambientLight.intensity=0.3;
        scene.add(ambientLight);

        var axes=new THREE.AxisHelper(20);
        scene.add(axes);

        var planeGeometry = new THREE.PlaneGeometry(60,20,1,1);
        var planeMaterial = new THREE.MeshPhongMaterial({color:0xffffff});
        var plane=new THREE.Mesh(planeGeometry,planeMaterial);

        plane.rotation.x=-0.5*Math.PI;
        plane.position.x=15;plane.position.y=0;plane.position.z=0;

        plane.receiveShadow=true;

        scene.add(plane);

        var cubeGeometry = new THREE.CubeGeometry(4,4,4);
        var cubeMaterial = new THREE.MeshPhongMaterial({color:0xff0000});
        var cube=new THREE.Mesh(cubeGeometry,cubeMaterial);

        cube.position.x=-4;cube.position.y=3;cube.position.z=0;

        cube.castShadow=true;

        scene.add(cube);

        var sphereGeometry=new THREE.SphereGeometry(4,20,20);
        var sphereMaterial=new THREE.MeshPhongMaterial({color:0x7777ff});
        var sphere=new THREE.Mesh(sphereGeometry,sphereMaterial);

        sphere.position.x=20;sphere.position.y=4;sphere.position.z=2;

        sphere.castShadow=true;

        scene.add(sphere);

        camera.position.x=-30;camera.position.y=40;camera.position.z=30;
        camera.lookAt(scene.position);
        document.getElementById("WebGL-output").appendChild(renderer.domElement);
        renderer.render(scene,camera);

        renderScene();


        var step=0;
        function renderScene() {
            stats.update();

            //animate cube
            cube.rotation.x+=0.02;cube.rotation.y+=0.02;cube.rotation.z+=0.02;

            //animate sphere
            step+=0.04;
            sphere.position.x=20+(10*Math.cos(step));
            sphere.position.y=2+(10*Math.abs(Math.sin(step)));

            requestAnimationFrame(renderScene);
            renderer.render(scene,camera);
        }

        function initStats() {
            var stats=new Stats();
            stats.setMode(0);
            stats.domElement.style.position='absolute';
            stats.domElement.style.left='0px';
            stats.domElement.style.top='0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);

             return stats;
        }
    }
    window.onload=init;
</script>

</body>
</html>

《使用dat.GUI来编写一个简单的UI》

dat.GUI为Google公司的一些人创建的库,该库的文档(http://code.google.com/p/dat-gui/ 需翻墙)通过这个库,我们可以实现用slider滑动条来控制立方体的自旋速度和球体的弹跳速度。
首先需要引用dat.GUI.js库

<script type="text/javascript" src="../libs/dat.gui.js"></script>

接下来添加一个JavaScript对象:

var controls = new function () {
    this.rotationSpeed = 0.02;
    this.bouncingSpeed = 0.03;
};

然后创建一个gui对象并添加控制器:

var gui=new dat.GUI();
gui.add(controls,'rotationSpeed',0,0.5);
gui.add(controls,'bouncingSpeed',0,0.5);

注意,这些代码需要写在渲染函数之前。
接下来修改上一节的renderScene函数中的方块和球体的动画控制代码:

function renderScene() {
    stats.update();

    //animate cube
    cube.rotation.x+=controls.rotationSpeed;cube.rotation.y+=controls.rotationSpeed;cube.rotation.z+=controls.rotationSpeed;

    //animate sphere
    step+=controls.bouncingSpeed;
    sphere.position.x=20+(10*Math.cos(step));
    sphere.position.y=2+(10*Math.abs(Math.sin(step)));

    requestAnimationFrame(renderScene);
    renderer.render(scene,camera);
}

编写完成之后运行效果如下:


本节的完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Example 01.05 - dat.GUI UserInterface</title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/jquery-3.1.1.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body{
            /*set margin to 0 and overflow to hidden,to use the complete page*/
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output"></div>
<!--Div which will hold the Output-->
<div id="WebGL-output"></div>

<!--Javascript code that runs out Three.js examples-->
<script type="text/javascript">
    //once everything is loaded, we run out Three.js stuff.
    function init() {
        var scene=new THREE.Scene();

        var camera=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000);

        var renderer=new THREE.WebGLRenderer();

        var stats=initStats();

        renderer.setClearColor(0xEEEEEE,0.5);
        renderer.setSize(window.innerWidth,window.innerHeight);
        renderer.shadowMapType=THREE.PCFSoftShadowMap;
        renderer.shadowMapEnabled=true;

        var spotLight=new THREE.SpotLight(0xffffff);
        spotLight.position.set(-40,60,-10);

        spotLight.castShadow=true;
        spotLight.shadowMapHeight=4096;
        spotLight.shadowMapWidth=4096;
        scene.add(spotLight);

        var ambientLight=new THREE.AmbientLight(0xffffff);
        ambientLight.intensity=0.3;
        scene.add(ambientLight);

        var axes=new THREE.AxisHelper(20);
        scene.add(axes);

        var planeGeometry = new THREE.PlaneGeometry(60,20,1,1);
        var planeMaterial = new THREE.MeshPhongMaterial({color:0xffffff});
        var plane=new THREE.Mesh(planeGeometry,planeMaterial);

        plane.rotation.x=-0.5*Math.PI;
        plane.position.x=15;plane.position.y=0;plane.position.z=0;

        plane.receiveShadow=true;

        scene.add(plane);

        var cubeGeometry = new THREE.CubeGeometry(4,4,4);
        var cubeMaterial = new THREE.MeshPhongMaterial({color:0xff0000});
        var cube=new THREE.Mesh(cubeGeometry,cubeMaterial);

        cube.position.x=-4;cube.position.y=3;cube.position.z=0;

        cube.castShadow=true;

        scene.add(cube);

        var sphereGeometry=new THREE.SphereGeometry(4,20,20);
        var sphereMaterial=new THREE.MeshPhongMaterial({color:0x7777ff});
        var sphere=new THREE.Mesh(sphereGeometry,sphereMaterial);

        sphere.position.x=20;sphere.position.y=4;sphere.position.z=2;

        sphere.castShadow=true;

        scene.add(sphere);

        camera.position.x=-30;camera.position.y=40;camera.position.z=30;
        camera.lookAt(scene.position);
        document.getElementById("WebGL-output").appendChild(renderer.domElement);
        renderer.render(scene,camera);

        var controls = new function () {
            this.rotationSpeed = 0.02;
            this.bouncingSpeed = 0.03;
        };

        var gui=new dat.GUI();
        gui.add(controls,'rotationSpeed',0,0.5);
        gui.add(controls,'bouncingSpeed',0,0.5);

        //this renderScene() function should be called after every
        renderScene();

        var step=0;
        function renderScene() {
            stats.update();

            //animate cube
            cube.rotation.x+=controls.rotationSpeed;cube.rotation.y+=controls.rotationSpeed;cube.rotation.z+=controls.rotationSpeed;

            //animate sphere
            step+=controls.bouncingSpeed;
            sphere.position.x=20+(10*Math.cos(step));
            sphere.position.y=2+(10*Math.abs(Math.sin(step)));

            requestAnimationFrame(renderScene);
            renderer.render(scene,camera);
        }

        function initStats() {
            var stats=new Stats();
            stats.setMode(0);
            stats.domElement.style.position='absolute';
            stats.domElement.style.left='0px';
            stats.domElement.style.top='0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);

             return stats;
        }
    }
    window.onload=init;
</script>

</body>
</html>

《动态改变渲染大小》

在使用Three.js的时候,我们还可以根据窗口大小的改变来调整渲染的大小。
在这里我们需要添加一个onSizeChanged函数

function onSizeChanged() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}

编写完该函数之后我们需要对窗口大小改变的事件增加监听:

window.addEventListener('resize', onSizeChanged, false);

注意,这里要把renderer、camera等变量定义成作用域更广的变量,具体参见详细代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Example 01.06 - Resize</title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/jquery-3.1.1.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body{
            /*set margin to 0 and overflow to hidden,to use the complete page*/
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output"></div>
<!--Div which will hold the Output-->
<div id="WebGL-output"></div>

<!--Javascript code that runs out Three.js examples-->
<script type="text/javascript">
    //once everything is loaded, we run out Three.js stuff.
    var scene;
    var camera;
    var stats;
    var renderer;
    function init() {
        scene=new THREE.Scene();

        camera=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000);

        renderer=new THREE.WebGLRenderer();

        stats=initStats();

        renderer.setClearColor(0xEEEEEE,0.5);
        renderer.setSize(window.innerWidth,window.innerHeight);
        renderer.shadowMapType=THREE.PCFSoftShadowMap;
        renderer.shadowMapEnabled=true;

        var spotLight=new THREE.SpotLight(0xffffff);
        spotLight.position.set(-40,60,-10);

        spotLight.castShadow=true;
        spotLight.shadowMapHeight=4096;
        spotLight.shadowMapWidth=4096;
        scene.add(spotLight);

        var ambientLight=new THREE.AmbientLight(0xffffff);
        ambientLight.intensity=0.3;
        scene.add(ambientLight);

        var axes=new THREE.AxisHelper(20);
        scene.add(axes);

        var planeGeometry = new THREE.PlaneGeometry(60,20,1,1);
        var planeMaterial = new THREE.MeshPhongMaterial({color:0xffffff});
        var plane=new THREE.Mesh(planeGeometry,planeMaterial);

        plane.rotation.x=-0.5*Math.PI;
        plane.position.x=15;plane.position.y=0;plane.position.z=0;

        plane.receiveShadow=true;

        scene.add(plane);

        var cubeGeometry = new THREE.CubeGeometry(4,4,4);
        var cubeMaterial = new THREE.MeshPhongMaterial({color:0xff0000});
        var cube=new THREE.Mesh(cubeGeometry,cubeMaterial);

        cube.position.x=-4;cube.position.y=3;cube.position.z=0;

        cube.castShadow=true;

        scene.add(cube);

        var sphereGeometry=new THREE.SphereGeometry(4,20,20);
        var sphereMaterial=new THREE.MeshPhongMaterial({color:0x7777ff});
        var sphere=new THREE.Mesh(sphereGeometry,sphereMaterial);

        sphere.position.x=20;sphere.position.y=4;sphere.position.z=2;

        sphere.castShadow=true;

        scene.add(sphere);

        camera.position.x=-30;camera.position.y=40;camera.position.z=30;
        camera.lookAt(scene.position);
        document.getElementById("WebGL-output").appendChild(renderer.domElement);

        //effect.render(scene,camera);

        var controls = new function () {
            this.rotationSpeed = 0.02;
            this.bouncingSpeed = 0.03;
        };

        var gui=new dat.GUI();
        gui.add(controls,'rotationSpeed',0,0.5);
        gui.add(controls,'bouncingSpeed',0,0.5);

        //this renderScene() function should be called after every
        renderScene();

        var step=0;
        function renderScene() {
            stats.update();

            //animate cube
            cube.rotation.x+=controls.rotationSpeed;cube.rotation.y+=controls.rotationSpeed;cube.rotation.z+=controls.rotationSpeed;

            //animate sphere
            step+=controls.bouncingSpeed;
            sphere.position.x=20+(10*Math.cos(step));
            sphere.position.y=2+(10*Math.abs(Math.sin(step)));

            requestAnimationFrame(renderScene);
            renderer.render(scene,camera);
        }

        function initStats() {
            var stats=new Stats();
            stats.setMode(0);
            stats.domElement.style.position='absolute';
            stats.domElement.style.left='0px';
            stats.domElement.style.top='0px';
            document.getElementById("Stats-output").appendChild(stats.domElement);

            return stats;
        }
    }
    function onResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    }
    window.onload=init;

    window.addEventListener('resize', onResize, false);
</script>

</body>
</html>

那么到这里,我们就完成了对Three.js的入门学习。下面我们会更加深入的讲解Three.js。

参考资料:
Learning Three.js The JavaSctipt 3D Library for WebGL ·Jos Dirksen

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

推荐阅读更多精彩内容

  • 本文主要是讲解 Three.js 的相关概念,帮助大家对 Three.js 以及相关知识形成比较完整的理解。今年来...
    Simon王小白阅读 8,435评论 2 9
  • 1、三大组建 在Three.js中,要渲染物体到网页中,我们需要3个组建:场景(scene)、相机(camera)...
    依依玖玥阅读 631评论 0 0
  • react vr中文网:www.vr-react.com react vr qq群:481244084 示例源码 ...
    liu_520阅读 3,637评论 4 6
  • 简书连载风云录《选择》目录上一章回顾:选择 (十二) 情书第十三章:相见欢 王凌云收到何嘉慧的回信,欢喜不已。但拆...
    林燕娜阅读 659评论 8 9
  • 狂暴的灵力,一的在牧尘气海之中翻涌着,九幽雀那燃烧着黑炎的眼睛中,有着难掩的急促与激动,它的那种情绪,就连牧尘都能...
    混沌天书阅读 375评论 0 0