介绍

太阳系是一个以太阳为中心,受其引力约束的天体系统。

**太阳**是绝对的主宰,质量占整个系统的99.86%,内部持续进行氢核聚变,为地球提供光和热。

八大行星按距离太阳的远近分为两类:**内圈行星**(水星、金星、地球、火星)是由岩石构成的类地行星,体积小、密度大;**外圈行星**(木星、土星、天王星、海王星)是气态或冰态巨行星,体积巨大,带有光环和众多卫星。其中,木星质量最大,土星拥有美丽的光环。

此外,系统中还包含了小行星带(位于火星和木星之间)、柯伊伯带以及无数彗星。整个太阳系直径约2光年,目前人类探测器已造访了所有行星,但仍在探索其边界——奥尔特云。

使用工具

001-OpenGL 
002-Pygame
003-numpy 
004-math

安装依赖

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy pygame PyOpenGL

代码实现

import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import math
import random
import sys
import numpy as np

# 初始化Pygame和OpenGL
def init_opengl(width=1200, height=800):
    pygame.init()
    display = (width, height)
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
    pygame.display.set_caption("太阳系运行模型 - 终极版")
    
    # 设置透视投影
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45, (display[0] / display[1]), 0.1, 1000.0)
    
    # 切换到模型视图矩阵
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    
    # 设置清屏颜色为深空色
    glClearColor(0.05, 0.05, 0.1, 1.0)
    glEnable(GL_DEPTH_TEST)
    
    # 启用光照和雾效
    glEnable(GL_LIGHTING)
    glEnable(GL_LIGHT0)
    glEnable(GL_LIGHT1)
    
    # 主光源(太阳)
    glLightfv(GL_LIGHT0, GL_POSITION, [0, 0, 0, 1])
    glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 1.0, 1.0, 1.0])
    glLightfv(GL_LIGHT0, GL_SPECULAR, [1.0, 1.0, 1.0, 1.0])
    
    # 环境光
    ambient_light = [0.2, 0.2, 0.2, 1.0]
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient_light)
    
    # 启用颜色追踪和材质
    glEnable(GL_COLOR_MATERIAL)
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
    
    # 启用平滑着色
    glShadeModel(GL_SMOOTH)
    
    # 启用雾效(银河背景效果)
    glEnable(GL_FOG)
    glFogfv(GL_FOG_COLOR, [0.05, 0.05, 0.1, 1.0])
    glFogf(GL_FOG_DENSITY, 0.01)
    glFogi(GL_FOG_MODE, GL_EXP)
    
    # 设置观察位置
    gluLookAt(30, 20, 50,  # 相机位置
              0, 0, 0,      # 观察中心
              0, 1, 0)      # 上方向

# 绘制星空的背景
def draw_stars(count=1000):
    glDisable(GL_LIGHTING)
    glPointSize(1.5)
    glBegin(GL_POINTS)
    for _ in range(count):
        # 随机分布在远处
        x = random.uniform(-300, 300)
        y = random.uniform(-300, 300)
        z = random.uniform(-300, 300)
        # 随机亮度
        brightness = random.uniform(0.5, 1.0)
        glColor3f(brightness, brightness, brightness)
        glVertex3f(x, y, z)
    glEnd()
    glEnable(GL_LIGHTING)

# 绘制球体(带纹理效果)
def draw_sphere(radius, color=None, emission=False, slices=48, stacks=48):
    if color:
        glColor3f(*color)
    
    if emission:
        glMaterialfv(GL_FRONT, GL_EMISSION, [*color, 1.0])
    
    # 使用二次曲面绘制球体
    quadric = gluNewQuadric()
    gluQuadricNormals(quadric, GLU_SMOOTH)
    gluQuadricTexture(quadric, GL_TRUE)
    gluSphere(quadric, radius, slices, stacks)
    gluDeleteQuadric(quadric)
    
    if emission:
        glMaterialfv(GL_FRONT, GL_EMISSION, [0.0, 0.0, 0.0, 1.0])

# 绘制轨道线(带渐变效果)
def draw_orbit(radius, color=(0.3, 0.3, 0.3), segments=200):
    glDisable(GL_LIGHTING)
    glLineWidth(1.0)
    glColor3f(*color)
    glBegin(GL_LINE_LOOP)
    for i in range(segments):
        theta = 2.0 * math.pi * i / segments
        x = radius * math.cos(theta)
        z = radius * math.sin(theta)
        glVertex3f(x, 0, z)
    glEnd()
    glEnable(GL_LIGHTING)

