3D区块渐变围栏

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

下面的主要代码:

javascript 复制代码
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;// 地图形状
    const group = new THREE.Group();


    // 初始化地理投影
    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 material = new THREE.ShaderMaterial({
            side: THREE.DoubleSide,// 双面
            transparent: true,// 透明
            depthTest: false,// 不检测深度
            uniforms: {
                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;                
            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,mix(1.0,0.0, vUv.y)) ;
                } 
            }`
        });

        const extrudeSettings = {
            depth: 1,// 厚度
            bevelEnabled: false// 是否使用倒角
        };

        const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
        const mesh = new THREE.Mesh(geometry, material);
        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 = () => {
        controls.update();// 如果不调用,就会很卡
        // animationRender()
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }
    animation();
}
相关推荐
轻口味17 分钟前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王1 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发1 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀1 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef3 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6414 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻4 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云4 小时前
npm淘宝镜像
前端·npm·node.js