threeJs实现3D地球-旋转-自定义贴图-透明发光

这篇具有很好参考价值的文章主要介绍了threeJs实现3D地球-旋转-自定义贴图-透明发光。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

//---html (angular)---        
<!-- 3D地球 -->

<div #mapId id="mapIdBox" class="mapBox" style="width: 800px; height: 800px;"></div>
//---ts---
import {Component, ElementRef, ViewChild } from '@angular/core';
//引入three相关

import * as THREE from "three"

import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"

import * as TWEEN from "tween"

import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils';

import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';

// 引入点阵数据

import mapPoints from "/assets/js/mapPoints/mapPoints";


let glRender; // webgl渲染器

let camera; // 摄像机

let scene; // 场景,一个大容器,可以理解为html中的body

let meshGroup; // 所有Mesh的容器,后面所有Mesh都会放在这里面,方便我们管理,可理解为一个div

let controls; // 轨道控制器,实现整体场景的控制

let globeRadius = 55;

let fov = 100;


@Component({

  selector: 'testThreeEartyh',

  templateUrl: './testThreeEartyh.component.html',

  styleUrls: ['./testThreeEartyh.component.less'],

})

export class IndexCenterComponent {

        constructor(){}

        @ViewChild('mapId', { static: true }) mapId: ElementRef;

        mapDom = null

        globeSegments = 100; // 球体面数,数量越大越光滑,性能消耗越大

        animationType = true // 地球入场动画

        rotationY = true // 地球自动旋转

        meshAnimateType = false // 标记点动画

        lonlat = { x: 0, y: 0, z: 200 }

        roratTimer = null;



        //标记点数据