# 绘制小行星带(增强版)
def draw_asteroid_belt(inner=2.2, outer=3.3, count=1000):
    glDisable(GL_LIGHTING)
    glPointSize(1.0)
    glBegin(GL_POINTS)
    for _ in range(count):
        r = random.uniform(inner, outer)
        angle = random.uniform(0, 2*math.pi)
        y = random.uniform(-0.5, 0.5)  # 增加厚度
        x = r * math.cos(angle)
        z = r * math.sin(angle)
        # 随机灰度
        gray = random.uniform(0.4, 0.8)
        glColor3f(gray, gray, gray)
        glVertex3f(x, y, z)
    glEnd()
    glEnable(GL_LIGHTING)

# 绘制柯伊伯带
def draw_kuiper_belt(inner=25, outer=35, count=2000):
    glDisable(GL_LIGHTING)
    glPointSize(1.2)
    glBegin(GL_POINTS)
    for _ in range(count):
        r = random.uniform(inner, outer)
        angle = random.uniform(0, 2*math.pi)
        y = random.uniform(-2.0, 2.0)  # 更大的厚度
        x = r * math.cos(angle)
        z = r * math.sin(angle)
        # 略带蓝色的冰质天体
        blue = random.uniform(0.5, 0.8)
        glColor3f(0.3, 0.4, blue)
        glVertex3f(x, y, z)
    glEnd()
    glEnable(GL_LIGHTING)

# 哈雷彗星类
class Comet:
    def __init__(self, name, radius, distance, color, speed, eccentricity=0.8):
        self.name = name
        self.radius = radius
        self.distance = distance  # 半长轴
        self.color = color
        self.speed = speed
        self.eccentricity = eccentricity  # 离心率
        self.angle = random.uniform(0, 2*math.pi)
        self.tail_length = 5.0
        
    def update(self):
        self.angle += self.speed
        
    def get_position(self):
        # 椭圆轨道计算
        r = self.distance * (1 - self.eccentricity**2) / (1 + self.eccentricity * math.cos(self.angle))
        x = r * math.cos(self.angle)
        z = r * math.sin(self.angle)
        return (x, 0, z)
    
    def draw_tail(self, x, y, z):
        """绘制彗尾"""
        glDisable(GL_LIGHTING)
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        
        # 彗尾方向(背离太阳)
        distance_to_sun = math.sqrt(x**2 + z**2)
        dir_x = -x / distance_to_sun if distance_to_sun > 0 else 0
        dir_z = -z / distance_to_sun if distance_to_sun > 0 else 0
        
        # 绘制粒子尾
        glPointSize(2.0)
        glBegin(GL_POINTS)
        for i in range(50):
            t = i / 50.0
            alpha = 1.0 - t
            tail_x = x + dir_x * t * self.tail_length * 3
            tail_z = z + dir_z * t * self.tail_length * 3
            tail_y = y + random.uniform(-0.2, 0.2) * t
            
            # 颜色渐变
            glColor4f(1.0, 1.0, 0.8, alpha)
            glVertex3f(tail_x, tail_y, tail_z)
        glEnd()
        
        glDisable(GL_BLEND)
        glEnable(GL_LIGHTING)
    
    def draw(self):
        x, y, z = self.get_position()
        
        # 绘制椭圆轨道
        glDisable(GL_LIGHTING)
        glColor3f(0.4, 0.4, 0.6)
        glBegin(GL_LINE_LOOP)
        for i in range(100):
            theta = 2.0 * math.pi * i / 100
            r = self.distance * (1 - self.eccentricity**2) / (1 + self.eccentricity * math.cos(theta))
            x_orbit = r * math.cos(theta)
            z_orbit = r * math.sin(theta)
            glVertex3f(x_orbit, 0, z_orbit)
        glEnd()
        glEnable(GL_LIGHTING)
        
        # 绘制彗尾
        self.draw_tail(x, y, z)
        
        # 绘制彗核
        glPushMatrix()
        glTranslatef(x, y, z)
        # 彗核发光效果
        glMaterialfv(GL_FRONT, GL_EMISSION, [0.3, 0.3, 0.1, 1.0])
        draw_sphere(self.radius, self.color)
        glMaterialfv(GL_FRONT, GL_EMISSION, [0.0, 0.0, 0.0, 1.0])
        
        # 标签
        glDisable(GL_LIGHTING)
        draw_text(0, self.radius + 0.8, 0, self.name, (1,1,0.8), 0.7)
        glEnable(GL_LIGHTING)
        glPopMatrix()

