1.2 场景类

本文主要介绍Scene类的几个重要函数和属性,如下表所示:

函数/属性

描述

add(object)

在场景中添加对象。

children

返回场景中所有对象的列表,包括相机和光源。

getChildrenByName(name)

创建对象时,可以通过name属性为它指定一个唯一的名称,然后你就可以使用这个方法根据对象名称返回这个对象。

remove(object)

从场景中移除对象。

traverse(function)

传入一个回调函数遍历scene的所有子对象。

fog

通过该属性可以设置场景的雾化效果。它可以渲染出一层雾气,隐藏远处的物体。

overrideMaterial

强制场景中的所有物体都使用相同的材质。

我们可以使用add(object)函数在场景中添加一个THREE.Mesh对象(即plane,作为地面)、一个THREE.AmbientLight(环境光)对象、一个THREE.SpotLight(聚光灯)对象。THREE.Camera(相机)对象会自动添加,无需手工添加。代码如下:

// 创建一个Plane作为地面
var planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
var planeMaterial =  new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry,planeMaterial);
plane.receiveShadow  = true;

// Plane默认在xy平面,需要将它旋转到xz平面
plane.rotation.x=-0.5*Math.PI;
plane.position.x=0
plane.position.y=0
plane.position.z=0

// 将这个Plane添加到场景中
scene.add(plane);        

// 添加环境光
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);

// 添加一个聚光灯用于产生阴影
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set( -40, 60, -10 );
spotLight.castShadow = true;
scene.add(spotLight);

使用dat.GUI控件可以在场景中添加方块、从场景中移除最后添加的方块,以及显示场景中的所有物体。在这个控件区中的最后一项用于显示场景中物体的数量。你会发现场景启动的时候场景中就已经有4个物体了。就是我们前面提到的地面、环境光、点光源和相机。添加方块的addBox()函数的代码如下:

this.addBox = function() {

    var boxSize = Math.ceil((Math.random() * 3));
    var boxGeometry = new THREE.BoxGeometry(boxSize,boxSize,boxSize);
    var boxMaterial = new THREE.MeshLambertMaterial({color:  Math.random() * 0xffffff });
    var box = new THREE.Mesh(boxGeometry, boxMaterial);
    box.castShadow = true;
    box.name = "box-" + scene.children.length;

    // 随机放置立方体
    box.position.x = -30 + Math.round((Math.random() * planeGeometry.parameters.width));
    box.position.y = Math.round((Math.random() * 5));
    box.position.z = -20 + Math.round((Math.random() * planeGeometry.parameters.height));

    // 将立方体添加到场景中
    scene.add(box);
    this.numberOfObjects = scene.children.length;
};

当你点击addBox按钮时,一个新的THREE.BoxGeometry实例就会创建出来,其尺寸是0到3之间的一个随机数。除了随机大小尺寸之外,这个方块在场景中的颜色和位置也是随机的。

这段代码里的一个新东西是我们使用name属性为这个方块指定了一个名字。方块的名字是在box-后面加上当前场景中对象的数量(即显示的scene.children.length属性)。所以会得到诸如box-1、box-2、box-3等类似的名字。名字在调试的时候很有用,可以使用Scene.getChildByName(name)函数直接获取指定的对象,然后可以执行一些操作,例如改变它的位置。最后一行代码的目的是在控制界面使用numberOfObjects变量显示场景中的对象。所以无论什么时候添加或删除对象,我们都要将这个变量设置为更新后的数量。

在控制界面上调用的下一个函数是removeBox,点击这个按钮可以把最后添加的方块从场景中移除。下面的代码片段即该函数的定义:

this.removeBox = function () {
    var allChildren = scene.children;
    var lastObject = allChildren[allChildren.length - 1];
    if (lastObject instanceof THREE.Mesh) {
        scene.remove(lastObject);
        this.numberOfObjects = scene.children.length;
    }
}

在这段代码里我们通过THREE.Scene()对象的children属性获取最后添加的对象。我们还需要检查一下这个对象是不是一个Mesh对象,以防移除相机和光源。删掉这个对象之后,我们将再一次更新控制界面上那个表示场景中对象数量的属性。

控制界面上的最后一个按钮的标签是outputObjects。这个按钮的功能是打印出场景中的所有对象,并将结果输出到网络浏览器的Console(控制台),如下图所示:

调试输出

往Console输出信息用的是内置的console对象,代码如下:

this.outputObjects = function() {
    console.log(scene.children);
}

这对调试来讲非常有用,可以帮助你查找场景中某个特定物体的相关的问题或错误。

在代码中,我们还用到了THREE.Scene.traverse()函数。我们可以将一个函数作为参数传递给traverser()函数。这个传递来的函数将会在场景的每一个子对象上调用一次:在render()函数里,我们将使用traverse()函数类更新每个方块的旋转弧度(我们特意忽略了表示地面的plane对象)。我们也可以使用for循环遍历children这个属性数组来达到同样的目的。代码如下:

function render() {
    stats.update();

    // 旋转立方体
    scene.traverse(function(e) {
        if (e instanceof THREE.Mesh && e != plane ) {
            e.rotation.x+=0.02;
            e.rotation.y+=0.02;
            e.rotation.z+=0.02;
        }
    });

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

在场景中添加雾化效果

通过fog属性可以为整个场景添加一种雾化效果。一个物体离得越远,就越模糊。

在Three.js库里打开雾化效果很简单。只要在定义完场景后加上如下一行代码即可:

scene.fog = new THREE.Fog(0xffffff, 0.015, 100);

我们在这里定义了一个白色的雾化效果(0xffffff)。后面的两个属性用来调节雾的显示。0.015是near(近处)属性的值,而100设置的是far(远处)属性的值。通过这两个属性你可以决定雾从什么地方开始,以及浓度加深的程度。还有另外一个方法可以设置场景中雾的浓度;你可以使用如下的定义:

scene.fog=new THREE.FogExp2( 0xffffff, 0.015 );

这次我们不指定near属性和far属性,只给出颜色和浓度。要想获得理想的效果,你最好亲手试验一下这些属性。

使用材质覆盖属性

场景的overrideMaterial属性用来设置场景中所有物体的材质。当使用下面这段代码那样使用这个属性时,所有添加到场景中的物体都会使用同样的材质:

scene.overrideMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });

你可以看到所有的方块都是用相同的材质和颜色渲染的。你可以将这行代码注释掉看看会发生什么。

最终的程序截图如下:

程序截图
点击图片可观看程序

完整代码

<!DOCTYPE html>

<html>

<head>
    <title>01.02 - 场景类</title>
    <script src="../../../Scripts/jquery-2.1.3.min.js"></script>
    <script src="../../../Scripts/Threejs/three.min.js"></script>
    <script src="../../../Scripts/Threejs/dat.gui.min.js"></script>
    <script src="../../../Scripts/Threejs/stats.js"></script>

    <style>
        body {
            /* 将margin设置为0,overflow设置为hidden,可让浏览器显示全屏 */
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>
    <div id="Stats-output">
    </div>
    <!-- 作为Canvas容器的div -->
    <div id="WebGL-output">
    </div>

    <script type="text/javascript">

        // 页面加载完毕后,就可以运行Three.js了。
        $(function () {

            var stats = initStats();

            // 创建渲染器,并设置视口大小和清除色
            var renderer = new THREE.WebGLRenderer();
            renderer.setClearColor(0xEEEEEE, 1.0);
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.shadowMapEnabled = true;
            // 将WebGL的输出canvas放置到div中
            $("#WebGL-output").append(renderer.domElement);

            // 创建scene对象,用来容纳网格、相机、光源等对象
            var scene = new THREE.Scene();
            //scene.fog=new THREE.FogExp2( 0xffffff, 0.015 );
            scene.fog = new THREE.Fog(0xffffff, 0.015, 100);
            scene.overrideMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });

            // 场景相机
            var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
            // 设置相机
            camera.position.x = -30;
            camera.position.y = 40;
            camera.position.z = 30;
            camera.lookAt(scene.position);

            // 创建一个Plane作为地面
            var planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
            var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
            var plane = new THREE.Mesh(planeGeometry, planeMaterial);
            plane.receiveShadow = true;

            // Plane默认在xy平面,需要将它旋转到xz平面
            plane.rotation.x = -0.5 * Math.PI;
            plane.position.x = 0
            plane.position.y = 0
            plane.position.z = 0

            // 将这个Plane添加到场景中
            scene.add(plane);

            // 添加环境光
            var ambientLight = new THREE.AmbientLight(0x0c0c0c);
            scene.add(ambientLight);

            // 添加一个聚光灯用于产生阴影
            var spotLight = new THREE.SpotLight(0xffffff);
            spotLight.position.set(-40, 60, -10);
            spotLight.castShadow = true;
            scene.add(spotLight);

            // GUI界面
            var controls = new function () {
                this.numberOfObjects = scene.children.length;
                this.fogColor = scene.fog.color.getStyle();

                this.addBox = function () {

                    var boxSize = Math.ceil((Math.random() * 3));
                    var boxGeometry = new THREE.BoxGeometry(boxSize, boxSize, boxSize);
                    var boxMaterial = new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff });
                    var box = new THREE.Mesh(boxGeometry, boxMaterial);
                    box.castShadow = true;
                    box.name = "box-" + scene.children.length;

                    // 随机放置立方体
                    box.position.x = -30 + Math.round((Math.random() * planeGeometry.parameters.width));
                    box.position.y = Math.round((Math.random() * 5));
                    box.position.z = -20 + Math.round((Math.random() * planeGeometry.parameters.height));

                    // 将立方体添加到场景中
                    scene.add(box);
                    this.numberOfObjects = scene.children.length;
                };

                this.removeBox = function () {
                    var allChildren = scene.children;
                    var lastObject = allChildren[allChildren.length - 1];
                    if (lastObject instanceof THREE.Mesh) {
                        scene.remove(lastObject);
                        this.numberOfObjects = scene.children.length;
                    }
                }

                this.outputObjects = function () {
                    console.log(scene.children);
                }
            }

            var gui = new dat.GUI();

            gui.add(controls, 'addBox');
            gui.add(controls, 'removeBox');
            gui.add(controls, 'numberOfObjects').listen();
            gui.addColor(controls, 'fogColor').onChange(function (e) {
                scene.fog.color.setStyle(e);
            });
            gui.add(controls, 'outputObjects');

            // 渲染
            render();

            function render() {
                stats.update();

                // 旋转立方体
                scene.traverse(function (e) {
                    if (e instanceof THREE.Mesh && e != plane) {
                        e.rotation.x += 0.02;
                        e.rotation.y += 0.02;
                        e.rotation.z += 0.02;
                    }
                });

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

            function initStats() {

                var stats = new Stats();

                stats.setMode(0); // 0: fps, 1: ms

                stats.domElement.style.position = 'absolute';
                stats.domElement.style.left = '0px';
                stats.domElement.style.top = '0px';

                $("#Stats-output").append(stats.domElement);

                return stats;
            }
        });
    </script>
</body>
</html>
文件下载(已下载 1889 次)

发布时间:2015/7/28 下午10:38:26  阅读次数:5685

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号