three.js入门(2) 添加灯光、阴影、动画

three.js入门(1)里已经将所要渲染的部分正确渲染了,接下来添加灯光

1.添加灯光

在three.js灯光有很多种,这里我们使用点光源(PointLight)

    //创建点光源
    var spotLight = new THREE.SpotLight( 0xffffff );
    //设置位置
    spotLight.position.set( -10, 20, -10 );
    //添加到场景
    scene.add(spotLight);

为了更好的查看灯光,添加点光源辅助线(PointLightHelper)

    //添加点光源辅助,传入点光源实例
    var spotLightHelper = new THREE.SpotLightHelper( spotLight );
    scene.add( spotLightHelper );

由于辅助线是白色的,将背景渲染为黑色

    renderer.setClearColor(0x000000);

渲染结果如下

这里写图片描述

发现场景里的物体跟之前没有什么区别,原因是不同的材质对光源的反应不相同,在示例中我们使用的是基础材质(MeshBasicMaterial),不会反射光,只会以指定的颜色渲染物体,所以我们下面就要添加新的材质了。

2.更改材质

  这里我们使用Lambert材质,Lambert材质(MeshLambertMaterial)是符合Lambert光照模型的材质。Lambert光照模型的主要特点是只考虑漫反射而不考虑镜面反射的效果,因而对于金属、镜子等需要镜面反射效果的物体就不适应,对于其他大部分物体的漫反射效果都是适用的。


    var planeGeometry = new THREE.PlaneGeometry(60, 20);
    //使用Lambert材质
    var planeMaterial = new THREE.MeshLambertMaterial({
        color: 0xffffff
    });
    var plane = new THREE.Mesh(planeGeometry, planeMaterial);
    ...

    var boxGeometry = new THREE.BoxGeometry(4, 4, 4);
    //使用Lambert材质
    var boxMaterial = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    var box = new THREE.Mesh(boxGeometry, boxMaterial);
    ...

    var sphereGeometry = new THREE.SphereGeometry(4, 32, 32);
    //使用Lambert材质
    var sphereMaterial = new THREE.MeshLambertMaterial({
        color: 0x7777ff,
        wireframe: true
    });
    var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
    ...

渲染结果如下图

这里写图片描述

3.添加阴影

渲染阴影是要消耗很多资源的,因此three默认情况下是 不生成阴影的。但添加阴影还是很容易的,首先我们要告诉renderer我们要阴影。

    //告诉渲染器我们要渲染阴影
    renderer.shadowMap.enabled = true;
    //设置阴影的类型,默认是 THREE.PCFShadowMap
    renderer.shadowMap.type = THREE.PCFSoftShadowMap; 

这个修改完后渲染结果是没有区别的,我们需要定义哪个物体投射阴影,哪个物体接收阴影,这里我们设置立方体和球投射阴影,平面接收阴影。

    //平面接收阴影
    plane.receiveShadow = true;
    ...
    //立方体和球投射阴影
    box.castShadow = true;
    ...
    sphere.castShadow = true;
    ...

要实现阴影,还有一件事要做,那就是定义哪个灯光可以产生阴影,毕竟在一个复杂的场景中是有很多光源的。

    //设置点光源可以产生阴影
    spotLight.castShadow = true;

好了,阴影产生了,来看下效果:

这里写图片描述

4.添加动画

  动画的本质是利用了人眼的视觉暂留特性,快速地变换画面,从而产生物体在运动的假象。而对于Three.js程序而言,动画的实现也是通过在每秒中多次重绘画面实现的。所以我们需要找到一种方法场景以一定的时间间隔不断的进行渲染,大家可能都想到使用 setInterval方法,但这个函数的问题在于:1.它不考虑浏览器发生的事情,如果你正在浏览其他页面,这个函数仍然会间隔时间被调用一次。2.它并没有跟显示器重画同步,这可能导致较高的cpu使用率,降低系统效率。3.在浏览器处理繁忙时,很可能低于设定值,会因此使整个程序放慢运行。幸运的是我们有requestAnimationFrame函数,它可以这样兼容:

    window.requestAnimFrame = (function () {
        return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            function (callback) {
                window.setTimeout(callback, 1000 / 60);
            };
    })();

