最近学了 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 的请移步至 郭志强的博客 http://www.bettersmile.cn/
来源: https://www.cnblogs.com/vadim-web/p/12077466.html