当前位置: 首页 > article >正文

3D区块多重渐变围栏

这里主要用到的就是threejs的shader,至于其他知识点,可以参考json生成3d区域

下面的主要代码: 

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
import * as d3geo from 'd3-geo'
import guangzhouJSON from '../assets/json/guangzhou.json'

export default (domId) => {
    /* ------------------------------初始化三件套--------------------------------- */
    const dom = document.getElementById(domId);
    const { innerHeight, innerWidth } = window

    const scene = new THREE.Scene();

    const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 1, 2000);
    camera.position.set(0, 0, 10);
    camera.lookAt(scene.position);

    const renderer = new THREE.WebGLRenderer({
        antialias: true,// 抗锯齿
        alpha: false,// 透明度
        powerPreference: 'high-performance',// 性能
        logarithmicDepthBuffer: true,// 深度缓冲
    })
    // renderer.setClearColor(0x000000, 0);// 设置背景色
    // renderer.clear();// 清除渲染器
    renderer.shadowMap.enabled = true;// 开启阴影
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;// 阴影类型
    renderer.outputEncoding = THREE.sRGBEncoding;// 输出编码
    renderer.toneMapping = THREE.ACESFilmicToneMapping;// 色调映射
    renderer.toneMappingExposure = 1;// 色调映射曝光
    renderer.physicallyCorrectLights = true;// 物理正确灯光
    renderer.setPixelRatio(devicePixelRatio);// 设置像素比
    renderer.setSize(innerWidth, innerHeight);// 设置渲染器大小
    dom.appendChild(renderer.domElement);

    // 重置大小
    window.addEventListener('resize', () => {
        const { innerHeight, innerWidth } = window
        camera.aspect = innerWidth / innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(innerWidth, innerHeight);
    })

    /* ------------------------------初始化工具--------------------------------- */
    const controls = new OrbitControls(camera, renderer.domElement) // 相机轨道控制器
    controls.enableDamping = true // 是否开启阻尼
    controls.dampingFactor = 0.05// 阻尼系数
    controls.panSpeed = -1// 平移速度

    // const axesHelper = new THREE.AxesHelper(10);
    // scene.add(axesHelper);

    /* ------------------------------正题--------------------------------- */
    // 相机控制器配置
    const cameraControl = {
        autoCamera: true,// 是否自动旋转
        height: 10,// 相机高度
        width: 0.5,// 相机宽度
        depth: 1,// 相机深度
        cameraPosX: 10,// 相机位置x
        cameraPosY: 181,// 相机位置y
        cameraPosZ: 116,// 相机位置z
        autoRotate: false,// 是否自动旋转
        rotateSpeed: 2000// 旋转速度
    }

    let geoFun;// 地理投影函数
    let guangzhouData = [];// 地图数据
    let shape = null;// 地图形状
    let time = 1;// 时间
    const group = new THREE.Group();
    const edgeMaterial = new THREE.ShaderMaterial({
        side: THREE.DoubleSide,
        transparent: true,
        depthTest: false,
        uniforms: {
            time: { value: 0.0 },
            num: { value: 5.0 },
            color1: { value: new THREE.Color('#00FFFF') }
        },
        vertexShader: ` 
          varying vec2 vUv;
          varying vec3 vNormal;
          void main() {
            vUv=uv;
            vNormal=normal;           
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
          }`,
        fragmentShader: `
        uniform vec3 color1;  
        uniform float time;  
        uniform float num;                                 
        varying vec2 vUv; 
        varying vec3 vNormal;
        void main() {
            if(vNormal.z==1.0||vNormal.z==-1.0||vUv.y ==0.0){
                discard;
            }
            else{
                gl_FragColor =  vec4(color1, 1.0-fract((vUv.y-time ) *num) ) ;
            } 
        }`
    });


    // 初始化地理投影
    const initGeo = (size) => {
        geoFun = d3geo.geoMercator().scale(size || 100)
    }

    // 经纬度转像素坐标
    const latlng2px = (pos) => {
        if (pos[0] >= -180 && pos[0] <= 180 && pos[1] >= -90 && pos[1] <= 90) {
            return geoFun(pos);
        }
        return pos;
    };

    // 处理地图数据
    const processData = () => {
        guangzhouData = guangzhouJSON.features[0].geometry.coordinates[0][0];
        // 数据 从 经纬度-> 像素坐标-> 三维坐标
        guangzhouData = guangzhouData.map(item => {
            const two = latlng2px(item);
            const three = new THREE.Vector3(two[0], 0, two[1]);
            return three;
        })
    }

    // 创建地图块
    const createMap = () => {
        const extrudeSettings = {
            depth: 0.2,// 区块厚度
            bevelEnabled: false// 是否使用倒角
        };

        shape = new THREE.Shape();
        shape.moveTo(guangzhouData[0].x, guangzhouData[0].z);// 移动到第一个点
        for (let i = 1; i < guangzhouData.length; i++) {
            shape.lineTo(guangzhouData[i].x, guangzhouData[i].z);// 连线
        }
        shape.lineTo(guangzhouData[0].x, guangzhouData[0].z);// 闭合
        const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);

        const img = new URL('../assets/images/tex.png', import.meta.url).href;
        const text = new THREE.TextureLoader().load(img);
        text.wrapS = THREE.RepeatWrapping;// 水平方向
        text.wrapT = THREE.RepeatWrapping;// 垂直方向
        const material = new THREE.MeshBasicMaterial({
            map: text,
            color: new THREE.Color('#00FFFF')
        })
        const mesh = new THREE.Mesh(geometry, material);
        mesh.rotateX(Math.PI / 2);// 旋转90度
        mesh.position.y = extrudeSettings.depth * 0.5;
        group.add(mesh);
    }

    // 创建边缘线
    const createEdge = () => {
        const extrudeSettings = {
            depth: 1,// 厚度
            bevelEnabled: false// 是否使用倒角
        };
        const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
        const mesh = new THREE.Mesh(geometry, edgeMaterial);
        mesh.rotation.x = Math.PI * 0.5;
        mesh.position.y = extrudeSettings.depth + 0.1;
        group.add(mesh);
    }

    // 设置模型的中心点
    const setModeCenter = (object, viewControl) => {
        // 如果对象不存在,则返回   
        if (!object) {
            return;
        }
        if (object.updateMatrixWorld) {
            object.updateMatrixWorld();// 更新模型矩阵
        }
        // 获得包围盒得min和max
        let box = new THREE.Box3().setFromObject(object);

        let objSize;// 获取包围盒的尺寸
        try {
            objSize = box.getSize();
        } catch (error) {
            objSize = new THREE.Vector3(
                Math.abs(box.max.x - box.min.x),
                Math.abs(box.max.y - box.min.y),
                Math.abs(box.max.z - box.min.z)
            );
        }

        // 返回包围盒的中心点
        const center = box.getCenter(new THREE.Vector3());
        object.position.x += object.position.x - center.x;
        object.position.y += object.position.y - center.y;
        object.position.z += object.position.z - center.z;

        let width = objSize.x;
        let height = objSize.y;
        let depth = objSize.z;

        // 设置相机位置
        let centroid = new THREE.Vector3().copy(objSize);
        centroid.multiplyScalar(0.5);
        if (viewControl.autoCamera) {
            camera.position.x =
                centroid.x * (viewControl.centerX || 0) + width * (viewControl.width || 0);
            camera.position.y =
                centroid.y * (viewControl.centerY || 0) + height * (viewControl.height || 0);
            camera.position.z =
                centroid.z * (viewControl.centerZ || 0) + depth * (viewControl.depth || 0);
        } else {
            camera.position.set(
                viewControl.cameraPosX || 0,
                viewControl.cameraPosY || 0,
                viewControl.cameraPosZ || 0
            );
        }
        camera.lookAt(0, 0, 0);
    }

    // 初始化
    const init = () => {
        initGeo(180)// 初始化地理投影
        processData()// 处理地图数据
        createMap()// 创建地图块
        createEdge()// 创建边缘线
        scene.add(group);// 添加到场景中
        setModeCenter(group, cameraControl)// 设置模型的中心点
    }
    init();

    /* ------------------------------动画函数--------------------------------- */
    const animation = () => {
        if (edgeMaterial) {
            if (time >= 1.0) {
                time = 0.0;
            }
            time += 0.005;
            edgeMaterial.uniforms.time.value = time;
        }
        controls.update();// 如果不调用,就会很卡
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }
    animation();
}


http://www.kler.cn/a/377605.html

相关文章:

  • 【MySQL】深层理解索引及特性(重点)--下(12)
  • QChart中柱形图的简单使用并实现【Qt】
  • Kafka在大数据处理中的作用及其工作原理
  • 钉钉向广告低头
  • 火山引擎VeDI数据服务平台:在电商场景中,如何解决API编排问题?
  • Axure大屏可视化模板:赋能各行各业的数据展示与管理
  • 【Linux】mnt命名空间-操作
  • NLP segment-20-分词开源项目介绍 HanLP 未来十年的自然语言处理
  • SpringBoot 在初始化加载无法使用@Value的时候读取配置文件教程
  • Admin.NET源码学习(5:swagger使用浅析)
  • Flutter 简述(1)
  • vue常用的修饰符有哪些
  • 外观模式及运用场景
  • Apifox 10月更新|测试步骤支持添加脚本和数据库操作、测试场景支持回收站、变量支持「秘密」类型
  • 关于安卓Handler之延时我不准时
  • Nginx 报错400 Request Header Or Cookie Too Large
  • 【MogDB】MogDB5.2.0重磅发布第九篇-SQL宽容性提升
  • npm入门教程7:npm语义化版本控制
  • Flink CDC 同步 Mysql 数据
  • 今日 AI 简报|多智能体协作平台、全能 AI 音频生成、长文本生成框架等前沿 AI 技术与应用
  • 【.NET 8 实战--孢子记账--从单体到微服务】--简易权限--接口路径管理
  • K 临近算法
  • AJ-Report:一款开源且非常强大的数据可视化大屏和报表工具
  • Nginx 深度解析:高性能 Web 服务器与反向代理的艺术
  • Hcia知识汇总
  • 局部加权回归