        objList = [

                { lon: 116.358976, lat: 39.803282, name: "中国", color: '#F03022' },

        ]



ngAfterViewInit(){

        let that = this;

        that.info ()

        // 添加标记点

        this.infoMap()

}

ngOnDestroy(){

        this.rotationY = false

        this.ballRotationY()

        clearInterval(this.roratTimer);

}

// 初始化

info () {

        let that = this

        this.infoThree()

        glRender.domElement.addEventListener("click", this.infoMouse)

        glRender.domElement.addEventListener("mouseover", function(){//鼠标移入地球停止旋转

                that.rotationY = false

                that.ballRotationY()

        })

        glRender.domElement.addEventListener("mouseout", function(){//鼠标移出地球开始旋转

                that.rotationY = true

                that.ballRotationY()

        })

}

// 基本配置

infoThree () {

        let that = this;

        // 场景

        scene = new THREE.Scene();

        meshGroup = new THREE.Group()

        // 渲染

        glRender = new THREE.WebGLRenderer({

                antialias: true,//抗锯齿

                alpha: true,//是否透明

        })

        this.mapDom = this.mapId.nativeElement;

        glRender.setSize(this.mapDom.clientWidth, this.mapDom.clientHeight)

        glRender.setClearColor(0x000, 0)//第二个参数来设置透明度0是完全透明,1是不透明

        this.mapDom.appendChild(glRender.domElement)

        // 相机

        camera = new THREE.PerspectiveCamera(

                fov,

                this.mapDom.clientWidth / this.mapDom.clientHeight,

                1,

                1000

        )

        camera.position.set(0, 0, 200)

        camera.lookAt(0, 0, 0)

        // 点阵

        // this.createMapPoints()

        // 创建地球

        this.infoBall()

        // 鼠标

        this.infoOrbitControls()

        // 轮廓描边

        this.initStrockMap(this.variableObj.mapDataObj.chinaMapFeatures)

        // 添加标记点

        // this.infoMap()

}

/**

     * 导入纹理

     * @param path

     * @returns {Promise}

*/

loadTexture(path){

        return new Promise((resolve, reject)=>{

                let loader = new THREE.TextureLoader();

                loader.load(path, texture => {

                        resolve(texture);

                } , ()=>{} , ()=>{reject('fail')});

        });

}

// 地球纹理贴图

async infoBall() {

        let that = this;

        // //创建球体

        let earthTexture = await that.loadTexture('/earth_atmos_2048_2.jpg');

        let specularMap = await that.loadTexture( '/earth_lights_2048.png' )

        let normalMap = await that.loadTexture( '/earth_normal_2048.jpg' )

        let earthGeometry = new THREE.SphereGeometry( globeRadius, that.globeSegments, that.globeSegments );

        var textureLoader = new THREE.TextureLoader();

        // 加载光照贴图

        var textureLight = textureLoader.load('/earth_clouds_2048.png');

        // let earthMaterial = new THREE.MeshBasicMaterial( { map: earthTexture, overdraw: 0.5, transparent: true,opacity: 0.6,} );//无光照

        let earthMaterial = new THREE.MeshPhongMaterial( { //有光照

                color: 0xffffff,

                map: earthTexture,

                specularMap: specularMap,

                normalMap: normalMap,

                shininess: 15,

                normalScale: new THREE.Vector2( 0.85, - 0.85 ),

                overdraw: 0.5,

                transparent: true,

                opacity: 0.9,

                emissive: 0x2eadf4, // 设置发光颜色

                emissiveIntensity: 0.8, // 设置发光强度

                lightMap:textureLight,// 设置光照贴图

        } );

        let earthMesh = new THREE.Mesh( earthGeometry, earthMaterial );

        earthMesh.castShadow = true; // 投射阴影

        meshGroup.add( earthMesh );



        //地球的云层

        let cloudTexture = await that.loadTexture('/earth_clouds_2048.png');

        let cloudGeometry = new THREE.SphereGeometry( globeRadius+1, that.globeSegments, that.globeSegments );

        // let cloudMaterial = new THREE.MeshBasicMaterial( { map: cloudTexture, overdraw: 0.5, transparent: true,side: THREE.DoubleSide} );//无光照

        let cloudMaterial = new THREE.MeshPhongMaterial( { //有光照

                map: cloudTexture,

                side: THREE.DoubleSide,

                shininess: 15,

                normalScale: new THREE.Vector2( 0.85, - 0.85 ),

                overdraw: 0.5,

                transparent: true,

                emissive: 0x2eadf4, // 设置发光颜色

                emissiveIntensity: 0.8 // 设置发光强度

        } );

        let cloudMesh = new THREE.Mesh( cloudGeometry, cloudMaterial );

        cloudMesh.castShadow = true; // 投射阴影

        meshGroup.add( cloudMesh );



        // 创建光并开启投射阴影

        var dirLight = new THREE.DirectionalLight( 0xffffff );

        dirLight.position.set( 90, 0, 200 ).normalize();

        const targetPosition = new THREE.Vector3(0, 0, 0);

        dirLight.target.position.copy(targetPosition);

        dirLight.castShadow = true;

        scene.add(dirLight);



        // 重新渲染

        // this.infoRender()

        this.roratTimer = setInterval(this.infoRender.bind(this), 17);//屏刷新频率是 60HZ ==> 也就是每秒60次. ==> 相当于1000毫秒60次 = 16.67ms一次。也就是说每16.67毫秒刷新一次是浏览器显示的最大刷新频率。我们一般设置16或者17 接近这个频率。

}



// 重新渲染

infoRender() {

        glRender.clear()

        // 地球入场动画

        if (this.animationType) this.ballAnimation()

        // 地球旋转

        if (this.rotationY) this.ballRotationY()

        // 标记点动画

        if (this.meshAnimateType) this.meshAnimate()

        glRender.render(scene, camera)

        // requestAnimationFrame(this.infoRender.bind(this))

        TWEEN.update()

}



// 鼠标

infoOrbitControls() {

        controls = new OrbitControls(camera, glRender.domElement)

        controls.enableDamping = true

        controls.enableZoom = true

        controls.autoRotate = false

        controls.autoRotateSpeed = 2

        controls.enablePan = true

}



/**

     * 生成点状世界地图方法

*/

createMapPoints() {

        // 点的基本材质.

        const material = new THREE.MeshBasicMaterial({

                // color: "#AAA",

                color: "rgba(170,170,170,0.1)",

        });

        const sphere = [];

        // 循环遍历所有点将2维坐标映射到3维坐标

        for (let point of mapPoints.points) {

                /**

                  * 我们需要获取2维点的数组,循环遍历并将每个点转换为其3维位置。这是执行转换的功能。根据您创建的模板投影的大小,您可能需要调整前几个变量

                */

                let x,y,z;

                const globeWidth = 4098 / 2;

                const globeHeight = 2050 / 2;

                let latitude = ((point['x'] - globeWidth) / globeWidth) * -180;

                let longitude = ((point['y'] - globeHeight) / globeHeight) * -90;

                latitude = (latitude * Math.PI) / 180;

                longitude = (longitude * Math.PI) / 180;

                const radius = Math.cos(longitude) * globeRadius;

                x = Math.cos(latitude) * radius;

                y = Math.sin(longitude) * globeRadius;

                z = Math.sin(latitude) * radius;

                if (x && y && z) {

                        // 生成点阵

                        const pingGeometry = new THREE.SphereGeometry(0.4, 5, 5);

                        pingGeometry.translate(x, y, z);

                        sphere.push(pingGeometry);

                }

        }

        // 合并所有点阵生成一个mesh对象

        const earthMapPoints = new THREE.Mesh(

                BufferGeometryUtils.mergeBufferGeometries(sphere),

                material

        );

        // 加入到mesh容器中

        meshGroup.add(earthMapPoints);

}



/**

    *经纬度转坐标(THREE.Vector3方式)

    *lng:经度

    *lat:纬度

    *radius:地球半径

*/

lglt2xyz(lng, lat, radius) {

        const theta = (90 + lng) * (Math.PI / 180)

        const phi = (90 - lat) * (Math.PI / 180)

        return (new THREE.Vector3()).setFromSpherical(new THREE.Spherical(radius, phi, theta))

}

// 添加中国省份轮廓线

initStrockMap( chinaJson ) {

        // 遍历省份构建模型

        chinaJson.features.forEach( elem => {

        // 新建一个省份容器:用来存放省份对应的模型和轮廓线

        const province = new THREE.Object3D();

        const coordinates = elem.geometry.coordinates;

        coordinates.forEach( multiPolygon => {

                multiPolygon.forEach( polygon => {

                        const lineMaterial = new THREE.LineBasicMaterial( { color: 0xffffff } ); //0x3BFA9E

                        const positions = [];

                        const linGeometry = new THREE.BufferGeometry();

                        for (let i = 0; i < polygon.length; i ++) {

                                var pos = this.lglt2xyz( polygon[i][0], polygon[i][1], globeRadius );

                                positions.push( pos.x, pos.y, pos.z );

                        }

                        linGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );

                        const line = new THREE.Line( linGeometry, lineMaterial );

                        province.add( line );

                } );

        } );

        meshGroup.add( province );

      } );

