6

我们在这片文章里不会上来就告诉你Three.js里面有什么方法,都该怎么去使用;我们想让你先尝试去使用Three.js,通过这个小例子希望可以勾引起你的兴趣;然后我们会在后面的学习中慢慢的深入Three.js的学习;慢慢来嘛,欲速则不达。


先看看示例

开始前的准备

  • 编辑器
    推荐使用WebStrom或者Atom或者SublimeText

  • Three.js
    我们学习Three.js当然要用到three.js,获取的途径有两种;一种是直接在github上的Three.js克隆下载,另一种方法是使用learning-threejs上的给你准备好的学习资料。我选择的是第二种方法,因为那里面还包含了许多我们将使用到的库。

  • Web服务器
    在刚开始的学习中,我们可能只会在一个页面上书写我们的代码;但是随着学习的深入,我们可能会加载许多的材料,这时候就需要服务器来帮忙了。当然搭建一个本地的服务器也是十分容易的。如果你使用的编辑器是WebStrom的话,那么就不需要搭建本地服务器了;因为通过WebStrom打开的页面就是在WebStrom创建的服务器中运行的。

第一个示例

  • 引入文件库

    <script src="libs/three.js"></script>
    <script src="libs/jquery-1.9.0.js"></script>
    <script src="libs/stats.js"></script>
    <script src="libs/dat.gui.js"></script>

    注意,我们上面引入的库在文件夹learning-threejs里面都有的。

  • 书写CSS和HTML代码

     body {
            margin: 0;
            overflow: hidden;
     }

    这些样式文件是为了防止里面内容的溢出,去除了body的外边距。

        <div id="container"></div>

    添加存放Three.js渲染内容的容器。

  • 添加场景,相机,渲染器

     // 添加场景
     var scene = new THREE.Scene();
     // 添加相机
     var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
     // 添加渲染器
     var renderer = new THREE.WebGLRenderer();

    场景就相当于一个画板,相机相当于你的眼睛,渲染器相当于一支画笔;这些是我的理解,如果你对这些概念不是很清楚的话,那也没关系;学习Three.js还有很长的路要走,随着学习的深入,相信你会有自己的理解。

  • 设置场景大小,背景颜色

    // 设置场景的大小
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 设置场景的背景颜色 第一个参数是一个表示颜色的字符串或者16进制的数字
    renderer.setClearColor(0x000000);
  • 将场景元素添加到页面,渲染场景

    // 选取页面中的容器,将我们的场景元素添加进去
    $('#container').append(renderer.domElement);
    // 渲染器渲染我们的场景
    renderer.render(scene, camera);
  • 效果图如下:
    图片描述

    虽然看着十分简单,但是这是我们开始下面内容的基础,继续前进吧。

  • 添加一个坐标轴

    // 新建坐标轴
    var axis = new THREE.AxisHelper(20);
    // 在场景中添加坐标轴
    scene.add(axis);
    
    /* 在设置了相机的位置之后才可以看到坐标轴 */
    
    // 设置相机的x坐标位置
    camera.position.x = -30;
    // 设置相机的y坐标位置
    camera.position.y = 40;
    // 设置相机的z坐标的位置
    camera.position.z = 30;
    // 设置相机的焦点
    camera.lookAt(scene.position);
  • 效果图如下:
    图片描述

    图二,添加了坐标轴的场景。

  • 添加一个平面

    // 创建平面的骨架
    var planeGeometry = new THREE.PlaneBufferGeometry(60, 30, 1, 1);
    // 创建平面的材料
    var planeMaterial = new THREE.MeshBasicMaterial({color: '#aaaaaa'});
    // 合成平面
    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.BoxGeometry(4, 4, 4);
    // 创建立方体材料
    var cubeMaterial = new THREE.MeshBasicMaterial({color: 'yellow', wireframe: true});
    // 合成立方体
    var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
    // 设置立方体位置
    cube.position.x = -8;
    cube.position.y = 4;
    cube.position.z = 0;
    // 在场景中添加立方体
    scene.add(cube);
    
    // 创建并在场景中添加球体
    var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
    var sphereMaterial = new THREE.MeshBasicMaterial({color: 'red', wireframe: true});
    var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
    sphere.position.x = 20;
    sphere.position.y = 10;
    sphere.position.z = 0;
    scene.add(sphere);
  • 效果图如下:
    图片描述

    图四,添加了坐标轴的场景。

  • 在场景中添加灯光

    // 创建灯光
    var spotLight = new THREE.SpotLight(0xffffff);
    // 设置灯光位置
    spotLight.position.set(-40, 60, -10);
    // 在场景中添加灯光
    scene.add(spotLight);

    添加灯光之后,你会发现页面并没有什么变化;那是因为我们在场景中添加的物体的材料是不会对光源产生任何反应的,所以我们要改动一些东西,如下所示:

    /* 将构建物体的材料改变 */
    //var planeMaterial = new THREE.MeshBasicMaterial({color: '#aaaaaa'});
    var planeMaterial = new THREE.MeshLambertMaterial({color: '#aaaaaa'});
    
    //var cubeMaterial = new THREE.MeshBasicMaterial({color: 'yellow', wireframe: true});
    var cubeMaterial = new THREE.MeshLambertMaterial({color: 'yellow', wireframe: true});
    
    //var sphereMaterial = new THREE.MeshBasicMaterial({color: 'red', wireframe: true});
    var sphereMaterial = new THREE.MeshLambertMaterial({color: 'red', wireframe: true});
    
    /* 设置渲染器允许阴影映射 */
    renderer.shadowMapEnabled = true;
    
    /* 设置哪些物体投射阴影,那些物体接受阴影 */
    plane.receiveShadow = true;
    cube.castShadow = true;
    sphere.castShadow = true;
    spotLight.castShadow = true;
  • 效果图如下:
    图片描述

    图五,添加了阴影映射的场景。

  • 添加监测动画运行帧数的辅助库

    var stats = initStats();
    
        renderScene();
    
        // 重复渲染场景
        function renderScene() {
            // 更新
            stats.update();
            // 使用requestAnimationFrame代替setInterval
            requestAnimationFrame(renderScene);
            // 渲染场景
            renderer.render(scene, camera);
        }
    
        // 初始化监测动画运行频帧库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';
            // 在页面上面添加一个div,id为stats
            $('#stats').append(stats.domElement);
            return stats;
        }
  • 为立方体和球体添加动画

        var stats = initStats();
        var step = 0;
        renderScene();
        // 重复渲染场景
        function renderScene() {
            // 更新
            stats.update();
            // 立方体的旋转
            cube.rotation.x += 0.02;
            cube.rotation.y += 0.02;
            cube.rotation.z += 0.02;
            // 球体的弹跳
            step += 0.03;
            sphere.position.x = 20 + (10 * Math.cos(step));
            sphere.position.y = 0 + (10 * Math.sin(step));
            // 使用requestAnimationFrame代替setInterval
            requestAnimationFrame(renderScene);
            // 渲染场景
            renderer.render(scene, camera);
        }
        // 初始化监测动画运行频帧库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';
            // 在页面上面添加一个div,id为stats
            $('#stats').append(stats.domElement);
            return stats;
        }
  • 效果图如下:
    图片描述

    图六,添加了动画以及监测动画运行频帧场景。

  • 使用dat.GUI简化测试

        // 定义我们使用到的测试变量
        var controls = new function() {
            this.rotationSpeed = 0.02;
            this.bouncingSpeed = 0.03;
        };
    
        var gui = new dat.GUI();
        gui.add(controls, 'rotationSpeed', 0, 1);
        gui.add(controls, 'bouncingSpeed', 0, 1);
        
        // 使用上面的变量替换我们的固定数据
        // 立方体的旋转
        cube.rotation.x += controls.rotationSpeed;
        cube.rotation.y += controls.rotationSpeed;
        cube.rotation.z += controls.rotationSpeed;
    
        // 球体的弹跳
        step += controls.bouncingSpeed;
        sphere.position.x = 20 + (10 * Math.cos(step));
        sphere.position.y = 0 + (10 * Math.sin(step));
  • 效果图如下:
    图片描述

    图七,最终形成的场景。

