震惊!我用Python和OpenGL还原了整个太阳系,木星真的会转!
·
介绍
太阳系是一个以太阳为中心,受其引力约束的天体系统。
**太阳**是绝对的主宰,质量占整个系统的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()
效果演示:

更多推荐


所有评论(0)