欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

24 - three.js 笔记 - SpotLight 光源

程序员文章站 2022-05-23 13:26:05
...

SpotLight光源是聚光灯光源,类似手电筒,会形成一种锥形效果的光,可以产生阴影,比较常用。
示例浏览地址:http://ithanmang.com/threeJs/home/02-spotlight.html
24 - three.js 笔记 - SpotLight 光源

构造函数

SpotLight( color : Integer, intensity : Float, distance : Float, angle : Radians, penumbra : Float, decay : Float )

参数

color - (可选) 十六进制颜色的光 默认为 0xffffff(white).
intensity - (可选) 光的强度数值. 默认值为1.
distance - 光源从原点发出的距离,值越大,光的强度会逐渐衰减.
angle - 光源的散射角度默认为Math.PI/2.
penumbra - 光影聚焦的百分比 0-1之间,默认值为0,所以阴影会产生锯齿效果.
decay - 光在距离上的强度.

阴影相关

SpotLightShadow

这不是一个方法,这是用来在使用聚光灯的时候可以对阴影进行微调.
首先,要想产生阴影,渲染器要开启阴影支持,如下代码

// 设置渲染器的像素比例,按照设备
renderer.setPixelRatio(window.devicePixelRatio);
// 渲染背景色
renderer.setClearColor(0x050505);
// 渲染范围
renderer.setSize(window.innerWidth, window.innerHeight);
// 开启阴影支持
renderer.shadowMap.enabled = true;
// 阴影类型
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

然后就可以对阴影进行微调

// 设置阴影分辨率
spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;

// 投影近点 --> 从距离光源的哪一才产生阴影
spotLight.shadow.camera.near = 0.1;
// 投影原点 --> 到光源的哪一点位置不产生阴影
spotLight.shadow.camera.far = 300;
// 投影视场
spotLight.shadow.camera.fov = 40;

scene.add(spotLight);

// 阴影相机助手
shadowCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera);
scene.add(shadowCameraHelper);

// 聚光光源助手
spotLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotLightHelper);

以上代码在场景中添加了一下辅助对象,CameraHelperSpotLightHelper,通过这些辅助对象,可以更好去理解SpotLight一些参数变化时不同的状态。
每个灯光都会有一个助手,来debug的时候添加,了解更多查看three.js文档。
24 - three.js 笔记 - SpotLight 光源
另外值得注意的是,接收阴影的对象,也会对阴影的效果产生一定的影响,例如,用一个平面来接收阴影,则平面的分割段数的多少则影响着阴影分辨率的效果。

本篇示例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Spotlight 锥形光源</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;/*溢出隐藏*/
        }
    </style>
    <script src="../../libs/build/three.min.js"></script>
    <script src="../../libs/examples/js/controls/OrbitControls.js"></script>
    <script src="../../libs/examples/js/libs/dat.gui.min.js"></script>
    <script src="../../libs/examples/js/libs/stats.min.js"></script>
    <script src="../../libs/examples/js/Detector.js"></script>
