three.js实现雷达扫描效果(three.js练习)

一、先看看效果

在这里插入图片描述

二、实现方法:

1.雷达扫描的相关配置数据准备

// 数组形式的数据,可以绘制多个
const radarData = [{
     position: { // 位置
         x: -100,
         y: 0,
         z: 0
     },
     radius: 80, // 半径大小
     color: '#ff0062', // 颜色
     opacity: 0.5, // 颜色最深的地方的透明度
     angle: Math.PI * 2, // 扫描区域大小的弧度值
     speed: 2 // 旋转的速度
 },{
     position: {
         x: 100,
         y: 0,
         z: 10
     },
     radius: 50,
     color: '#f000f2',
     opacity: 0.5,
     angle: Math.PI,
     speed: 2
 }];

2. 创建雷达的函数

function initRadar(options) {
	// 接受传入的参数
     const {
         position,
         radius,
         color,
         opacity,
         speed,
         angle,
     } = options;
	
	// 计算创建的矩形的大小,等于雷达扫描半径的2倍
     const size = radius * 2;
	// 创建一个矩形
     let plane = new THREE.PlaneGeometry(size, size);
     // 创建shader材质
     let material_1 = new THREE.ShaderMaterial({
         transparent: true,
         depthWrite: false,
         side: THREE.DoubleSide,
         uniforms: {
             uTime: ratio, // 时间
             u_radius: {  // 半径
                 value: radius
             },
             u_speed: { // 旋转速度
                 value: speed
             },
             u_opacity: { // 默认透明度
                 value: opacity
             },
             u_width: { // 扫描区域的大小
                 value: angle
             },
             u_color: { // 颜色
                 value: new THREE.Color(color)
             },

         },
         vertexShader,  // 顶点着色器代码源
         fragmentShader // 片源着色器代码源
     })
     // 创建mesh并添加到场景中
     let planeMesh = new THREE.Mesh(plane, material_1);
     scene.add(planeMesh);

     planeMesh.rotation.x = -0.5 * Math.PI
     // 设置位置为传入的位置
     planeMesh.position.copy(position);
 }

3、调用方法,创建雷达扫描

// 创建雷达扫描
 radarData.forEach(item => {
     initRadar(item);
 });

4、render函数中更新time

