zoukankan      html  css  js  c++  java
  • three.js 制作太阳系统

    最近学了three.js,想拿来练练手,喜欢宇宙,于是亲手撸代码来完成这个,为了更真实,于是查了一些相关资料。
    1. 距离太阳由近及远分别是【水星,金星,地球,火星,木星,土星,天王星,海王星】
    2. 他们分别到太阳的距离为5791, 10820, 14960, 22794, 77833, 142940, 287099, 450400(单位万千米)
    3. 他们的半径分别为2440, 6052, 6371, 3397, 71492, 60268, 25559, 24766(千米)
    4. 他们的公转周期分别为88, 225, 365, 687, 4329, 10767, 30769, 60152(天)
    5. 他们的自转周期分别为58,243,1, 1, 0.41, 0.42, 0.64, 0.65,
    好了除了八大行星都已经调查好了,接下来八大行星都将按照真实比例绘制,(由于周期相差过大,遂正比于1/2次方)

    1.绘制八大行星

    绘制行星很容易,直接看代码

    shuixing = renderMesh("shuixing", 0);
    jinxing = renderMesh("jinxing", 1);
    diqiu = renderMesh("diqiu", 2);
    huoxing = renderMesh("huoxing", 3);
    muxing = renderMesh("muxing", 4);
    tuxing = renderMesh("tuxing", 5);
    tianwangxing = renderMesh("tianwangxing", 6);
    haiwangxing = renderMesh("haiwangxing", 7);
    scene.add(shuixing);
    scene.add(jinxing);
    scene.add(diqiu);
    scene.add(huoxing);
    scene.add(muxing);
    scene.add(tuxing);
    scene.add(tianwangxing);
    scene.add(haiwangxing);
    function renderMesh(name, num) {
        let texture = new THREE.TextureLoader().load("http://www.bettersmile.cn:90/three_code/assets/textures/planets/"+name+".jpg");
        let geometry = new THREE.SphereGeometry(radiuses[num] * RADIUS, 50, 50);
        let material = new THREE.MeshBasicMaterial({map: texture});
        let mesh = new THREE.Mesh(geometry, material);
        mesh.position.x = distances[num] * DISTANCE + SUNRADIUS * RADIUS;
        return mesh;
    }

    在绘制的同时,我们还固定了他们的位置哦,

    2.绘制小行星带

    八大行星都绘制好了,怎么能少了小行星带呢?由于小行星带都是不规则的石头,这里我们使用上一节的ConvexGeometry()渲染。

    let asteroidTexture = new THREE.TextureLoader().load("http://www.bettersmile.cn:90/three_code/assets/textures/planets/asteroid.jpg");
    asteriods = renderAsteriod(asteroidTexture);
    scene.add(asteriods);
    function renderAsteriod(texture) {
        let asteroid = new THREE.Geometry();
        let asteroidMate = new THREE.MeshBasicMaterial();
        for(var i=0; i<20000; i++) {
            let points = [];
            let rad = Math.pow(Math.random(), 3) * ASTERIODRADIUS;
            for(var j=0; j<30; j++) {
                points.push(new THREE.Vector3(Math.random() * RADIUS * rad, Math.random() * RADIUS * rad, Math.random() * RADIUS * rad))
            }
            var asteroidGeom = new THREE.ConvexGeometry(points);
    
            var asterMesh = new THREE.Mesh(asteroidGeom, asteroidMate);
            //2.17-3.64 * 14960  = 32450 - 54450
            let dis = (Math.random() * 22000 + 32450) * DISTANCE + SUNRADIUS * RADIUS;
            let angle = Math.random() * Math.PI * 2;
            asterMesh.position.x = dis * Math.sin(angle);
            asterMesh.position.y = Math.random() * ASTERIODRADIUS * RADIUS * 4 - ASTERIODRADIUS * RADIUS * 2;
            asterMesh.position.z = dis * Math.cos(angle);
    
            asterMesh.updateMatrix();
            asteroid.merge(asterMesh.geometry, asterMesh.matrix);
        }
        let mesh = new THREE.Mesh(asteroid, new THREE.MeshLambertMaterial({map: texture, emissive: 0x484137}));
        return mesh;
    }

    我们渲染了20000个小行星,这里小行星带距离太阳2.17~3.64个天文单位,一天文单位为14960万千米。

    3.加下来我们绘制太阳,月球,土星环

    绘制太阳,月球和土星环(土星环P了好久),相对容易,太阳设置在原点,月球围绕地球,土星环围绕土星,

    let taiyangTexture = new THREE.TextureLoader().load("http://www.bettersmile.cn:90/three_code/assets/textures/planets/sun.jpg");
    let yueqiuTexture = new THREE.TextureLoader().load("http://www.bettersmile.cn:90/three_code/assets/textures/planets/yueqiu.jpg");
    let tuxinghuanTexture = new THREE.TextureLoader().load("http://www.bettersmile.cn:90/three_code/assets/textures/planets/tuxinghuan.png");
    let taiyangGeom = new THREE.SphereGeometry(SUNRADIUS * RADIUS, 50, 50);
    let taiyangMate = new THREE.MeshBasicMaterial({map: taiyangTexture});
    let yueqiuGeom = new THREE.SphereGeometry(MOONRADIUS * RADIUS, 50, 50);
    let yueqiuMate = new THREE.MeshBasicMaterial({map: yueqiuTexture});
    tuxinghuan = renderTuxinghuan(tuxinghuanTexture);
    tuxinghuan.position.copy(tuxing.position);
    scene.add(tuxinghuan);
    taiyang = new THREE.Mesh(taiyangGeom, taiyangMate);
    yueqiu = new THREE.Mesh(yueqiuGeom, yueqiuMate);
    yueqiu.position.set((distances[2] * DISTANCE + SUNRADIUS * RADIUS) + MOONDISTANCE * DISTANCE, 0, 0);
    scene.add(taiyang);
    scene.add(yueqiu);
    function renderTuxinghuan(texture) {
        let geom = new THREE.RingGeometry(16, 24, 50, 10, 0, Math.PI * 2);
        let mate = new THREE.MeshLambertMaterial({emissive: 0xaa8766, transparent:false, opacity: 0.8, map: texture, side: THREE.DoubleSide});
        let ring = new THREE.Mesh(geom, mate);
        ring.rotation.x = - Math.PI / 2;
        ring.rotation.y = - Math.PI / 12;
        return ring;
    }
    function initCanvas() {
        let canvas = document.createElement('canvas');
        canvas.width = 16;
        canvas.height = 16;
        let context = canvas.getContext('2d');
        let gradient = context.createRadialGradient(canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, canvas.width / 2);
        gradient.addColorStop(0, 'rgba(244,164,33,1)');
        gradient.addColorStop(0.2, 'rgba(244,164,33,1)');
        gradient.addColorStop(0.4, 'rgba(244,164,33,.6)');
        gradient.addColorStop(1, 'rgba(0,0,0,0)');
        context.fillStyle = gradient;
        context.fillRect(0, 0, canvas.width, canvas.height);
        return canvas;
    }
    
    function initSunshine() {
        let centerBallLite = new THREE.Sprite(new THREE.SpriteMaterial({
            map: new THREE.CanvasTexture(initCanvas()),
            blending: THREE.AdditiveBlending
        }));
        centerBallLite.scale.x = centerBallLite.scale.y = centerBallLite.scale.z = 250000 * RADIUS;
        scene.add(centerBallLite);
    }

    这里我们的土星环使用了RingGeometry()几何体,太阳的光晕使用CanvasTexture()

    4.让星球动起来

    让所有星星动起来,使用自转和公转,这里需要一点三角函数的功底,

    function allStarsSelfRotate() {
        taiyang.rotation.y = 1 / Math.sqrt(25) * step1 * SELECYCLE;
        yueqiu.rotation.y = 1 / Math.sqrt(30) * step1 * SELECYCLE;
        shuixing.rotation.y = 1 / Math.sqrt(58.65) * step1 * SELECYCLE;
        jinxing.rotation.y = - 1 / Math.sqrt(243) * step1 * SELECYCLE;
        diqiu.rotation.y = step1 * SELECYCLE;
        huoxing.rotation.y = step1 * SELECYCLE;
        muxing.rotation.y = 1 / Math.sqrt(0.41) * step1 * SELECYCLE;
        tuxing.rotation.y = 1 / Math.sqrt(0.42) * step1 * SELECYCLE;
        tianwangxing.rotation.z = 1 / Math.sqrt(0.64) * step1 * SELECYCLE;
        haiwangxing.rotation.y = 1 / Math.sqrt(0.65) * step1 * SELECYCLE;
        asteriods.rotation.y = 1 / 50 * step1 * PUBLICCYCLE;
        step1 ++;
    }
    
    function allStarsPublicRotate() {
        shuixing.position.set(rotating(0).x,0,rotating(0).z);
        jinxing.position.set(rotating(1).x,0,rotating(1).z);
        diqiu.position.set(rotating(2).x,0,rotating(2).z);
        huoxing.position.set(rotating(3).x,0,rotating(3).z);
        muxing.position.set(rotating(4).x,0,rotating(4).z);
        tuxing.position.set(rotating(5).x,0,rotating(5).z);
        tianwangxing.position.set(rotating(6).x,0,rotating(6).z);
        haiwangxing.position.set(rotating(7).x,0,rotating(7).z);
        tuxinghuan.position.copy(tuxing.position);
        yueqiu.position.set(
            (distances[2] * DISTANCE + SUNRADIUS * RADIUS) * Math.cos(step2 / Math.sqrt(durations[2]) * PUBLICCYCLE) +
            (MOONDISTANCE * DISTANCE) * Math.sin(step2 / Math.sqrt(10) * PUBLICCYCLE),
            0,
            - (distances[2] * DISTANCE + SUNRADIUS * RADIUS) * Math.sin(step2 / Math.sqrt(durations[2]) * PUBLICCYCLE) +
            (MOONDISTANCE * DISTANCE) * Math.cos(step2 / Math.sqrt(10) * PUBLICCYCLE)
        );
        step2 ++;
    }
    function rotating(num) {
        let x = (distances[num] * DISTANCE + SUNRADIUS * RADIUS) * Math.cos(step2 / Math.sqrt(durations[num]) * PUBLICCYCLE);
        let z = - (distances[num] * DISTANCE + SUNRADIUS * RADIUS) * Math.sin(step2 / Math.sqrt(durations[num]) * PUBLICCYCLE);
        return {x, z};
    }

    全是简单的三角函数,呵呵

    5.加上星空

    星空我们直接使用PointsMaterial(),我们随机3000个点(点太多会很乱)。

    function initSprites() {
        let geom = new THREE.Geometry();
        let mate = new THREE.PointsMaterial({
            size: 0.2,
            vertexColors: true,
            color: 0xffffff,
            transparent: true,
            opacity: 0.6
        })
        for(var i=0; i<3000; i++) {
            let x = Math.random() * 10000 - 5000;
            let y = Math.random() * 10000 - 5000;
            let z = Math.random() * 10000 - 5000;
            if(x*x+y*y+z*z > 250000) {
                var vector = new THREE.Vector3(x,y,z);
                geom.vertices.push(vector);
                geom.colors.push(new THREE.Color(0xffffff));
            }
        }
        cloud = new THREE.Points(geom, mate);
        scene.add(cloud);
    }

    x*x+y*y+z*z > 250000,为了让他们处在很远的地方,我们过滤掉离中心小于500的星星,所以剩下的应该不多。

    6.加上tween动画和飞行相机

    动画我们是先给每个星球一个镜头(毕竟球球平等,但是给了地球最好的贴图),然后俯瞰整个太阳系

    function initTween() {
        let pos = {x: 41, y: 17, z: -35, lx: 0, ly: 0, lz: 0};
        let tween1 = new TWEEN.Tween(pos).to({x: 40.36, y: 0.29, z: 2.642, lx: 40.54, ly: 0, lz: 0}, 20 * ANIMATIONTIME);
        tween1.easing(TWEEN.Easing.Linear.None);
        let tween2 = new TWEEN.Tween(pos).to({x: 67.41, y: 0.43, z: -4, lx: 65.7, ly: 0, lz: 0}, 22 * ANIMATIONTIME);
        tween2.easing(TWEEN.Easing.Linear.None);
        let tween3 = new TWEEN.Tween(pos).to({x: 84.6, y: 0.91, z: -3.42, lx: 86.4, ly: 0, lz: 0}, 24 * ANIMATIONTIME);
        tween3.easing(TWEEN.Easing.Linear.None);
        let tween4 = new TWEEN.Tween(pos).to({x: 127.66, y: 0.26, z: -1.47, lx: 125.56, ly: 0, lz: 0}, 27 * ANIMATIONTIME);
        tween4.easing(TWEEN.Easing.Linear.None);
        let tween5 = new TWEEN.Tween(pos).to({x: 177.78, y: 1.64, z: -61.37, lx: 200.84, ly: 0, lz: 0}, 30 * ANIMATIONTIME);
        tween5.easing(TWEEN.Easing.Linear.None);
        let tween6 = new TWEEN.Tween(pos).to({x: 359.81, y: 11.44, z: -26.28, lx: 400.76, ly: 0, lz: 0}, 35 * ANIMATIONTIME);
        tween6.easing(TWEEN.Easing.Linear.None);
        let tween7 = new TWEEN.Tween(pos).to({x: 703.65, y: 34.43, z: -73.74, lx: 726.29, ly: 0, lz: 0}, 40 * ANIMATIONTIME);
        tween7.easing(TWEEN.Easing.Linear.None);
        let tween8 = new TWEEN.Tween(pos).to({x: 1441.76, y: 2.69, z: -26.5, lx: 1447.09, ly: 0, lz: 0}, 50 * ANIMATIONTIME);
        tween8.easing(TWEEN.Easing.Linear.None);
        let tween9 = new TWEEN.Tween(pos).to({x: 2262.5, y: 4.43, z: 17.22, lx: 2263.59, ly: 0, lz: 0}, 65 * ANIMATIONTIME);
        tween9.easing(TWEEN.Easing.Linear.None);
        let tween10 = new TWEEN.Tween(pos).to({x: -374, y: 452, z: -3.4, lx: 400.76, ly: 0, lz: 0}, 85 * ANIMATIONTIME);
        tween10.easing(TWEEN.Easing.Linear.None);
    
        tween1.chain(tween2);
        tween2.chain(tween3);
        tween3.chain(tween4);
        tween4.chain(tween5);
        tween5.chain(tween6);
        tween6.chain(tween7);
        tween7.chain(tween8);
        tween8.chain(tween9);
        tween9.chain(tween10);
    
        var onUpdate = function () {
            var px = this.x;
            var py = this.y;
            var pz = this.z;
            var lx = this.lx;
            var ly = this.ly;
            var lz = this.lz;
    
            camera.position.set(px, py, pz);
            camera.lookAt(new THREE.Vector3(lx, ly, lz))
        };
    
        tween1.onUpdate(onUpdate);
        tween2.onUpdate(onUpdate);
        tween3.onUpdate(onUpdate);
        tween4.onUpdate(onUpdate);
        tween5.onUpdate(onUpdate);
        tween6.onUpdate(onUpdate);
        tween7.onUpdate(onUpdate);
        tween8.onUpdate(onUpdate);
        tween9.onUpdate(onUpdate);
        tween10.onUpdate(onUpdate);
        tween1.delay(70 * ANIMATIONTIME);
        tween2.delay(30 * ANIMATIONTIME);
        tween3.delay(30 * ANIMATIONTIME);
        tween4.delay(30 * ANIMATIONTIME);
        tween5.delay(30 * ANIMATIONTIME);
        tween6.delay(30 * ANIMATIONTIME);
        tween7.delay(30 * ANIMATIONTIME);
        tween8.delay(30 * ANIMATIONTIME);
        tween9.delay(30 * ANIMATIONTIME);
        tween10.delay(30 * ANIMATIONTIME);
        tween1.start();
        tween10.onComplete(function () {
            off = true;
        })
    }

    代码很长不过很好理解...
    这样我们就完成了一个简单的太阳系。

    想看demo的请移步至 郭志强的博客

    转载请注明原文地址 https://www.cnblogs.com/vadim-web/p/12077466.html

  • 相关阅读:
    [Lydsy1706月赛]大根堆
    某考试 T1 Function
    [SHOI2016] 随机序列
    某考试 T1 至危警告
    某考试 T2 yja
    bzoj1880 [Sdoi2009]Elaxia的路线
    bzoj1804 [Ioi2007]Flood 洪水
    bzoj4546 codechef XRQRS
    bzoj4547 小奇的集合
    bzoj1443 [JSOI2009]游戏Game
  • 原文地址:https://www.cnblogs.com/vadim-web/p/12077466.html
Copyright © 2011-2022 走看看