zoukankan      html  css  js  c++  java
  • 用Three.js绘制一个3D天体系统

    年前就一直研究了下WebGL相关的东西,看了很多资料和文档,这里做了一些小实践,记录分享一下。

    代码:链接
    预览:链接

    demo:
    在这里插入图片描述

    前置知识

    WebGL和Threejs的关系:

    WebGL是一种 3D 绘图协议,这种绘图技术标准结合了JavaScript和OpenGL ES 2.0,在HTML5的Canvas元素中使用,从而可以在 Web 浏览器中呈现 3D 场景,

    而Threejs是对WebGL的封装,可以让之前很少接触OpenGL的研发人员直接上手3D开发。掌握WebGL有利于理解Threejs的各种api,理解threejs开发的理念。

    上手Threejs之前,最好多看看理解理解WebGL,GLSL,线性代数,一些几何算法。

    具体相关,可以到网上搜索。

    官方文档:

    着手开发

    创建三要素

    threejs三要素:场景,相机,渲染器,这三个对象是threejs一个3d场景必须创建的三要素:

    let scene = new THREE.Scene(); //创建场景
    let camera = new THREE.PerspectiveCamera(
      45,
      window.innerWidth / window.innerHeight,
      1,
      cameraFar
    ); //创建透视相机 (参数分别是   FOV:可视角度,  aspect ratio:宽高比,  near:近剪切面,  far:远剪切面)
    // 渲染器
    let renderer = new THREE.WebGLRenderer({
      canvas
    });
    renderer.render(scene, camera);
    

    创建天体物体

    3d天体主要以太阳系为模型,这里需要创建中间的太阳和八大行星。物体的创建在three里面有很全的几何类,球体,圆环,正方体等等,这些类主要以Mesh为基类,采用三角形网格。这里我们把行星的初始封装成一个方法:

    function initStar(name, speed, angle, color, distance, volume, ringInfo) {
      let mesh = new THREE.Mesh(
        new THREE.SphereGeometry(volume, 16, 16),
        new THREE.MeshLambertMaterial({
          color
        })
      );
      mesh.position.x = distance; // 右手坐标系,x即为在同一个平面上行星距离太阳的距离
    
      // 其他自定义属性
      mesh.receiveShadow = true;
      mesh.castShadow = true;
      mesh.name = name;
      // !行星轨道
      let track = new THREE.Mesh(
        new THREE.RingGeometry(distance - 0.2, distance + 0.2, 64, 1),
        new THREE.MeshBasicMaterial({
          color: 0x888888,
          side: THREE.DoubleSide
        })
      );
    
      track.rotation.x = -Math.PI / 2;
      scene.add(track);
    
      let star = {
        name,
        speed,
        angle,
        distance,
        volume,
        Mesh: mesh
      };
    
      // 有行星环的情况
      if (ringInfo) {
        // console.log("进入了ring,Info为", ringInfo);
        let ring = new THREE.Mesh(
          new THREE.RingGeometry(ringInfo.innerRedius, ringInfo.outerRadius, 32, 6),
          new THREE.MeshBasicMaterial({
            color: ringInfo.color,
            side: THREE.DoubleSide,
            opacity: 0.7,
            transparent: true
          })
        );
    
        ring.name = `Ring of ${name}`;
        ring.rotation.x = -Math.PI / 3;
        ring.rotation.y = -Math.PI / 4;
        scene.add(ring);
    
        star.ring = ring;
      }
    
      scene.add(mesh);
      return star;
    }
    

    name, speed, angle, color, distance, volume, ringInfo的参数意义分别是,行星名字,初始角度,距离太阳的直线距离,行星颜色,行星x轴坐标(离恒星太阳的距离),半径,行星环信息。

    注意three中采用的是右手坐标系
    在这里插入图片描述
    行星和恒星处于同一平面,所以y轴坐标为0,差别是x轴,以太阳为中心当做原点的话,初始化行星的distance参数就是离原点恒星的距离。通过计算三角函数,可以算出坐标系中的xy轴值。

    运动和动画

    运动主要是动态计算设置每个行星的x,y轴。
    在这里插入图片描述
    这里的y轴实际对应是three坐标系中的z轴。天体都在一个平面,天体在three坐标系中的y轴都为0。

    // 行星公转
    function revolution(star) {
      star.angle += star.speed;
      star.angle > Math.PI * star.distance &&
        (star.angle -= Math.PI * star.distance);
      star.Mesh.position.set(
        star.distance * Math.sin(star.angle),
        0,
        star.distance * Math.cos(star.angle)
      );
    }
    
    function move() {
      //太阳自转
      Sun.rotation.y += 0.008; // 旋转网格的x轴
    
      // 行星公转
      stars.map((star) => revolution(star));
    
      control.update(clock.getDelta()); //此处传入的delta是两次animationFrame的间隔时间,用于计算速度
    
      renderer.render(scene, camera);
      requestAnimationFrame(move);
    }
    

    注意threejs里面几乎所有的动画都是用rFA做的,rFA做动画的好处就是能保证整体动画速度不会被“拖慢”,相对的保证动画流畅。这一点其实网上很多博客资料都讲了,但是都没有说清楚是怎么保证动画流畅的,而且这里的流畅是有歧义的,rFA会采用跳过某些帧的方式表现动画,有时候动画表现上会出现“卡顿”,所以这里的流畅是相对结果而言。

    什么意思呢?打个比喻:

    比如说你的游戏逻辑
    你有一个人物在移动,移动速度是每秒60px,也就是每帧1px
    如果你的游戏逻辑执行时间超过了 1/60 秒
    那结果就是,一秒钟过后,人物没有正确的移动 60px
    但如果你用 rAF 保证上一帧逻辑不阻塞下一帧逻辑
    你的运算就不会堵住
    但人物的位置是对的

    再举个例子 手机屏幕 你做一个方块 手指拖动到哪他就移动到哪
    如果运算卡住的话 他会不跟手 你手拖很远了他还在慢慢移动
    但是如果运算不阻塞 即便可能会有点瞬移 但方块一直在你手指下。

    所以rFA保证动画流畅就是这么个意思。

    光源

    做到这,跑来的话你发现是黑乎乎的一片,因为场景里还缺少光源。
    定义光源和环境光。
    光源就是真实的一个光源点,以中间的太阳恒星为光源点,公转的行星背部也有阴影的真实效果,光源点的参数可以定义光颜色,光照强度,以及光照到0强度的距离:

    PointLight( color : Integer, intensity : Float, distance : Number,
    decay : Float ) color - (可选参数)) 十六进制光照颜色。 缺省值 0xffffff (白色)。 intensity

    • (可选参数) 光照强度。 缺省值 1。

    distance - 这个距离表示从光源到光照强度为0的位置。 当设置为0时,光永远不会消失(距离无穷大)。缺省值 0. decay -
    沿着光照距离的衰退量。缺省值 1。 在 physically correct 模式中,decay = 2。

    环境光主要是模拟整体环境的光,这种光每个狭隙都能照射到,理想中的均匀光。配合宇宙背景小点点行星亮光会更真实。

    //环境光
    let ambient = new THREE.AmbientLight(0xffffff, 0.5);
    scene.add(ambient);
    /*太阳光*/
    let sunLight = new THREE.PointLight(0xddddaa, 1.5, 500);
    scene.add(sunLight);
    

    行星运动的轨迹

    为了更好区别每个行星的运动,需要给每个行星公转的轨迹显示出来。
    其实就是在初始化行星的时候,在行星的distance基础上初始化一个圆环物体,设置内环外环半径。

      // !行星轨道
      let track = new THREE.Mesh(
        new THREE.RingGeometry(distance - 0.2, distance + 0.2, 64, 1),
        new THREE.MeshBasicMaterial({
          color: 0x888888,
          side: THREE.DoubleSide
        })
      );
    
      track.rotation.x = -Math.PI / 2;
      scene.add(track);
    

    注意需要旋转默认圆环体是竖着的,需要旋转一下。

    视角控制

    引入第一人称视角控制,视角跟着鼠标和键盘的方向键控制视角和距离。

    
      /*镜头控制*/
      control = new THREE.FirstPersonControls(camera, canvas);
      control.movementSpeed = 100; //镜头移速
      control.lookSpeed = 0.125; //视角改变速度
      control.lookVertical = true; //是否允许视角上下改变
      camera.lookAt(new THREE.Vector3(0, 0, 0));
    

    FirstPersonControls库需要作为文件单独引入,three官方还有其他控制相关的库。
    在这里插入图片描述

    其他一些细节

    还有很多其他一些细节,太阳的外燃烧蒙层,限定视角范围,行星环,鼠标移动到行星显示文字,星星背景等,都可以在源码里看到或者待完善。

    tip:在vscode里没有好用的three的Snippets,可以npm i three,利用npm three包的ts智能提示。three中的loader加载物体的纹理皮肤或者字体,3d模型等在本地会被cors block,需要本地工程化,起个node服务或者webpack server支持。

  • 相关阅读:
    【crontab】误删crontab及其恢复
    New Concept English there (7)
    New Concept English there (6)
    New Concept English there (5)
    New Concept English there (4)
    New Concept English there (3)
    New Concept English there (2)Typing speed exercise
    New Concept English there (1)Typing speed exercise
    New Concept English Two 34 game over
    New Concept English Two 33 94
  • 原文地址:https://www.cnblogs.com/zhangmingzhao/p/12430135.html
Copyright © 2011-2022 走看看