现实世界中(假设只在地球,先不考虑外太空three.js给场景加上阴影three.js给场景加上阴影three.js给场景加上阴影),只要有光的地方就会有阴影产生。所以当我们构建好一个具备光照的场景后,还必须给其加上阴影,才能更清楚地显示场景中各个元素的关系,光照和阴影是webgl和three.js开发大型场景必不可少的元素,如果少了这两者,想象一下,地球上一切事物如果没有阴影,没有灯光,那么是不是世界乌漆嘛黑,什么都看不见?本节先不讲光照,先讲讲阴影的投射和接收,各种光源和材质的介绍和应用,在后面会单独写文章进行详细介绍。接下来先看看怎样给场景添加阴影。

添加阴影主要是这几个步骤:

渲染器一定要允许产生阴影:

//渲染器
//antialias:true增加抗锯齿效果
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setClearColor(new THREE.Color(0x000000));//设置窗口背景颜色为黑
renderer.setSize(window.innerWidth, window.innerHeight);//设置窗口尺寸
renderer.shadowMapEnabled = true;//开启阴影,加上阴影渲染

灯光要投下阴影,否则场景不会产生阴影:

var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-50, 60, 15);
spotLight.castShadow = true;//开启灯光投射阴影

物体要投下阴影和接受阴影,假设这里的物体为Mesh;否则也不会有阴影产生:

Mesh.castShadow = true;//开启投影
Mesh.receiveShadow = true;//接收阴影
当阴影添加完毕后,就可以产生阴影的效果啦。接下来实现一个有阴影的场景,其效果如下图所示:

three.js给场景加上阴影

这个实例的静态版本在我另一片博文:点击打开链接,轨道控制器鼠标交互的版本在这篇博文:点击打开链接

但有一点需要特别注意,光源的位置一定要距离合适,否则容易引起阴影模糊粗糙的像打马赛克一样three.js给场景加上阴影three.js给场景加上阴影three.js给场景加上阴影,像下面的图所示:

three.js给场景加上阴影

下面附上示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>threejs-basic-geometry</title>
    <style>
        body{
            font-family: Monospace;
            background: #f0f0f0;
            margin: 0px;
            overflow: hidden;
        }
    </style>
</head>
<body>
<script type="text/javascript" src="build/three.js"></script>
<script type="text/javascript" src="js/Detector.js"></script>
<script type="text/javascript" src="js/controls/OrbitControls.js"></script>
<script type="text/javascript">
    //检测webgl的支持情况
    if(!Detector.webgl) {Detector.addGetWebGLMessage();}
    var container;
    var camera, scene, renderer;
    //用于轨道控制器
    var orbitControls, clock, delta;
    main();
    render();
    //主函数
    function main(){
        //添加一个div元素
        container = document.createElement('div')
        document.body.appendChild(container);
        scene = new THREE.Scene();//创建一个新场景
        //添加一个透视相机
        camera = new THREE.PerspectiveCamera(30,
                      window.innerWidth/window.innerHeight, 1, 1000);
        camera.position.set(100, 300, 100);//设置相机位置
        camera.lookAt(new THREE.Vector3(0,0,0));//让相机指向原点
        //渲染器
        //antialias:true增加抗锯齿效果
        renderer = new THREE.WebGLRenderer({antialias:true});
        renderer.setClearColor(new THREE.Color(0x000000));//设置窗口背景颜色为黑
        renderer.setSize(window.innerWidth, window.innerHeight);//设置窗口尺寸
        renderer.shadowMapEnabled = true;
        //将renderer关联到container,这个过程类似于获取canvas元素
        container.appendChild(renderer.domElement);
        //添加轨道控制器
        //新建一个轨道控制器
        orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
        orbitControls.target = new THREE.Vector3(0, 0, 0);//控制焦点
        orbitControls.autoRotate = false;//将自动旋转关闭
        clock = new THREE.Clock();//用于更新轨道控制器
        //给场景添加光源
        //自然光
        var ambientLight = new THREE.AmbientLight( 0x0c0c0c );
        scene.add( ambientLight );
//        //平行光源
//        var directionalLight = new THREE.DirectionalLight( 0xffffff );
//        directionalLight.position.set( 1, 0.75, 0.5 ).normalize();
//        directionalLight.castShadow = true;
//        scene.add(directionalLight)
        var spotLight = new THREE.SpotLight(0xffffff);
        //spotLight.position.set(-50, 60, 15);
        spotLight.position.set(-200, 160, 85);
        spotLight.castShadow = true;//允许投射阴影
        scene.add(spotLight);
        plane();
        cube();
        sphere();
        cylinder();
    }
    //创建一个平面
    function plane(){
        var planeGeo = new THREE.PlaneGeometry(100,100,10,10);//创建平面
        var planeMat = new THREE.MeshLambertMaterial({  //创建材料
            color:0xFFFFFF,
            wireframe:false
        });
        var planeMesh = new THREE.Mesh(planeGeo, planeMat);//创建网格模型
        planeMesh.position.set(0, 0, -20);//设置平面的坐标
        planeMesh.rotation.x = -0.5 * Math.PI;//将平面绕X轴逆时针旋转90度
        planeMesh.receiveShadow = true;//允许接收阴影
        scene.add(planeMesh);//将平面添加到场景中
    }
    //创建一个立方体
    function cube(){
        var cubeGeo = new THREE.CubeGeometry(20, 20, 20, 5, 5, 5);//创建立方体
        var cubeMat = new THREE.MeshLambertMaterial({//创建材料
            color:0x003300,
            wireframe:false
        });
        var cubeMesh = new THREE.Mesh(cubeGeo, cubeMat);//创建立方体网格模型
        cubeMesh.position.set(15, 10, 0);//设置立方体的坐标
        cubeMesh.castShadow = true;//允许投射阴影
        cubeMesh.receiveShadow = true;//允许接收阴影
        scene.add(cubeMesh);//将立方体添加到场景中
    }
    //创建一个球
    function sphere(){
        var sphereGeo = new THREE.SphereGeometry(16, 40, 40);//创建球体
        var sphereMat = new THREE.MeshLambertMaterial({//创建材料
            color:0x0000FF,
            wireframe:false
        });
        var sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);//创建球体网格模型
        sphereMesh.position.set(-25, 16, 0);//设置球的坐标
        sphereMesh.castShadow = true;//允许投射阴影
        sphereMesh.receiveShadow = true;//允许接收阴影
        scene.add(sphereMesh);//将球体添加到场景
    }
    //创建圆柱体
    function cylinder(){
        //创建圆柱体
        var cylinderGeo = new THREE.CylinderGeometry(15, 15 ,40 ,40 ,40);
        var cylinderMat = new THREE.MeshLambertMaterial({//创建材料
            color:0xFF6600,
            wireframe:false
        });
        //创建圆柱体网格模型
        var cylinderMesh = new THREE.Mesh(cylinderGeo, cylinderMat);
        cylinderMesh.position.set(0, 20, -40);//设置圆柱坐标sphere
        cylinderMesh.castShadow = true;//允许投射阴影
        cylinderMesh.receiveShadow = true;//允许接收阴影
        scene.add(cylinderMesh);//向场景添加圆柱体
    }
    //渲染
    function render(){
        delta = clock.getDelta();
        orbitControls.update(delta);
        requestAnimationFrame(render);
        renderer.render(scene, camera);
    }
</script>
</body>
</html>


相关文章: