Threejs实现3d地球记录(4)

这篇具有很好参考价值的文章主要介绍了Threejs实现3d地球记录(4)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

三、地球信息流可视化(飞线)

1、曲线介绍

Three.js基础曲线函数有三种:

  • 样条曲线:在三维空间中设置5个顶点,输入三维样条曲线CatmullRomCurve3函数作为参数,然后返回更多个顶点,通过返回的顶点数据,构建一个几何体,然后绘制出来一条沿着5个顶点的光滑样条曲线。

  • 三维三次贝赛尔曲线: 由起点、终点、及两个控制点定义,通过三维三次贝塞尔曲线(CubicBezierCurve3)绘制出一条平滑的曲线
    Threejs实现3d地球记录(4)

  • 圆弧曲线:类似于画一个圆,取其中一段作为弧线;将圆心坐标,圆半径,圆弧起始角度作为ArcCurve参数,绘制一段圆弧。
    Threejs实现3d地球记录(4)

2、使用圆弧曲线绘制轨迹线

(1)、xoy平面上关于y轴对称的圆弧曲线

Threejs实现3d地球记录(4)

  • 根据起始点确定两点间的顶点坐标(上图青色的点)
    为了美观我们设定距离越远的两点,中间顶点距离球面也越远,简单来说就是顶点到球面的距离与两点之间和球心构成夹角成正相关,夹角越大距离越远,相反则越近。因此我们需要先计算出两点之间和球心构成夹角的弧度值:
//计算两点之间和球心构成夹角的弧度值方法
function radianAOB(A, B, O) {
  // dir1、dir2:球面上两个点和球心构成的方向向量
  var dir1 = A.clone().sub(O).normalize();
  var dir2 = B.clone().sub(O).normalize();
  //.dot()计算夹角余弦值
  var cosAngle = dir1.clone().dot(dir2);
  var radianAngle = Math.acos(cosAngle);//余弦值转夹角弧度值
  return radianAngle
}

首先算出两点的中点,然后根据中点确定出顶点与球心构成的方向向量,最后由顶点到球面的距离与两点之间和球心构成夹角成正相关关系式计算出顶点坐标:

// 计算两点的中点
  var middleV3 = new THREE.Vector3().addVectors(startPoint, endPoint).multiplyScalar(0.5);
  // 顶点与球心构成的方向向量
  var dir = middleV3.clone().normalize()
  // 计算夹角的弧度值
  var earthRadianAngle = radianAOB(startPoint, endPoint, new THREE.Vector3(0, 0, 0))
  //弧度值 * R * 0.2:表示飞线轨迹圆弧顶部距离地球球面的距离,与弧度值成正相关即可,可自行调整
  var arcTopCoord = dir.multiplyScalar(R + earthRadianAngle * R * 0.2)
  • 通过三个点确定圆弧线
    绘制圆弧曲线要使用到ArcCurve函数,那就得先计算出圆心坐标,圆半径以及圆弧的起始角度
    1)、计算圆心坐标:
//求p1, p2, p3三个点的外接圆圆心
function threePointCenter(p1, p2, p3) {
  var L1 = p1.lengthSq();//到坐标原点距离的平方
  var L2 = p2.lengthSq();
  var L3 = p3.lengthSq();
  var x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y, x3 = p3.x, y3 = p3.y;
  var S = x1 * y2 + x2 * y3 + x3 * y1 - x1 * y3 - x2 * y1 - x3 * y2;
  var x = (L2 * y3 + L1 * y2 + L3 * y1 - L2 * y1 - L3 * y2 - L1 * y3) / S / 2;
  var y = (L3 * x2 + L2 * x1 + L1 * x3 - L1 * x2 - L2 * x3 - L3 * x1) / S / 2;
  // 三点外接圆圆心坐标
  var center = new THREE.Vector3(x, y, 0);
  return center
}

2)、计算圆半径(通过顶点y坐标减去圆心y坐标获得)

var flyArcR = Math.abs(flyArcCenter.y - arcTopCoord.y);

3)、计算圆弧的起始角度(可使用到上边计算两点之间和球心构成夹角的弧度值方法)

  var flyRadianAngle = radianAOB(startPoint, new THREE.Vector3(0, -1, 0), flyArcCenter);
  var startAngle = -Math.PI / 2 + flyRadianAngle;//飞线圆弧开始角度
  var endAngle = Math.PI - startAngle;//飞线圆弧结束角度

4)、通过ArcCurve函数计算出圆弧曲线,使用getSpacedPoints(x)方法返回生成圆弧线的点坐标,返回多少个点由x决定,x越大圆弧越圆,但渲染性能越低,最后用setFromPoints渲染几何体顶点坐标以Line绘制出来

function circleLine(x, y, r, startAngle, endAngle) {
  var geometry = new THREE.BufferGeometry();
  // THREE.ArcCurve创建圆弧曲线
  var arc = new THREE.ArcCurve(x, y, r, startAngle, endAngle, false);
  //getSpacedPoints是基类Curve的方法,返回一个vector2对象作为元素组成的数组
  var points = arc.getSpacedPoints(50); //分段数50,返回51个顶点
  geometry.setFromPoints(points);// setFromPoints方法从points中提取数据改变几何体的顶点属性vertices
  var material = new THREE.LineBasicMaterial({ color: 0xffffff, });//线条材质
  var line = new THREE.Line(geometry, material);//线条模型对象
  return line;
}

绘制圆弧曲线完整方法:

function arcXOY(startPoint, endPoint) {
  var middleV3 = new THREE.Vector3().addVectors(startPoint, endPoint).multiplyScalar(0.5);
  var dir = middleV3.clone().normalize()
  var earthRadianAngle = radianAOB(startPoint, endPoint, new THREE.Vector3(0, 0, 0))
  var arcTopCoord = dir.multiplyScalar(R + earthRadianAngle * R * 0.2)

  var flyArcCenter = threePointCenter(startPoint, endPoint, arcTopCoord)
  var flyArcR = Math.abs(flyArcCenter.y - arcTopCoord.y);

  var flyRadianAngle = radianAOB(startPoint, new THREE.Vector3(0, -1, 0), flyArcCenter);
  var startAngle = -Math.PI / 2 + flyRadianAngle;
  var endAngle = Math.PI - startAngle;

  var arcline = circleLine(flyArcCenter.x, flyArcCenter.y, flyArcR, startAngle, endAngle)
  arcline.center = flyArcCenter;//飞线圆弧自定一个属性表示飞线圆弧的圆心
  arcline.topCoord = arcTopCoord;//飞线圆弧自定一个属性表示飞线圆弧中间也就是顶部坐标
  return arcline
}
  • 场景中随机点绘制圆弧曲线
	var num = 10//批量绘制多组
    for (var i = 1; i < num; i++) {
      var startAngle = Math.PI / 2 / num * i;//圆弧起点和坐标原点构成的角度
      var x = R * Math.cos(startAngle); //飞线圆弧起点横坐标
      var y = R * Math.sin(startAngle); //飞线圆弧起点纵坐标
      
      var startPoint = new THREE.Vector3(x, y, 0)
      var endPoint = new THREE.Vector3(-x, y, 0)

      var arcline = arcXOY(startPoint, endPoint);
      scene.add(arcline); //飞线插入场景中

效果:
Threejs实现3d地球记录(4)

(2)、解析经纬度坐标绘制轨迹线

  • 经纬度坐标转球面坐标
  var sphereCoord1 = lon2xyz(R, lon1, lat1);//经纬度坐标转球面坐标
  //轨迹线起点球面坐标
  var startSphereCoord = new THREE.Vector3(sphereCoord1.x, sphereCoord1.y, sphereCoord1.z);
  var sphereCoord2 = lon2xyz(R, lon2, lat2);
  //轨迹线结束点球面坐标
  var endSphereCoord = new THREE.Vector3(sphereCoord2.x, sphereCoord2.y, sphereCoord2.z);
  • 把3D球面上任意的两个飞线起点和结束点绕球心旋转到到XOY平面上,
    同时保持关于y轴对称
    上面我们知道如何在xoy平面上绘制关于y轴对称的圆弧曲线,在已知初始点和结束点的情况下,我们可以将这两点通过两次旋转得到在xoy平面上且关于y轴对称的两点,然后绘制圆弧曲线,最后再逆旋转回初始位置,此处我们需要用到‘四元数Quaternion’的概念,我将它简单理解为记录旋转的值。
    1)、计算第一次旋转的四元数(旋转至xoy平面)
    第一次旋转可看作两点初始位置与球心构成平面的法向量旋转至xoy平面的法向量
  var origin = new THREE.Vector3(0, 0, 0);//球心坐标
  var startDir = startSphere.clone().sub(origin);//飞线起点与球心构成方向向量
  var endDir = endSphere.clone().sub(origin);//飞线结束点与球心构成方向向量
  // 两点初始位置与圆心构成平面的法向量
  var normal = startDir.clone().cross(endDir).normalize();
  var xoyNormal = new THREE.Vector3(0, 0, 1);//XOY平面的法线
  //计算旋转到xoy平面的四元数
  var quaternion3D_XOY = new THREE.Quaternion().setFromUnitVectors(normal, xoyNormal);
  //第一次旋转后的起点与终点
  var startSphereXOY = startSphere.clone().applyQuaternion(quaternion3D_XOY);
  var endSphereXOY = endSphere.clone().applyQuaternion(quaternion3D_XOY);

2)、计算第二次旋转的四元数(将旋转至xoy平面的两点再旋转至两点关于y轴对称)
第二次旋转可看作在xoy平面两点的中点与球心构成的方向向量旋转至y轴方向向量

  //第一次旋转后的两点中点
  var middleV3 = startSphereXOY.clone().add(endSphereXOY).multiplyScalar(0.5);
  var midDir = middleV3.clone().sub(origin).normalize();// 第一次旋转后的两点中点和球心构成的方向向量
  var yDir = new THREE.Vector3(0, 1, 0);//y轴方向向量
  //第二次旋转的四元数
  var quaternionXOY_Y = new THREE.Quaternion().setFromUnitVectors(midDir, yDir);

  //第二次旋转后的起点与终点
  var startSpherXOY_Y = startSphereXOY.clone().applyQuaternion(quaternionXOY_Y);
  var endSphereXOY_Y = endSphereXOY.clone().applyQuaternion(quaternionXOY_Y);

3)、计算逆旋转的四元数
一个四元数表示一个旋转过程,.invert()方法表示四元数的逆,简单说就是把旋转过程倒过来,两次旋转的四元数执行.invert()求逆,然后执行.multiply()相乘

var quaternionInverse = quaternion3D_XOY.clone().invert().multiply(quaternionXOY_Y.clone().invert())

完整方法为:

function _3Dto2D(startSphere, endSphere) {
  //第一次旋转
  var origin = new THREE.Vector3(0, 0, 0);
  var startDir = startSphere.clone().sub(origin);
  var endDir = endSphere.clone().sub(origin);
  var normal = startDir.clone().cross(endDir).normalize();
  var xoyNormal = new THREE.Vector3(0, 0, 1);//XOY平面的法线
  var quaternion3D_XOY = new THREE.Quaternion().setFromUnitVectors(normal, xoyNormal);
  var startSphereXOY = startSphere.clone().applyQuaternion(quaternion3D_XOY);
  var endSphereXOY = endSphere.clone().applyQuaternion(quaternion3D_XOY);
  //第二次旋转
  var middleV3 = startSphereXOY.clone().add(endSphereXOY).multiplyScalar(0.5);
  var midDir = middleV3.clone().sub(origin).normalize();
  var yDir = new THREE.Vector3(0, 1, 0);
  var quaternionXOY_Y = new THREE.Quaternion().setFromUnitVectors(midDir, yDir);

  var startSpherXOY_Y = startSphereXOY.clone().applyQuaternion(quaternionXOY_Y);
  var endSphereXOY_Y = endSphereXOY.clone().applyQuaternion(quaternionXOY_Y);

  var quaternionInverse = quaternion3D_XOY.clone().invert().multiply(quaternionXOY_Y.clone().invert())
  return {
    // 返回两次旋转四元数的逆四元数
    quaternion: quaternionInverse,
    // 范围两次旋转后在XOY平面上关于y轴对称的圆弧起点和结束点坐标
    startPoint: startSpherXOY_Y,
    endPoint: endSphereXOY_Y,
  }
}
  • 调用arcXOY函数绘制圆弧轨迹
    计算得到绘制圆弧需要的关于y轴对称的起点、结束点和旋转四元数,通过arcXOY函数绘制出xoy平面关于y轴对称的圆弧轨迹,将得到的轨迹线乘以逆旋转的四元数,旋转回初始位置
  var startEndQua = _3Dto2D(startSphereCoord, endSphereCoord)
  // 调用arcXOY函数绘制圆弧轨迹
  var arcline = arcXOY(startEndQua.startPoint, startEndQua.endPoint);
  arcline.quaternion.multiply(startEndQua.quaternion)

完整方法:

function flyArc(lon1, lat1, lon2, lat2) {
  var sphereCoord1 = lon2xyz(R, lon1, lat1);
  var startSphereCoord = new THREE.Vector3(sphereCoord1.x, sphereCoord1.y, sphereCoord1.z);
  var sphereCoord2 = lon2xyz(R, lon2, lat2);
  var endSphereCoord = new THREE.Vector3(sphereCoord2.x, sphereCoord2.y, sphereCoord2.z);

  var startEndQua = _3Dto2D(startSphereCoord, endSphereCoord)
  var arcline = arcXOY(startEndQua.startPoint, startEndQua.endPoint);
  arcline.quaternion.multiply(startEndQua.quaternion)
  return arcline;
}
  • 最后调用绘制方法绘制出相关曲线
var flyArcGroup = new THREE.Group();
Object.keys(data).map(item => { //data为起始国家经纬度坐标
  data[item].end.forEach((coord) => {
    var arcline = flyArc(data[item].start.E, data[item].start.N, coord.E, coord.N)
    flyArcGroup.add(arcline); 
  });
})

效果:
Threejs实现3d地球记录(4)

3、使用圆弧绘制飞线

  • 绘制一段飞线,圆心做坐标原点
    使用顶点颜色渲染vertexColors实现飞线颜色渐变
  function createFlyLine(r, startAngle, endAngle) {
  var geometry = new THREE.BufferGeometry(); 
  var arc = new THREE.ArcCurve(0, 0, r, startAngle, endAngle, false);
  var pointsArr = arc.getSpacedPoints(50); 
  geometry.setFromPoints(pointsArr);

  // 批量计算所有顶点颜色数据
  var colorArr = [];
  for (var i = 0; i < pointsArr.length; i++) {
    var color1 = new THREE.Color(0xffffff); //白色
    var color2 = new THREE.Color(0xffff00); //黄色
    var color = color1.lerp(color2, i / pointsArr.length) //颜色的渐变
    colorArr.push(color.r, color.g, color.b);
  }
  // 设置几何体顶点颜色数据
  geometry.attributes.color = new THREE.BufferAttribute(new Float32Array(colorArr), 3);
  // 点模型渲染几何体每个顶点
  var material = new THREE.PointsMaterial({
    size: 3.0, 
    vertexColors: THREE.VertexColors, 
  });
  var FlyLine = new THREE.Points(geometry, material);
  return FlyLine;
}
  • 将生成的飞线作为原轨迹线的子元素,此时飞线将继承原轨迹线的旋转等变换
    生成的飞线是以坐标原点为圆心的,为了使之与原轨迹线重合,需将y坐标平移到xoy平面上关于y轴对称的圆弧线y坐标上