      scene.add( meshGroup );

}



// 地球入场动画

ballAnimation() {

        fov -= 0.8

        if (fov <= 45) {

                this.animationType = false

                this.infoOrbitControls()

        } else {

                camera = new THREE.PerspectiveCamera(

                        fov,

                        this.mapDom.clientWidth / this.mapDom.clientHeight,

                        1,

                        1000

                );

        }

        camera.position.set(0, 0, 200)

        camera.lookAt(0, 0, 0)

}



// 地球自动旋转

ballRotationY() {

        if(this.rotationY){

                meshGroup.rotateY(0.03)//每次绕y轴旋转0.0005弧度

        }else{

                meshGroup.rotateY(0)

        }

}



// 地球添加标记点

infoMap() {

        // for (let i = 0; i < this.objList.length; i++) {

        for (let i = 0; i < 1000; i++) {

                this.infoMark(this.objList[i]);

        }

}



// 添加纹理标记点

infoMark(item) {

        let cityGeometry = new THREE.PlaneGeometry(1, 1) //默认在XOY平面上

        let textureLoader = new THREE.TextureLoader()

        let texture;

        texture = textureLoader.load('/redflag.png')



        let cityWaveMaterial = new THREE.MeshBasicMaterial({

                color: item.color,

                map: texture,

                transparent: true,

                opacity: 1,

                side: THREE.DoubleSide

        })

        let mesh = new THREE.Mesh(cityGeometry, cityWaveMaterial)

        const coord = this.lon2xyz(globeRadius * 1.03, item.lon, item.lat)

        mesh.scale.set(2, 2, 2)

        // 唯一标识

        mesh.name = item.name

        mesh.privateType = 'mark'

        mesh.position.set(coord.x, coord.y, coord.z)

        const coordVec3 = new THREE.Vector3(

                coord.x, 0, coord.z

        ).normalize()

        const meshNormal = new THREE.Vector3(0, 0, 1)

        mesh.quaternion.setFromUnitVectors(meshNormal, coordVec3)

        // if (scene.getObjectByName(item.name) === undefined) {

                meshGroup.add(mesh)

                //网格模型添加到场景中

                scene.add(meshGroup)

                this.meshAnimateType = false

        // }

}