通过requestAnimationFrame,你可以指定一个函数。按照浏览器定义的时间间隔调用,你可以在指定的函数中执行所有必要的重绘操作,而浏览器会尽可能的保证绘画过程平滑、高效。使用方法很简单,你只要写一个负责渲染的函数:

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

在render函数里,我们又调了一次requestAnimationFrame,目的是让render函数持续运行。最后我们需要在场景创建完毕后,调用一次render函数来启动动画,而不是调用renderer.render(scene,camera);

...
render();

运行上述例子,我们还看不出差别,原因是我们还没有加入任何动画,现在就让我们加入动画效果:

var step = 0;
function render() {
    //立方体转动
    box.rotation.x += 0.02;
    box.rotation.y += 0.02;
    box.rotation.z += 0.02;
    //+0.04代表的是小球的速度
    step += 0.04;
    //小球弹跳
    sphere.position.x = 20 + 10*Math.cos(step)
    sphere.position.y = 2 + 10*Math.abs(Math.sin(step))
    requestAnimationFrame(render);
    renderer.render(scene,camera);
}

查看效果:

这里写图片描述

下面贴上完整代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>three.js入门(2) 添加灯光、阴影、动画</title>
    <script src="three.js"></script>
</head>
<body>
    <script>
    // 渲染器
    var renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    renderer.setClearColor(0x000000);
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
    // 场景    
    var scene = new THREE.Scene();
    // 照相机
    var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.set(-30, 40, 30);
    camera.lookAt(new THREE.Vector3(0, 0, 0));
    // 辅助轴
    var axes = new THREE.AxisHelper(20);
    scene.add(axes);
    // 平面
    var planeGeometry = new THREE.PlaneGeometry(60, 20);
    var planeMaterial = new THREE.MeshLambertMaterial({
        color: 0xffffff
    });
    var plane = new THREE.Mesh(planeGeometry, planeMaterial);
    plane.position.set(15, 0, 0);
    plane.rotation.x = -0.5 * Math.PI;
    plane.receiveShadow = true;
    scene.add(plane);
    // 立方体
    var boxGeometry = new THREE.BoxGeometry(4, 4, 4);
    var boxMaterial = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    var box = new THREE.Mesh(boxGeometry, boxMaterial);
    box.position.set(-4, 3, 0);
    box.castShadow = true;
    scene.add(box);
    // 球
    var sphereGeometry = new THREE.SphereGeometry(4, 32, 32);
    var sphereMaterial = new THREE.MeshLambertMaterial({
        color: 0x7777ff,
        wireframe: true
    });
    var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
    sphere.position.set(20, 4, 2);
    sphere.castShadow = true;
    scene.add(sphere);
    // 灯光
    var spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 20, -10);
    spotLight.castShadow = true;
    scene.add(spotLight);
    // 灯光辅助
    var spotLightHelper = new THREE.SpotLightHelper(spotLight);
    scene.add(spotLightHelper);
    // 调用渲染函数
    render();

    var step = 0;
    function render() {
        box.rotation.x += 0.02;
        box.rotation.y += 0.02;
        box.rotation.z += 0.02;
        step += 0.04;
        sphere.position.x = 20 + 10 * Math.cos(step)
        sphere.position.y = 2 + 10 * Math.abs(Math.sin(step))
        requestAnimationFrame(render);
        renderer.render(scene, camera);
    }
    </script>
</body>
</html>

three.js入门(3) stats.js(用于显示动画运行的帧数)和dat.gui.js (创建简单的界面组件,用户修改代码中的变量)的使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值