</head>
<body>
<script>

    let stats = initStats();
    let scene, camera, renderer, spotLight, controls, guiControls;
    let shadowCameraHelper, spotLightHelper;

    // 场景
    function initScene() {

        scene = new THREE.Scene();

    }

    // 相机
    function initCamera() {

        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(-100, 300, 300);
        camera.lookAt(new THREE.Vector3(0, 0, 0));

    }

    // 渲染器
    function initRenderer() {

        renderer = new THREE.WebGLRenderer({antialias: true});
        // 设置渲染器的像素比例,按照设备
        renderer.setPixelRatio(window.devicePixelRatio);
        // 渲染背景色
        renderer.setClearColor(0x050505);
        // 渲染范围
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 开启阴影支持
        renderer.shadowMap.enabled = true;
        // 阴影类型
        renderer.shadowMap.type = THREE.PCFSoftShadowMap;

        document.body.appendChild(renderer.domElement);

    }

    // 灯光
    function initLight() {

        scene.add(new THREE.AmbientLight(0xCCCCCC));

        spotLight = new THREE.SpotLight();
        spotLight.color = new THREE.Color(0xffffff);

        spotLight.castShadow = true;

        spotLight.position.set(-80, 180, -80);

        // 光的强度 默认值为1
        spotLight.intensity = 1;
        // 从发光点发出的距离,光的亮度,会随着距离的远近线性衰减
        spotLight.distance = 350;
        // 光色散角度,默认是 Math.PI * 2
        spotLight.angle = 0.4;
        // 光影的减弱程度,默认值为0, 取值范围 0 -- 1之间
        spotLight.penumbra = 0.1;
        // 光在距离上的量值, 和光的强度类似(衰减指数)
        spotLight.decay = 1;

        // 设置阴影分辨率
        spotLight.shadow.mapSize.width = 1024;
        spotLight.shadow.mapSize.height = 1024;

        // 投影近点 --> 从距离光源的哪一才产生阴影
        spotLight.shadow.camera.near = 0.1;
        // 投影原点 --> 到光源的哪一点位置不产生阴影
        spotLight.shadow.camera.far = 300;
        // 投影视场
        spotLight.shadow.camera.fov = 40;

        scene.add(spotLight);

        // 阴影相机助手
        shadowCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera);
        scene.add(shadowCameraHelper);

        // 聚光光源助手
        spotLightHelper = new THREE.SpotLightHelper(spotLight);
        scene.add(spotLightHelper);
    }

    // 控制器
    function initControls() {

        controls = new THREE.OrbitControls(camera, renderer.domElement);
        // 添加惯性
        controls.enableDamping = true;
        // 最大偏移角度
        controls.maxPolarAngle = 0.45 * Math.PI;
        // 进制移动
        controls.noPan = true;
        // 旋转速度
        controls.rotateSpeed = 0.05;
        // 最大可视距离
        controls.maxDistance = 500;
        // 最小可视距离
        controls.minDistance = 100;

    }

    // 调试插件
    function initGui() {

        guiControls = new function () {

            this.spotLightColor = 0xffffff;
            this.intensity = 1;
            this.distance = 350;
            this.angle = 0.4;
            this.penumbra = 0.1;
            this.castShadow = true;
            this.decay = 1;

        };

        let gui = new dat.GUI();

        gui.addColor(guiControls, 'spotLightColor').onChange(function (e) {
            spotLight.color = new THREE.Color(e);
        });

        gui.add(guiControls, 'intensity', 0, 4).onChange(function (e) {
            spotLight.intensity = e;
        });

        gui.add(guiControls, 'distance', 200, 500).onChange(function (e) {
            spotLight.distance = e;
        });

        gui.add(guiControls, 'angle', 0, 1).onChange(function (e) {
            spotLight.angle = e;
        });

        gui.add(guiControls, 'penumbra', 0, 1).onChange(function (e) {
            spotLight.penumbra = e;
        });

        gui.add(guiControls, 'castShadow').onChange(function (e) {
            spotLight.castShadow = e;
        });

        gui.add(guiControls, 'decay', 0, 1).onChange(function (e) {
            spotLight.decay = e;
        });

    }

    // 场景中的内容
    function initContent() {

        // 接收阴影的片面段,也会对阴影产生一定的效果,片面段越多,阴影分辨率越清晰
        let planeGeometry = new THREE.PlaneGeometry(300, 300, 300, 300);
        let planeMaterial = new THREE.MeshLambertMaterial({color: 0x666666});
        let plane = new THREE.Mesh(planeGeometry, planeMaterial);

        // 绕 x 轴旋转 -90 度
        plane.rotation.x = -0.5 * Math.PI;
        plane.receiveShadow = true;

        scene.add(plane);

        let cubeGeometry = new THREE.CubeGeometry(20, 5, 10);
        let cubeMaterial = new THREE.MeshLambertMaterial({color: 0x99CCCC});
        let cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
        cube.castShadow = true;
        cube.position.y = 15;

        scene.add(cube);

    }

    // 性能插件
    function initStats() {

        let stats = new Stats();

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

        document.body.appendChild(stats.domElement);

        return stats;
    }

    // 更新
    function update() {

        stats.update();
        controls.update();
        shadowCameraHelper.update();
        spotLightHelper.update();

    }

    // 初始化
    function init() {
        // 兼容性判断,若不兼容会提示信息
        if (!Detector.webgl) Detector.addGetWebGLMessage();

        initScene();
        initCamera();
        initRenderer();
        initLight();
        initControls();
        initContent();
        initGui();

        window.addEventListener('resize', onWindowResize, false);
    }

    // 窗口变动触发的方法
    function onWindowResize() {

        // 重新设置相机的宽高比
        camera.aspect = window.innerWidth / window.innerHeight;

        // 更新相机投影矩阵
        camera.updateProjectionMatrix();

        // 更新渲染器大小
        renderer.setSize(window.innerWidth, window.innerHeight);

    }

    // 循环渲染
    function animate() {

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

    // 页面绘制完后加载
    window.onload = function () {

        init();
        animate();

    };

</script>
</body>
</html>