// 标记点动画

meshAnimate() {

        for (let i = 0; i < meshGroup.children.length; i++) {

                if (meshGroup.children[i].privateType === "mark") {

                          // 添加初始随机数,防止动画同步

                          meshGroup.children[i].material.opacity += Math.random() * 0.05

                          meshGroup.children[i].scale.set(

                                meshGroup.children[i].material.opacity + 7,

                                meshGroup.children[i].material.opacity + 7,

                                meshGroup.children[i].material.opacity + 7

                        )

                        if (meshGroup.children[i].scale.x >= 9) {

                                meshGroup.children[i].material.opacity = 0

                        }

                }

        }

}



// 移动相机

cameraPos(val) {

        // this.frameDivClose ()

        let layerObj = scene.getObjectByName(val.name)

        if (layerObj) {

                scene.rotation.y = 0

                this.rotationY = false

                new TWEEN.Tween( { x: this.lonlat.x, y: this.lonlat.y, z: this.lonlat.z } )

                .to( { x: layerObj.position.x * 2.8, y: layerObj.position.y * 2.8, z: layerObj.position.z * 2.8}, 1500 )

                .onUpdate( function () {

                        camera.position.x = this.x

                        camera.position.y = this.y

                        camera.position.z = this.z

                        camera.lookAt(0, 0, 0)

                })

                .onComplete ( ()=> {  })

                .easing(TWEEN.Easing.Sinusoidal.InOut)

                .start()

                this.lonlat = camera.position

        } else {

                console.log('图层数据已被全部删除,请重新刷新界面,或者重新调用数据初始化方法: this.infoMap ()')

        }

}



// 鼠标事件(点击标记的点的事件)

//@ts-ignore

infoMouse(event) {

        console.log(event)

        let that = this

        let elementCanvas = document.getElementById('mapIdBox')

        event.preventDefault();

        const raycaster = new THREE.Raycaster();

        const mouse = new THREE.Vector2();

        // 通过鼠标点击位置,计算出 raycaster 所需点的位置,以屏幕为中心点,范围 -1 到 1

        let getBoundingClientRect = elementCanvas.getBoundingClientRect();

        mouse.x = ((event.clientX - getBoundingClientRect.left) / elementCanvas.offsetWidth) * 2 - 1;

        mouse.y = -((event.clientY - getBoundingClientRect.top) / elementCanvas.offsetHeight) * 2 + 1;

        //通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置

        raycaster.setFromCamera(mouse, camera);

        // 获取与射线相交的对象数组,其中的元素按照距离排序,越近的越靠前

        let intersects = raycaster.intersectObjects(scene.children);

        // 点击对象的处理

        for (let i = 0; i < intersects.length; i++) {

                if (intersects[i].object.name !== 'ballMain') {

                        let objListObj = {

                                name: intersects[i].object.name

                        }

                        if(objListObj.name != ""){

                                that.cameraPos(objListObj)

                        }

                        return false

                } else {

                        // 开启自动旋转

                        this.rotationY = true

                }

        }

}



// 经纬度转坐标(js方式)

lon2xyz(R, longitude, latitude) {

        const lon = (Number(longitude) + 90) * (Math.PI / 180)

        const lat = Number(latitude) * (Math.PI / 180)

        const x = R * Math.cos(lat) * Math.sin(lon)

        const y = R * Math.sin(lat)

        const z = R * Math.cos(lon) * Math.cos(lat)

        return { x, y, z }

}

}

效果图:

thress.js 旋转3d地球,threeJs,angular.js,3d,贴图文章来源地址https://www.toymoban.com/news/detail-854127.html

