【js&threeJS】入门three,并实现地月绕行,附带全码

效果图:

序幕准备:

需要自己准备地球跟月球的纹理贴图,如下:

图片格式转换网址,如下:

Zamzar - File Conversion progress

开始编码:

实现步骤如下:

  • 在JavaScript部分,首先初始化场景、相机、渲染器、光源以及控制器。

  • 通过调用sphereGeometry函数创建地球和月球的几何体,并应用纹理贴图。

  • 调用createLabel函数创建标签,将标签绑定到地球和月球模型上。

  • 初始化相机位置和渲染器设置。

  • 使用OrbitControls控制器来实现鼠标和触摸事件的交互控制。

    扫描二维码关注公众号,回复: 16330016 查看本文章
  • 实现动画效果的ani函数,其中通过计时器对象clock获取经过的时间,根据时间计算月球绕行轨道的位置,以及地球的自转角度。

  • 使用渲染器对象rendererlabelRenderer进行场景渲染。

  • 使用浏览器提供的requestAnimationFrame函数递归调用ani函数,实现动画效果。

完整代码展示:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body {
            background: url(https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F1%2F590c2682cc04c.jpg%3Fdown&refer=http%3A%2F%2Fpic1.win4000.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641349822&t=06301990af74d7bde9f5675864375620) no-repeat;
            background-size: cover;
            padding: 0;
            margin: 0;
        }

        .label {
            color: #ffffff;
            font-size: 16px;
        }
    </style>
</head>

<body>

</body>

</html>
<script type="importmap">
    {
        "imports": {
            "three": "./three.module.js"
        }
    }
</script>
<script type="module">
    import * as THREE from 'three';
    import { OrbitControls } from './jsm/controls/OrbitControls.js'
    import { CSS2DRenderer, CSS2DObject } from './jsm/renderers/CSS2DRenderer.js'


    var scene, camera, renderer, labelRenderer, controls;
    var earth, moon;
    let oldtime = 0;

    // 用于测量时间
    var clock = new THREE.Clock();

    // 用于加载纹理贴图
    const textureLoader = new THREE.TextureLoader();

    // 分别表示 地球和月球的半径
    const EARCH_RADIUS = 2.6;
    const MOON_RADIUS = 0.3;


    init();
    ani();
    //初始化
    function init() {
        //场景初始化
        scene = new THREE.Scene();
        //光源初始化  聚光灯光源对象
        const light = new THREE.SpotLight(0xffffff);
        //沿着z轴打光
        light.position.set(0, 0, 50);

        scene.add(light);
        //物体初始化
        let earthUrl = './earthUrl.jpg';
        let moonUrl = './seleno.jpg';

        // 应用纹理贴图 
        // sphereGeomery 函数负责创建球体几何体,参数包括纹理贴图的路径、半径、水平和垂直分段数
        earth = sphereGeomery(earthUrl, EARCH_RADIUS, 16, 16);
        moon = sphereGeomery(moonUrl, MOON_RADIUS, 16, 16);

        // 创建标签
        createLabel(earth, '地球', EARCH_RADIUS);
        createLabel(moon, '月亮', MOON_RADIUS);

        // 将地球和月球添加到场景中,使它们可以在渲染中显示
        scene.add(earth);
        scene.add(moon);


        //创建了一个 Three.js 透视相机对象
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 500);
        //设置相机的位置
        camera.position.set(0, 0, 20);

        //创建了一个 Three.js WebGL 渲染器对象
        renderer = new THREE.WebGLRenderer({
            antialias: true,//消除锯齿
            alpha: true //透明
        });
        // 设置渲染器的尺寸
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);
        // 像素比例
        document.body.appendChild(renderer.domElement);

        // 创建了一个用于渲染 CSS2DObject 的渲染器对象,并设置其尺寸与窗口大小一致
        labelRenderer = new CSS2DRenderer();
        labelRenderer.setSize(window.innerWidth, window.innerHeight);

        // 将 CSS2D 渲染器的 DOM 元素添加到 HTML 文档的 <body> 元素中,并将其设置为绝对定位并置于页面顶部
        labelRenderer.domElement.style.position = 'absolute';
        labelRenderer.domElement.style.top = '0px';
        document.body.appendChild(labelRenderer.domElement);

        // camera 是要进行控制的相机对象,document.body 是用于监听鼠标和触摸事件的 DOM 元素。
        controls = new OrbitControls(camera, document.body);
        controls.maxPolarAngle = 0.9 * Math.PI / 2
        controls.enableZoom = true
        controls.enableDamping = true

    }

    //创建物体
    function sphereGeomery(textureUrl, radius, widthSegments, heightSegments) {
        // 创建一个 Three.js 的球体几何体对象
        // 参数包括球体的半径、水平分段数和垂直分段数
        let geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);

        // 创建了一个基于 Phong 光照模型的网格材质对象 MeshPhongMaterial,并为其指定了一个纹理贴图
        let material = new THREE.MeshPhongMaterial({
            // 加载指定 URL 的纹理贴图,并将其赋值给材质对象的 map 属性
            map: textureLoader.load(textureUrl)
        });

        // 创建的几何体和材质创建一个网格对象 Mesh,并将几何体与材质进行组合
        let mesh = new THREE.Mesh(geometry, material);
        return mesh;

    }

    //创建标签
    function createLabel(mesh, text, radius) {
        const div = document.createElement('div');
        div.className = 'label';
        div.textContent = text;
        // 创建的 <div> 元素包装在 CSS2DObject 对象中
        const label = new CSS2DObject(div);
        // 设置标签元素的位置。它将标签元素放置在与球体物体相对应的位置上方,radius + 0.5 表示在球体半径的基础上向上偏移 0.5 的距离
        label.position.set(0, radius + 0.5, 0);
        // 将标签元素添加到传递进来的 mesh 对象中,使标签元素与物体绑定
        mesh.add(label);
    }

    //动画
    function ani() {
        // clock 是 Three.js 提供的一个计时器对象,getElapsedTime 方法可以返回自上次重置以来经过的时间
        const elapsed = clock.getElapsedTime();

        // 设置 moon 对象的位置实现月球的绕行效果。利用正弦函数和余弦函数计算出月球在 x、y 和 z 轴上的坐标,从而使其沿着一个圆形轨道绕着中心点运动
        moon.position.set(Math.sin(elapsed) * 5, 0, Math.cos(elapsed) * 5);

        // 向量 (0, 1, 0) 表示在三维空间中的 Y 轴方向。该向量可以用于进行旋转操作,指定物体绕着 Y 轴旋转
        var axis = new THREE.Vector3(0, 1, 0);
        // rotateOnAxis 方法实现地球的自转效果
        // 接受一个向量和一个旋转角度作为参数,使物体围绕指定轴进行旋转
        // 这里根据经过的时间差计算出旋转角度,并乘以一个系数,控制旋转的速度
        earth.rotateOnAxis(axis, (elapsed - oldtime) * Math.PI / 10);

        // 渲染
        renderer.render(scene, camera);
        labelRenderer.render(scene, camera);

        // 将当前的时间值存储到变量 oldtime 中,以便在下一帧计算地球自转的角度变化
        oldtime = elapsed;

        // 使用浏览器提供的 requestAnimationFrame 函数,将 ani 函数递归调用,以便在下一帧执行动画效果
        requestAnimationFrame(ani);

    }