总结

  • 最终的javascript代码部分

    $(function() {
    
        var controls = new function() {
            this.rotationSpeed = 0.02;
            this.bouncingSpeed = 0.03;
        };
    
        var gui = new dat.GUI();
        gui.add(controls, 'rotationSpeed', 0, 1);
        gui.add(controls, 'bouncingSpeed', 0, 1);
    
        // 添加场景
        var scene = new THREE.Scene();
        // 添加相机
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        // 添加渲染器
        var renderer = new THREE.WebGLRenderer();
        // 设置场景的大小
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 设置场景的背景颜色
        renderer.setClearColor(0x000000);
        // 设置渲染器允许阴影映射
        renderer.shadowMapEnabled = true;
    
        // 新建坐标轴
        var axis = new THREE.AxisHelper(20);
        // 在场景中添加坐标轴
        scene.add(axis);
    
        // 创建平面的骨架
        var planeGeometry = new THREE.PlaneBufferGeometry(60, 30, 1, 1);
        // 创建平面的材料
    //    var planeMaterial = new THREE.MeshBasicMaterial({color: '#aaaaaa'});
        var planeMaterial = new THREE.MeshLambertMaterial({color: '#aaaaaa'});
        // 合成平面
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;
    
        // 设置平面的旋转角度
        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.BoxGeometry(4, 4, 4);
        // 创建立方体材料
    //    var cubeMaterial = new THREE.MeshBasicMaterial({color: 'yellow', wireframe: true});
        var cubeMaterial = new THREE.MeshLambertMaterial({color: 'yellow', wireframe: true});
        // 合成立方体
        var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
        cube.castShadow = true;
        // 设置立方体位置
        cube.position.x = -8;
        cube.position.y = 4;
        cube.position.z = 0;
        // 在场景中添加立方体
        scene.add(cube);
    
        // 创建并在场景中添加球体
        var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
    //    var sphereMaterial = new THREE.MeshBasicMaterial({color: 'red', wireframe: true});
        var sphereMaterial = new THREE.MeshLambertMaterial({color: 'red', wireframe: true});
        var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphere.castShadow = true;
        sphere.position.x = 20;
        sphere.position.y = 10;
        sphere.position.z = 0;
        scene.add(sphere);
    
        // 创建灯光
        var spotLight = new THREE.SpotLight(0xffffff);
        // 设置灯光位置
        spotLight.position.set(-40, 60, -10);
        spotLight.castShadow = true;
        // 在场景中添加灯光
        scene.add(spotLight);
    
        // 设置相机的x坐标位置
        camera.position.x = -30;
        // 设置相机的y坐标位置
        camera.position.y = 40;
        // 设置相机的z坐标的位置
        camera.position.z = 30;
        // 设置相机的焦点
        camera.lookAt(scene.position);
    
        // 选取页面中的容器,将我们的场景元素添加进去
        $('#container').append(renderer.domElement);
    
    
        var stats = initStats();
        var step = 0;
    
        renderScene();
    
        // 重复渲染场景
        function renderScene() {
            // 更新
            stats.update();
    
            // 立方体的旋转
            cube.rotation.x += controls.rotationSpeed;
            cube.rotation.y += controls.rotationSpeed;
            cube.rotation.z += controls.rotationSpeed;
    
            // 球体的弹跳
            step += controls.bouncingSpeed;
            sphere.position.x = 20 + (10 * Math.cos(step));
            sphere.position.y = 0 + (10 * Math.sin(step));
    
            // 使用requestAnimationFrame代替setInterval
            requestAnimationFrame(renderScene);
            // 渲染场景
            renderer.render(scene, camera);
        }
    
        // 初始化监测动画运行频帧库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';
            // 在页面上面添加一个div,id为stats
            $('#stats').append(stats.domElement);
            return stats;
        }
    })
  • 如果文中有什么不足欢迎大家指出,我们一起进步。

这一系列文章大都参考了《Learning Three.js》


dreamapplehappy
6.6k 声望5.9k 粉丝

引用和评论

0 条评论