到了这里,关于threeJs实现3D地球-旋转-自定义贴图-透明发光的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用 Threejs 从基础开始构建 3D 地球

    演示效果 git源码地址 场景创建 相机 坐标辅助器 状态检测器 渲染器 轨道控制器 粒子星空 地球和大气层创建 创建星轨环 创建卫星移动轨迹 创建卫星 二维经纬度坐标转三维球坐标 创建标点 绘制飞线 在地球上绘制标点和飞线 一般在执行完上述方法后能看到如下图的效果:

    2024年01月25日
    浏览(61)
  • VUE3+ThreeJs实现3D全景场景,可自由旋转视角

    three.js是一个用于在Web上创建三维图形的JavaScript库。它可以用于创建各种类型的三维场景,包括游戏、虚拟现实、建筑和产品可视化等。three.js提供了许多功能和特性,包括3D渲染、光照、材质、几何形状、动画、交互和相机控制等。使用three.js,开发人员可以轻松地创建复杂

    2024年02月11日
    浏览(57)
  • ThreeJS——在3D地球上标记中国地图板块

    地球预览视频效果 TweenJS (动画库)用来做相机转场的动画 Jquery (这里只用到一个 each 循环方法,可以使用 js 去写) ThreeJS (3D 地球制作) 100000.json (全国城市经纬度) d3.v6.js 用来设置平面转3D效果(本来考虑做成3D的中国地图板块,最后因效果看起来比较美观还是考虑用线条嵌入球体

    2024年02月12日
    浏览(40)
  • 基于threejs开发的3D地球大屏可视化,支持2D地图模式,飞线,涟漪,配置简单易上手

    基于threejs 封装的3D可视化地球组件,开箱即用 主要实现功能 根据geojson格式的json文件,渲染平面2D 和3D地图,地图可配置区域色,边界色 支持以贴图的方式实现用户设计的个性化地图图片的3D地球渲染(2D的支持正在开发) 通过本组件提供的方法可实现 新增飞线动画 和 标记

    2024年02月08日
    浏览(65)
  • ThreeJS-3D教学六-物体位移旋转

    之前文章其实也有涉及到这方面的内容,比如在ThreeJS-3D教学三:平移缩放+物体沿轨迹运动这篇中,通过获取轨迹点物体动起来,其它几篇文章也有旋转的效果,本篇我们来详细看下,另外加了tween.js知识点,tween可以很好的协助 three 做动画,与之相似的还有 gsap.js方法类似。

    2024年02月04日
    浏览(48)
  • VTK————3D模型的旋转、交互、透明度设置

    旋转模型的话可以用到以下例程,但是每次设置选择坐标时,都是累加的。可以通过SetOrientation来将模型方向设为初试方向。如果三个坐标都使用较大值来进行旋转,那么由于累加的特性。模型会在空间内进行大幅度的旋转和位移,这是需要注意的。 也可以通过设置interacto

    2024年02月10日
    浏览(55)
  • Web3D数学基础(平移、旋转、缩放矩阵)—WebGL、WebGPU、Threejs

    参考资料:threejs中文网 threejs qq交流群:814702116 本下节课给大家介绍下矩阵的概念,以及用于几何变换的矩阵,比如平移矩阵、缩放矩阵、旋转矩阵。 如果你对这些几何变换的矩阵概念比较熟悉,可以跳过本节课。 线性代数、图形学 如果你有《线性代数》、《计算机图形学

    2024年02月03日
    浏览(53)
  • Threejs发光闪烁提示特效

    发光闪烁特效应该在我们的项目中是经常需要去封装的一个特效吧,一般用于点击选择,选中物体,或者一些特效加持于中心物体,物体碰撞检测后的发光特效等等 我们可以合理的使用后处理特效,上步骤: 首先,我们利用后处理效果 然后整合输出于屏幕 再加持自定义着色

    2024年02月04日
    浏览(47)
  • ThreeJs的场景实现鼠标拖动旋转控制

            前面一个章节中已经实现在场景中放置一个正方体,并添加灯光使得正方体可见。但是由于是静态的还不能证明是3D的,我们需要添加一些控制器,使得通过鼠标控制正方体可以动起来,实现真正的3D效果,由此引入OrbitControls组件,他实质是改变相机的位置,实现

    2024年02月07日
    浏览(59)
  • Threejs实现数字人绿幕视频背景透明播放,Shader绿幕视频抠像

    个人主页: 左本Web3D,更多案例预览请点击==》 在线案例 个人简介:专注Web3D使用ThreeJS实现3D效果技巧和学习案例 💕 💕积跬步以至千里,致敬每个爱学习的你。喜欢的话请三连,有问题请私信或者加微信         在数字人应用中,绿幕技术是一种常见的技术,也是实

    2024年02月10日
    浏览(156)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包