let next = 0;
render();
function render () {
    next += 0.01
    ratio.value = next;

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

5、着色器部分(核心代码)

// 顶点着色器
const vertexShader =  `
     precision mediump float;
     precision highp int;
     
     varying vec2 vPosition;
     void main () {
     	// 把当前像素点的x和y专递给片元着色器,这里我们在xy轴所在平面上画图,不考虑z轴
         vPosition = vec2(position.x, position.y);
         gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
 }`;
 const fragmentShader =  `
     precision mediump float;
     precision highp int;

	// 接收从js中传入的uniform数据
     uniform float uTime;
     uniform float u_radius;
     uniform float u_speed;
     uniform float u_opacity;
     uniform float u_width;
     uniform vec3 u_color;

     varying vec2 vPosition;
     #define PI 3.14159265359

     void main () {
         // 计算当前扫描旋转的弧度值总数
         float currentRadius = u_speed * uTime;

         // 计算当前像素点与原点连线和x轴构成的夹角的弧度值
         // atan 接受两个参数(y,x)时 等同于 atan2,返回的是atan(y/x);
         // 但比后者更稳定,返回值区间[-PI, PI]
         float angle = atan(vPosition.y, vPosition.x) + PI;

         // 计算当前像素低旋转后的弧度值,值固定在[0, PI * 2]之间
         float angleT = mod(currentRadius + angle, PI * 2.0);

         // 计算当前位置距离中心点距离
         float dist = distance(vec2(0.0, 0.0), vPosition);
         
         float tempOpacity = 0.0;

         // 设置雷达外层圆环的宽度
         float circleWidth = 5.0;
         // 如果当前点在外层圆环上, 设置一个透明度
         if (dist < u_radius && dist > u_radius - circleWidth) {
             // 做一个虚化渐变效果
             float pct = smoothstep(u_radius - circleWidth, u_radius, dist);
             tempOpacity = sin(pct * PI);
         }

         // 设置雷达扫描圈的效果 (-5.0是给外层圆环和内层圆之间设置一点空白间距)
         if (dist < (u_radius - 5.0)) {
             tempOpacity = 1.0 - angleT / u_width;
         }
		// 设置颜色
         gl_FragColor = vec4(u_color, u_opacity * tempOpacity);
 }
 `;

三、实例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>雷达扫描</title>
    <style>
        body {
            height: 100vh;
            overflow: hidden;
        }
    </style>
    
    <script src="./lib/three.js"></script>
    <script src="./lib/OrbitControls.js"></script>
</head>
<body>
<script>
    let scene, camera, renderer, controls, mixer, tubeCurve, AgvCar;
    let width = window.innerWidth;
    let height = window.innerHeight

    let ratio = {
        value: 0
    }

    // 雷达扫描的相关配置数据
    const radarData = [{
        position: {
            x: -100,
            y: 0,
            z: 0
        },
        radius: 80,
        color: '#ff0062',
        opacity: 0.5,
        angle: Math.PI * 2,
        speed: 2
    },{
        position: {
            x: 100,
            y: 0,
            z: 10
        },
        radius: 50,
        color: '#f000f2',
        opacity: 0.5,
        angle: Math.PI,
        speed: 2
    }];

    function init () {
        // 场景
        scene = new THREE.Scene();
        let helper = new THREE.AxesHelper(100, 100);
        scene.add(helper);

        // 环境光
        let light = new THREE.AmbientLight(0xadadad); // soft white light
        scene.add(light);

        // 平行光源
        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
        directionalLight.position.set(100, 100, 0);
        scene.add(directionalLight); 

        // 相机
        camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000)
        camera.position.set(300, 320, 60)
        scene.add(camera)

        // 渲染器
        renderer = new THREE.WebGLRenderer();
        renderer.setSize(width, height)
        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        renderer.setClearColor(new THREE.Color('#eeeeee'), 1);
        document.body.appendChild(renderer.domElement);

        const vertexShader =  `
            precision mediump float;
            precision highp int;

            varying vec2 vPosition;

            void main () {
                vPosition = vec2(position.x, position.y);
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }`;
        const fragmentShader =  `
            precision mediump float;
            precision highp int;

            uniform float uTime;
            uniform float u_radius;
            uniform float u_speed;
            uniform float u_opacity;
            uniform float u_width;
            uniform vec3 u_color;

            varying vec2 vPosition;
            #define PI 3.14159265359

            void main () {
                // 计算当前扫描旋转的弧度值总数
                float currentRadius = u_speed * uTime;

                // 计算当前像素点与原点连线和x轴构成的夹角的弧度值
                // atan 接受两个参数(y,x)时 等同于 atan2,返回的是atan(y/x);
                // 但比后者更稳定,返回值区间[-PI, PI]
                float angle = atan(vPosition.y, vPosition.x) + PI;

                // 计算当前像素低旋转后的弧度值,值固定在[0, PI * 2]之间
                float angleT = mod(currentRadius + angle, PI * 2.0);

                // 计算当前位置距离中心点距离
                float dist = distance(vec2(0.0, 0.0), vPosition);
                
                float tempOpacity = 0.0;

                // 设置雷达外层圆环的宽度
                float circleWidth = 5.0;
                // 如果当前点在外层圆环上, 设置一个透明度
                if (dist < u_radius && dist > u_radius - circleWidth) {
                    // 做一个虚化渐变效果
                    float pct = smoothstep(u_radius - circleWidth, u_radius, dist);
                    tempOpacity = sin(pct * PI);
                }

                // 设置雷达扫描圈的效果 (-5.0是给外层圆环和内层圆之间设置一点空白间距)
                if (dist < (u_radius - 5.0)) {
                    tempOpacity = 1.0 - angleT / u_width;
                }



                gl_FragColor = vec4(u_color, u_opacity * tempOpacity);
        }
        `;

        // 创建雷达扫描
        radarData.forEach(item => {
            initRadar(item);
        });
        
        function initRadar(options) {
            const {
                position,
                radius,
                color,
                opacity,
                speed,
                angle,
            } = options;

            const size = radius * 2;

            let plane = new THREE.PlaneGeometry(size, size);
            let material_1 = new THREE.ShaderMaterial({
                transparent: true,
                depthWrite: false,
                side: THREE.DoubleSide,
                uniforms: {
                    uTime: ratio,
                    u_radius: {
                        value: radius
                    },
                    u_speed: {
                        value: speed
                    },
                    u_opacity: {
                        value: opacity
                    },
                    u_width: {
                        value: angle
                    },
                    u_color: {
                        value: new THREE.Color(color)
                    },

                },
                vertexShader, 
                fragmentShader
            })
            let planeMesh = new THREE.Mesh(plane, material_1);
            scene.add(planeMesh);

            planeMesh.rotation.x = -0.5 * Math.PI
            planeMesh.position.copy(position);
        }

        

        let next = 0;
        render();
        function render () {
            next += 0.01
            ratio.value = next;

            requestAnimationFrame(render);
            renderer.render(scene, camera);
        }
        controls = new THREE.OrbitControls(camera, renderer.domElement);
    }
    window.onload = init
</script>
    
</body>
</html>

版权声明:本文为weixin_40856652原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。