前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >threejs地球、星空、世界轮廓绘制、飞线、坐标涟漪 、旋转动画(上篇)

threejs地球、星空、世界轮廓绘制、飞线、坐标涟漪 、旋转动画(上篇)

作者头像
星宇大前端
发布2022-03-09 11:24:58
9.7K0
发布2022-03-09 11:24:58
举报
文章被收录于专栏:大宇笔记

3D 地球成果展示

github仓库地址:https://github.com/RainManGO/3d-earth

npm:https://www.npmjs.com/package/3d-earth

支持vue/react/html 嵌入简单。

请添加图片描述
请添加图片描述

是不是有点干,咽不下去了。

前言

头一阵子B站,抖音都被 陶大宇大哥的倒转地球刷屏了,终于热度下去了,不用倒转头七了。

请添加图片描述
请添加图片描述

真的和地球扛上了,公司的大屏项目需要科技感的地球、飞线图。

为什么不用echarts

公司数据分类项目和大屏项目使用echart 比较多,对echart使用不能说是手到擒来,也是比较熟练地。

个人比较倾向于它,最重要的配置型,找到个案例复制粘贴完事。

于是和设计商量下做了一版,最终还是被客户否决了。

原因如下:

  1. 加载慢
  2. 不漂亮

饿,echarts 灵活度没有那么高,只能想别的办法了,最后定位ThreeJs。ThreeJs需要一定的计算机视图知识,从来没有学过,必定是场恶战。

实现过程

做完了,回头看来,一些东西看似简单,还是需要细细品味分解。把总体过程分解一一说来。因为涉及代码过多,不全部贴出来说明,具体查看github 仓库源码。直说结构过程。

目标设计样子:

请添加图片描述
请添加图片描述

实现步骤分解:

  1. ThreeJS环境初始化
  2. 星空背景
  3. 添加带纹理的地球
  4. 世界地图轮廓边界绘制
  5. 地球光晕
  6. 添加地球云层
  7. 城市位置标注和涟漪效果
  8. 添加飞线B样条
  9. 地球自转和镜头缩放动画

初始化

初始化列表:

  1. webgl渲染器(WebGLRenderer)和css2d 渲染器(CSS2DRenderer)
  2. 透视投影相机 (PerspectiveCamera)
  3. 场景(scene)
  4. 轨道控制器(OrbitControls)
  5. 多种灯光
渲染器初始化

这个库和外界需要一个接口,可以通过id选择器拿到dom节点,从而获取到宽高。轨道控制器需要一个2D 渲染器所以一起初始化。

代码语言:javascript
复制
    this.renderer = initRender(this.width, this.height);
    this.renderer2d = initRender2D(this.width, this.height);

WebGLRenderer初始化

代码语言:javascript
复制
export const initRender = (width:number,height:number)=>{
  let renderer = new WebGLRenderer({
    antialias: true,
    alpha: true
  });
  renderer.shadowMap.enabled = false;
  renderer.shadowMap.type = PCFShadowMap;
  renderer.setSize(width,height)

  return renderer
}

CSS2DRenderer初始化

代码语言:javascript
复制
export const initRender2D = (width:number,height:number)=>{
  const renderer2d = new CSS2DRenderer();
  renderer2d.setSize(width,height)
  renderer2d.domElement.style.position = "absolute";
  renderer2d.domElement.style.top = "0px";
  renderer2d.domElement.tabIndex = 0;
  renderer2d.domElement.className = "coreInnerRenderer2d";
  
  return renderer2d
}

相机和场景初始化没有什么特别的就不多说了,见代码。

轨道控制器初始化

注意点是用的2d 渲染器

代码语言:javascript
复制
const orbitControl = new OrbitControls(
      this.camera,
      this.renderer2d.domElement
    );
    orbitControl.minZoom = controlConfig.minZoom;
    orbitControl.maxZoom = controlConfig.maxZoom;
    orbitControl.minPolarAngle = controlConfig.minPolarAngle;
    orbitControl.maxPolarAngle = controlConfig.maxPolarAngle;
    
    orbitControl.update();
灯光添加

灯光有多种,主要是:

  1. 平行光
  2. 点光
  3. 半球光
在这里插入图片描述
在这里插入图片描述

地球的贴图是这种发光材质,需要光照来打效果。

代码语言:javascript
复制
export const initLight = (scene:Scene)=>{
   /**
     * 光源设置
     */
    // 平行光
    var directionalLight = new DirectionalLight(0x80b5ff, 1);
    directionalLight.position.set(-250, 250, 100);
    scene.add(directionalLight);
    
    // 点光
    var pointLight = new PointLight(0x80d4ff, 1);
    pointLight.position.set(-250, 250, 100);
    scene.add(pointLight);
 
    // 半球光
    var hemisphereLight = new HemisphereLight(0xffffff, 0x3d6399, 1);
    hemisphereLight.position.set(-250, 250, 100);
    scene.add(hemisphereLight);
 
    //环境光
    var ambient = new AmbientLight(0x002bff, 0.8);
    scene.add(ambient);
}

星空背景

星空背景主要是点光源,主要思路是随机位置和颜色大小等

效果如下:

在这里插入图片描述
在这里插入图片描述

代码:

代码语言:javascript
复制
export const starBackground = () => {
  const positions = [];
  const colors = [];
  const geometry = new BufferGeometry();
  for (var i = 0; i < 10000; i++) {
    var vertex = new Vector3();
    vertex.x = Math.random() * 2 - 1;
    vertex.y = Math.random() * 2 - 1;
    vertex.z = Math.random() * 2 - 1;
    positions.push(vertex.x, vertex.y, vertex.z);
    var color = new Color();
    color.setHSL(Math.random() * 0.2 + 0.5, 0.55, Math.random() * 0.25 + 0.55);
    colors.push(color.r, color.g, color.b);
  }
  geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
  geometry.setAttribute("color", new Float32BufferAttribute(colors, 3));

  var textureLoader = new TextureLoader();
  var texture = textureLoader.load(starPng); //加载纹理贴图

  var starsMaterial = new PointsMaterial({
    map: texture,
    size: 1,
    transparent: true,
    opacity: 1,
    vertexColors: true, //true:且该几何体的colors属性有值,则该粒子会舍弃第一个属性--color,而应用该几何体的colors属性的颜色
    blending: AdditiveBlending,
    sizeAttenuation: true,
  });
  let stars = new Points(geometry, starsMaterial);
  stars.scale.set(300, 300, 300);
  
  return stars
};

添加3d地球对象

因为地球元素比较多,且需要转动,所以是一个3dobject对象,添加多个mesh。

代码语言:javascript
复制
 var object3D = new Object3D();
 let earthMesh = createEarthImageMesh(earthRadius);

创建一个地球:

代码语言:javascript
复制
export const createEarthImageMesh = (radius: number) => {
  // TextureLoader创建一个纹理加载器对象,可以加载图片作为纹理贴图
  var textureLoader = new TextureLoader();
  //加载纹理贴图
  var texture = textureLoader.load(earthTexture);
  //创建一个球体几何对象
  var geometry = new SphereGeometry(radius, 96, 96);
  //材质对象Material
  // MeshLambertMaterial  MeshBasicMaterial
  var material = new MeshLambertMaterial({
    map: texture, //设置地球0颜色贴图map
  });
  var mesh = new Mesh(geometry, material); //网格模型对象Mesh
  return mesh;
};

现在是这个样子了,光秃秃的缺少了点什么:

在这里插入图片描述
在这里插入图片描述

世界地图轮廓边界绘制

刚才光秃秃的地球,需要加上点轮廓。

threejs 通过 LineLoop 和世界点数据,可以绘制多边形。利用这个原理绘制国家边界。

LineLoop和Line功能一样,区别在于首尾顶点相连,轮廓闭合,但是绘制条数太多会用性能问题,LineSegments 是一条线绘制,提高性能,需要复制顶点。

把计算好的数据,放到data里。

在这里插入图片描述
在这里插入图片描述

然后使用LineSegments绘出来,代码如下:

代码语言:javascript
复制
/*
 * @Author: ZY
 * @Date: 2021-12-31 15:47:59
 * @LastEditors: ZY
 * @LastEditTime: 2022-01-05 10:15:57
 * @FilePath: /3d-earth/lib/src/earth/countryPolygon.ts
 * @Description: 世界轮廓
 */
import {
  BufferGeometry,
  BufferAttribute,
  LineBasicMaterial,
  LineSegments
} from "three";
//引入国家边界数据
import pointArr from "../data/world";
import { countryLineColor } from "../config/index";
// R:球面半径
function countryLine(R:number) {
  var geometry = new BufferGeometry(); //创建一个Buffer类型几何体对象
  //类型数组创建顶点数据
  var vertices = new Float32Array(pointArr);
  // 创建属性缓冲区对象
  var attribute = new BufferAttribute(vertices, 3); //3个为一组,表示一个顶点的xyz坐标
  // 设置几何体attributes属性的位置属性
  geometry.attributes.position = attribute;
  // 线条渲染几何体顶点数据
  var material = new LineBasicMaterial({
    color: countryLineColor, //线条颜色
  }); //材质对象
  var line = new LineSegments(geometry, material); //间隔绘制直线
  line.scale.set(R, R, R); //lineData.js对应球面半径是1,需要缩放R倍
  return line;
}

export { countryLine };

地球光晕

地球光晕其实是一个精灵贴图,这里放了两层,下面放一张看下。

在这里插入图片描述
在这里插入图片描述

大气层光晕代码:

代码语言:javascript
复制
/*
 * @Author: ZY
 * @Date: 2021-12-31 16:40:30
 * @LastEditors: ZY
 * @LastEditTime: 2022-01-05 14:53:18
 * @FilePath: /3d-earth/lib/src/earth/glow.ts
 * @Description: 大气层光环效果
 */

import { TextureLoader, SpriteMaterial,Sprite} from "three";
export const earthGlow =  (radius:number,img:any,scale:number) =>{
  // TextureLoader创建一个纹理加载器对象,可以加载图片作为纹理贴图
  var textureLoader = new TextureLoader();
  var texture = textureLoader.load(img); //加载纹理贴图
  // 创建精灵材质对象SpriteMaterial
  var spriteMaterial = new SpriteMaterial({
    map: texture, //设置精灵纹理贴图
    transparent: true, //开启透明
    // opacity: 0.5,//可以通过透明度整体调节光圈
  });
  // 创建表示地球光圈的精灵模型
  var sprite = new Sprite(spriteMaterial);
  sprite.scale.set(radius * scale, radius * scale, 1); //适当缩放精灵

  return sprite
};

添加地球云层

云层效果不是一个精灵,它是相当于在地球上又套了一个圆球,半径比地球大一点。

云层图:

在这里插入图片描述
在这里插入图片描述

添加之后的效果:

在这里插入图片描述
在这里插入图片描述

还有飞线、动画和涟漪效果本篇内容过长,下篇奉上。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 3D 地球成果展示
    • 前言
      • 为什么不用echarts
        • 实现过程
          • 初始化
          • 星空背景
          • 添加3d地球对象
          • 世界地图轮廓边界绘制
          • 地球光晕
          • 添加地球云层
      相关产品与服务
      图像处理
      图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档