# 绘制文本标签
def draw_text(x, y, z, text, color=(1,1,1), scale=1.0):
    glColor3f(*color)
    glPushMatrix()
    glTranslatef(x, y, z)
    glScalef(0.005 * scale, 0.005 * scale, 0.005 * scale)
    for char in text:
        glutStrokeCharacter(GLUT_STROKE_ROMAN, ord(char))
    glPopMatrix()

# 天体基类(增强版)
class CelestialBody:
    def __init__(self, name, radius, distance, color, speed, has_orbit=True, emission=False):
        self.name = name
        self.radius = radius
        self.distance = distance
        self.color = color
        self.speed = speed
        self.angle = random.uniform(0, 2*math.pi)
        self.has_orbit = has_orbit
        self.emission = emission
        self.moons = []
        self.rotation = 0
        self.rotation_speed = random.uniform(0.01, 0.05)

    def add_moon(self, moon):
        self.moons.append(moon)

    def update(self):
        self.angle += self.speed
        self.rotation += self.rotation_speed
        for moon in self.moons:
            moon.update()

    def get_position(self):
        x = self.distance * math.cos(self.angle)
        z = self.distance * math.sin(self.angle)
        return (x, 0, z)

    def draw(self):
        # 绘制轨道
        if self.has_orbit:
            draw_orbit(self.distance, (0.4, 0.4, 0.5))

        # 获取位置
        x, y, z = self.get_position()

        # 绘制星球
        glPushMatrix()
        glTranslatef(x, y, z)
        glRotatef(self.rotation, 0, 1, 0)  # 自转
        
        # 绘制球体
        draw_sphere(self.radius, self.color, self.emission)
        
        # 标签
        glDisable(GL_LIGHTING)
        draw_text(0, self.radius + 0.8, 0, self.name, (1,1,1), 0.8)
        glEnable(GL_LIGHTING)
        glPopMatrix()

        # 绘制卫星
        for moon in self.moons:
            moon.draw_with_parent(x, y, z)

# 卫星类(增强版)
class Moon:
    def __init__(self, name, radius, distance, color, speed):
        self.name = name
        self.radius = radius
        self.distance = distance
        self.color = color
        self.speed = speed
        self.angle = random.uniform(0, 2*math.pi)

    def update(self):
        self.angle += self.speed

    def draw_with_parent(self, parent_x, parent_y, parent_z):
        x = parent_x + self.distance * math.cos(self.angle)
        z = parent_z + self.distance * math.sin(self.angle)
        y = parent_y

        # 绘制卫星轨道(淡化的线)
        glDisable(GL_LIGHTING)
        glColor3f(0.3, 0.3, 0.4)
        glBegin(GL_LINE_LOOP)
        for i in range(30):
            theta = 2.0 * math.pi * i / 30
            rx = parent_x + self.distance * math.cos(theta)
            rz = parent_z + self.distance * math.sin(theta)
            glVertex3f(rx, y, rz)
        glEnd()
        glEnable(GL_LIGHTING)

        # 绘制卫星
        glPushMatrix()
        glTranslatef(x, y, z)
        draw_sphere(self.radius, self.color)
        
        glDisable(GL_LIGHTING)
        draw_text(0, self.radius + 0.4, 0, self.name, (1,1,1), 0.5)
        glEnable(GL_LIGHTING)
        glPopMatrix()

def create_solar_system():
    """创建太阳系中的所有天体"""
    
    # 太阳(发光)
    sun = CelestialBody("Sun", 3.0, 0, (1.0, 1.0, 0.0), 0, has_orbit=False, emission=True)
    
    # 水星
    mercury = CelestialBody("Mercury", 0.5, 4.0, (0.6, 0.6, 0.6), 0.015)
    
    # 金星
    venus = CelestialBody("Venus", 0.7, 5.5, (0.9, 0.7, 0.4), 0.012)
    
    # 地球 + 月球
    earth = CelestialBody("Earth", 0.8, 7.0, (0.0, 0.5, 1.0), 0.01)
    moon = Moon("Moon", 0.25, 1.2, (0.8, 0.8, 0.8), 0.05)
    earth.add_moon(moon)

    # 火星 + 2颗卫星
    mars = CelestialBody("Mars", 0.7, 8.5, (1.0, 0.4, 0.0), 0.009)
    phobos = Moon("Phobos", 0.15, 0.7, (0.6, 0.6, 0.6), 0.08)
    deimos = Moon("Deimos", 0.12, 1.0, (0.6, 0.6, 0.6), 0.06)
    mars.add_moon(phobos)
    mars.add_moon(deimos)

    # 木星 + 4颗卫星(增加木卫四)
    jupiter = CelestialBody("Jupiter", 1.5, 11.0, (0.9, 0.7, 0.5), 0.007)
    io = Moon("Io", 0.3, 1.6, (0.9, 0.9, 0.5), 0.07)
    europa = Moon("Europa", 0.28, 2.0, (0.7, 0.7, 0.8), 0.065)
    ganymede = Moon("Ganymede", 0.32, 2.4, (0.6, 0.6, 0.7), 0.06)
    callisto = Moon("Callisto", 0.3, 2.8, (0.6, 0.6, 0.5), 0.055)
    jupiter.add_moon(io)
    jupiter.add_moon(europa)
    jupiter.add_moon(ganymede)
    jupiter.add_moon(callisto)

    # 土星 + 5颗卫星(增加更多卫星)
    saturn = CelestialBody("Saturn", 1.3, 14.0, (0.8, 0.7, 0.4), 0.006)
    titan = Moon("Titan", 0.35, 1.8, (0.8, 0.8, 0.6), 0.055)
    rhea = Moon("Rhea", 0.25, 2.2, (0.7, 0.7, 0.7), 0.05)
    iapetus = Moon("Iapetus", 0.3, 2.7, (0.6, 0.6, 0.6), 0.045)
    dione = Moon("Dione", 0.2, 1.4, (0.7, 0.7, 0.7), 0.06)
    tethys = Moon("Tethys", 0.2, 1.1, (0.7, 0.7, 0.7), 0.065)
    saturn.add_moon(titan)
    saturn.add_moon(rhea)
    saturn.add_moon(iapetus)
    saturn.add_moon(dione)
    saturn.add_moon(tethys)

    # 天王星 + 3颗卫星
    uranus = CelestialBody("Uranus", 1.1, 17.0, (0.5, 0.9, 0.9), 0.005)
    titania = Moon("Titania", 0.28, 1.7, (0.8, 0.8, 0.9), 0.06)
    oberon = Moon("Oberon", 0.26, 2.1, (0.7, 0.8, 0.9), 0.055)
    umbriel = Moon("Umbriel", 0.22, 1.3, (0.7, 0.7, 0.8), 0.065)
    uranus.add_moon(titania)
    uranus.add_moon(oberon)
    uranus.add_moon(umbriel)

    # 海王星 + 3颗卫星
    neptune = CelestialBody("Neptune", 1.1, 20.0, (0.2, 0.4, 1.0), 0.004)
    triton = Moon("Triton", 0.29, 1.8, (0.8, 0.8, 0.8), 0.058)
    nereid = Moon("Nereid", 0.24, 2.2, (0.7, 0.7, 0.7), 0.052)
    proteus = Moon("Proteus", 0.2, 1.4, (0.7, 0.7, 0.7), 0.062)
    neptune.add_moon(triton)
    neptune.add_moon(nereid)
    neptune.add_moon(proteus)

    # 哈雷彗星
    halley = Comet("Halley", 0.4, 25.0, (0.9, 0.9, 0.8), 0.002, eccentricity=0.8)

    # 所有行星按距离排序
    planets = [mercury, venus, earth, mars, jupiter, saturn, uranus, neptune]
    
    return sun, planets, halley

def draw_saturn_rings(planet_x, planet_y, planet_z, radius):
    """绘制土星的光环(增强版)"""
    glDisable(GL_LIGHTING)
    glEnable(GL_BLEND)
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
    
    # 绘制多个光环层
    ring_colors = [(0.8, 0.7, 0.5, 0.6), (0.7, 0.6, 0.4, 0.5), (0.6, 0.5, 0.3, 0.4)]
    ring_radii = [(1.8, 2.5), (2.2, 2.8), (2.5, 3.0)]
    
    for (inner_r, outer_r), color in zip(ring_radii, ring_colors):
        glBegin(GL_TRIANGLE_STRIP)
        for i in range(0, 360, 5):
            theta = math.radians(i)
            next_theta = math.radians(i + 5)
            
            # 内环和外环
            x1 = planet_x + inner_r * radius * math.cos(theta)
            z1 = planet_z + inner_r * radius * math.sin(theta)
            x2 = planet_x + outer_r * radius * math.cos(theta)
            z2 = planet_z + outer_r * radius * math.sin(theta)
            x3 = planet_x + inner_r * radius * math.cos(next_theta)
            z3 = planet_z + inner_r * radius * math.sin(next_theta)
            x4 = planet_x + outer_r * radius * math.cos(next_theta)
            z4 = planet_z + outer_r * radius * math.sin(next_theta)
            
            glColor4f(*color)
            glVertex3f(x1, planet_y - 0.1, z1)
            glVertex3f(x2, planet_y - 0.1, z2)
            glVertex3f(x3, planet_y - 0.1, z3)
            glVertex3f(x4, planet_y - 0.1, z4)
        glEnd()
    
    glDisable(GL_BLEND)
    glEnable(GL_LIGHTING)

def main():
    # 初始化GLUT
    glutInit(sys.argv)
    
    # 初始化OpenGL
    init_opengl()
    
    # 创建太阳系
    sun, planets, halley = create_solar_system()
    
    clock = pygame.time.Clock()
    running = True
    rotation_x = 20
    rotation_y = 30
    distance = 60
    
    print("=" * 50)
    print("太阳系运行模型 - 终极版")
    print("=" * 50)
    print("包含天体:")
    print("· 太阳(中心发光体)")
    print("· 八大行星(水星、金星、地球、火星、木星、土星、天王星、海王星)")
    print("· 行星卫星(共17颗卫星)")
    print("· 小行星带(1000颗小行星)")
    print("· 哈雷彗星(椭圆轨道)")
    print("· 柯伊伯带(2000颗冰质天体)")
    print("\n操作说明:")
    print("- 鼠标拖动:旋转视角")
    print("- 滚轮:缩放")
    print("- ESC:退出")
    print("=" * 50)
    
    # 鼠标控制
    pygame.mouse.set_pos(600, 400)
    pygame.mouse.set_visible(False)
    mouse_pressed = False
    last_mouse_x = 600
    last_mouse_y = 400
    
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    running = False
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 4:  # 滚轮向上
                    distance = max(20, distance - 2)
                elif event.button == 5:  # 滚轮向下
                    distance = min(200, distance + 2)
                elif event.button == 1:  # 左键按下
                    mouse_pressed = True
                    pygame.mouse.set_visible(False)
            elif event.type == pygame.MOUSEBUTTONUP:
                if event.button == 1:  # 左键释放
                    mouse_pressed = False
                    pygame.mouse.set_visible(True)
            elif event.type == pygame.MOUSEMOTION:
                if mouse_pressed:
                    dx = event.pos[0] - last_mouse_x
                    dy = event.pos[1] - last_mouse_y
                    rotation_y += dx * 0.3
                    rotation_x += dy * 0.3
                    rotation_x = max(-90, min(90, rotation_x))
        
        if mouse_pressed:
            last_mouse_x, last_mouse_y = pygame.mouse.get_pos()

        # 清除缓冲区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        
        # 重置模型视图矩阵
        glLoadIdentity()
        
        # 设置相机位置
        rad_x = math.radians(rotation_x)
        rad_y = math.radians(rotation_y)
        
        eye_x = distance * math.cos(rad_x) * math.sin(rad_y)
        eye_y = distance * math.sin(rad_x)
        eye_z = distance * math.cos(rad_x) * math.cos(rad_y)
        
        gluLookAt(eye_x, eye_y, eye_z,
                  0, 0, 0,
                  0, 1, 0)

        # 绘制星空背景
        draw_stars(2000)
        
        # 绘制太阳(自发光)
        sun.draw()
        
        # 启用光照绘制行星
        glEnable(GL_LIGHTING)
        
        # 绘制小行星带
        draw_asteroid_belt(inner=9.5, outer=10.5, count=1000)
        
        # 绘制柯伊伯带
        draw_kuiper_belt(inner=22, outer=28, count=2000)
        
        # 更新并绘制行星
        for planet in planets:
            planet.update()
            planet.draw()
            
            # 绘制土星光环
            if planet.name == "Saturn":
                x, y, z = planet.get_position()
                draw_saturn_rings(x, y, z, planet.radius)
        
        # 更新并绘制哈雷彗星
        halley.update()
        halley.draw()
        
        # 更新显示
        pygame.display.flip()
        clock.tick(60)

    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()

效果演示:

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