</script>

注解:

new THREE.SphereGeometry(radius, widthSegments, heightSegments) 水平分段数和垂直分段数

  • 水平分段数(widthSegments)指的是在球体的水平方向上将经度划分成多少段。较高的数值会在水平方向上增加更多的面片,使球体表面更加平滑。这决定了球体经线(经度线)的数量。

  • 垂直分段数(heightSegments)指的是在球体的垂直方向上将纬度划分成多少段。同样地,较高的数值会在垂直方向上增加更多的面片,使球体表面更加平滑。这决定了球体纬线(纬度线)的数量。

new THREE.MeshPhongMaterial()是什么

Phong是一种经典的着色模型,可以模拟光照效果

着色模型在Three.js中的作用是定义物体表面的外观,决定了物体在渲染时呈现出的效果

在Three.js中,有多种着色模型可用于渲染网格模型的表面,一些常见的着色模型:

  • 基础着色模型(Basic Material):最简单的着色模型,不考虑光照和阴影效果,只显示材质的基本颜色。

  • 基础光照着色模型(Lambert Material):使用Lambert光照模型,考虑漫反射光照,适用于呈现无光泽的物体。

  • Phong着色模型(Phong Material):使用Phong光照模型,考虑漫反射、镜面反射和环境光照。可以实现光滑的表面效果。

  • Blinn-Phong着色模型(MeshStandardMaterial):是Phong模型的一种变体,使用Blinn-Phong光照模型,相对于Phong模型计算更加高效。

  • 物理着色模型(Physical Material):基于真实物理原理的光照模型,考虑漫反射、镜面反射、金属度、粗糙度等参数。可以实现高度逼真的外观效果。

猜你喜欢

转载自blog.csdn.net/weixin_52479803/article/details/132211316