function arcXOY(startPoint, endPoint) {
  ...
  var flyAngle = (endAngle - startAngle) / 7; //设置飞线圆弧的弧度和轨迹线弧度相关
  var flyLine = createFlyLine(flyArcR, startAngle, startAngle + flyAngle);
  flyLine.position.y = flyArcCenter.y;//平移飞线圆弧和飞线轨迹圆弧重合
  //飞线段flyLine作为飞线轨迹arcLine子对象,继承飞线轨迹平移旋转等变换
  arcline.add(flyLine);
  //飞线段运动范围startAngle~flyEndAngle
  flyLine.flyEndAngle = endAngle - startAngle - flyAngle;
  flyLine.startAngle = startAngle;
  // 飞线段当前角度位置,设置随机值让飞线不是同步运动
  flyLine.AngleZ = arcline.flyEndAngle * Math.random();
  arcline.flyLine = flyLine;
  return arcline
}
  • 要使飞线运动起来需在循环渲染函数中添加操作
    飞线动画可通过绕z轴旋转实现,旋转到终点后再从头开始旋转
	const animate = function () {
      // 批量设置所有飞线的运动动画
      flyArr.forEach((fly) => {
        fly.rotation.z += 0.02; //调节飞线速度
        if (fly.rotation.z >= fly.flyEndAngle) fly.rotation.z = fly.startAngle;
      });
      ...
    };

效果:
Threejs实现3d地球记录(4)

4、实现小蝌蚪状飞线

小蝌蚪状飞线是指飞线段从头到尾点大小逐渐变小,要实现这种效果需要修改顶点着色器的源码,
顶点着色器源码可在three/src/renserers/shaders/ShaderLib/points.glsl.js中查看。

  • 通过设置attributes.percent 用于控制点的渲染大小
  var percentArr = []; //attributes.percent的数据
  for (var i = 0; i < pointsArr.length; i++) { //pointsArr飞线点集合
    percentArr.push(i / pointsArr.length);
  }
  var percentAttribue = new THREE.BufferAttribute(new Float32Array(percentArr), 1);
  // 通过顶点数据percent点模型从大到小变化,产生小蝌蚪形状飞线
  geometry.attributes.percent = percentAttribue;
  • 修改顶点着色器源码(注意:不同版本细节可能会稍微会有区别,不过整体思路是一样的)
  material.onBeforeCompile = function (shader) {
    // 顶点着色器中声明一个attribute变量:百分比
    shader.vertexShader = shader.vertexShader.replace(
      'void main() {',
      [
        'attribute float percent;', //顶点大小百分比变量,控制点渲染大小
        'void main() {',
      ].join('\n') // .join()把数组元素合成字符串
    );
    /*此处相当于将'void main() {'替换为: 
    attribute float percent;'
        void main() {*/
    // 调整点渲染大小计算方式
    shader.vertexShader = shader.vertexShader.replace(
      'gl_PointSize = size;',
      [
        'gl_PointSize = percent * size;',
      ].join('\n')
    );
  };

完整代码为:

function createFlyLine(r, startAngle, endAngle) {
  var geometry = new THREE.BufferGeometry(); 
  var arc = new THREE.ArcCurve(0, 0, r, startAngle, endAngle, false);
  var pointsArr = arc.getSpacedPoints(50);
  geometry.setFromPoints(pointsArr);
  var percentArr = []; 
  for (var i = 0; i < pointsArr.length; i++) {
    percentArr.push(i / pointsArr.length);
  }
  var percentAttribue = new THREE.BufferAttribute(new Float32Array(percentArr), 1);
  geometry.attributes.percent = percentAttribue;
  
  var colorArr = [];
  for (var i = 0; i < pointsArr.length; i++) {
    var color1 = new THREE.Color(0xffffff);
    var color2 = new THREE.Color(0xffff00); 
    var color = color1.lerp(color2, i / pointsArr.length)
    colorArr.push(color.r, color.g, color.b);
  }

  geometry.attributes.color = new THREE.BufferAttribute(new Float32Array(colorArr), 3);

  var material = new THREE.PointsMaterial({
    size: 3.0, 
    vertexColors: THREE.VertexColors,
  });

  material.onBeforeCompile = function (shader) {
    shader.vertexShader = shader.vertexShader.replace(
      'void main() {',
      [
        'attribute float percent;', 
        'void main() {',
      ].join('\n') 
    );
    
    shader.vertexShader = shader.vertexShader.replace(
      'gl_PointSize = size;',
      [
        'gl_PointSize = percent * size;',
      ].join('\n') 
    );
  };
  var FlyLine = new THREE.Points(geometry, material);
  return FlyLine;
}

效果:
Threejs实现3d地球记录(4)文章来源地址https://www.toymoban.com/news/detail-432810.html

到了这里,关于Threejs实现3d地球记录(4)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 液体神经网络LLN:通过动态信息流彻底改变人工智能

    巴乌米克·泰吉

    2024年02月11日
    浏览(42)
  • 【论文阅读】HOLMES:通过关联可疑信息流进行实时 APT 检测(S&P-2019)

    HOLMES: Real-time APT Detection through Correlation of Suspicious Information Flows SP-2019 伊利诺伊大学芝加哥分校、密歇根大学迪尔伯恩分校、石溪大学 Milajerdi S M, Gjomemo R, Eshete B, et al. Holmes: real-time apt detection through correlation of suspicious information flows[C]//2019 IEEE Symposium on Security and Privacy (SP). IE

    2024年02月11日
    浏览(36)
  • APP高曝光率到智能化投放,SDK猫眼信息流广告的投放策略!

    ​随着移动设备和社交媒体的普及,越来越多的人开始通过信息流获取新闻、娱乐和其他内容。信息流广告是一种以自然的方式呈现在用户浏览的内容中,以吸引用户点击的广告形式。 除此之外,各种类型的媒体也开始将其自身的交互特点与信息流广告相结合,从而丰富了这

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

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

    2024年02月08日
    浏览(62)
  • threeJs实现3D地球-旋转-自定义贴图-透明发光

    //---html (angular)---         //---ts--- 效果图:

    2024年04月17日
    浏览(38)
  • 前端前沿web 3d可视化技术 ThreeJS学习全记录

    完整案例与项目代码: gitee开源项目地址 https://gitee.com/jumping-world-line/01_threeJS_basic 随着浏览器性能和网络带宽的提升 使得3D技术不再是桌面的专利 打破传统平面展示模式 前端方向主要流向的3D图形库包括Three.js和WebGL WebGL灵活高性能,但代码量大,难度大,需要掌握很多底层

    2024年02月01日
    浏览(58)
  • 基于ThreeJS的3D地球

    第一次接触threeJS,说实话,挺脑瓜子疼的! 功能:3D地球(纹理贴图),地球上添加标记点(经纬度),点击标记点弹出对应的信息框,地球入场动画,相机移动动画等。 先开效果图吧 一:添加必要的依赖 二:组件代码 三:父组件中的代码 四:项目gitee地址 mythree: 基于

    2024年02月12日
    浏览(36)
  • Three.js - 实现一个3D地球可视化

    3D地球可视化效果 3D地球的开发并不复杂,对球形物体进行贴图操作,完成球体自转和月球公转,太阳场景设置等即可 上代码 更多详细代码请关注公众号索取(备注:公众号):

    2024年02月04日
    浏览(57)
  • 使用 Threejs 从基础开始构建 3D 地球

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

    2024年01月25日
    浏览(59)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包