In questo tutorial, ti guiderò attraverso la creazione di un programma Python per visualizzare un toro 3D rappresentato con linee e farlo ruotare intorno al suo asse utilizzando PyOpenGL e Pygame. Infine, mostrerò come generare e salvare una GIF animata del toro ruotante.
Prerequisiti:
Prima di iniziare, assicurati di aver installato le seguenti dipendenze:
pip install PyOpenGL PyOpenGL_accelerate numpy pygame imageio imageio[ffmpeg]
Passo 1: Importare le librerie necessarie
Innanzitutto, importiamo le librerie che useremo nel nostro codice:
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import numpy as np
import imageio
Passo 2: Creare la funzione per generare il toro
Creiamo una funzione chiamata torus
che genera i vertici e gli indici del toro. La funzione accetta quattro argomenti: il raggio del cerchio interno, il raggio del tubo, il numero di segmenti e il numero di segmenti del tubo.
def torus(radius, tube_radius, num_segments, num_tube_segments):
vertices = []
indices = []
# Calcolare i vertici del toro
for i in range(num_segments):
theta = i * 2 * np.pi / num_segments
for j in range(num_tube_segments):
phi = j * 2 * np.pi / num_tube_segments
x = (radius + tube_radius * np.cos(phi)) * np.cos(theta)
y = (radius + tube_radius * np.cos(phi)) * np.sin(theta)
z = tube_radius * np.sin(phi)
vertices.append([x, y, z])
# Creare gli indici per collegare i vertici
for i in range(num_segments):
for j in range(num_tube_segments):
index1 = (i * num_tube_segments + j) % (num_segments * num_tube_segments)
index2 = ((i + 1) * num_tube_segments + j) % (num_segments * num_tube_segments)
index3 = ((i + 1) * num_tube_segments + (j + 1)) % (num_segments * num_tube_segments)
index4 = (i * num_tube_segments + (j + 1)) % (num_segments * num_tube_segments)
indices.extend([index1, index2, index3, index4])
return vertices, indices
Passo 3: Creare la funzione per disegnare il toro
La funzione draw_torus
accetta i vertici e gli indici generati dalla funzione torus
e utilizza le funzioni OpenGL per disegnare il toro come una serie di linee.
def draw_torus(vertices, indices):
for i in range(0, len(indices), 4):
glBegin(GL_LINES)
glVertex3fv(vertices[indices[i]])
glVertex3fv(vertices[indices[i + 1]])
glEnd()
glBegin(GL_LINES)
glVertex3fv(vertices[indices[i + 1]])
glVertex3fv(vertices[indices[i + 2]])
glEnd()
glBegin(GL_LINES)
glVertex3fv(vertices[indices[i + 2]])
glVertex3fv(vertices[indices[i + 3]])
glEnd()
glBegin(GL_LINES)
glVertex3fv(vertices[indices[i + 3]])
glVertex3fv(vertices[indices[i]])
glEnd()
Passo 4: Creare la funzione per catturare i frame OpenGL
Creiamo una funzione chiamata capture_frame
che utilizza glReadPixels
per catturare i frame direttamente dall’OpenGL e restituisce un array NumPy.
def capture_frame(width, height):
buffer = (GLubyte * (3 * width * height))()
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer)
return np.frombuffer(buffer, dtype=np.uint8).reshape(height, width, 3)[::-1, :, :]
Passo 5: Creare la funzione principale
Ora creiamo la funzione principale main
che inizializza Pygame, configura la finestra e la visualizzazione OpenGL, e crea un ciclo principale che gestisce gli eventi, aggiorna la rotazione del toro, disegna il toro sullo schermo e cattura i frame per generare la GIF animata.
def main():
pygame.init()
display = (800, 600)
pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
gluPerspective(45, (display[0] / display[1]), 0.1, 50.0)
glTranslatef(0.0, 0.0, -5)
glEnable(GL_DEPTH_TEST)
torus_vertices, torus_indices = torus(0.5, 0.5, 32, 32)
frames = []
num_frames = 360
frame_interval = 10
# Ruota il toro di 90 gradi
glRotatef(90, 0, 1, 0)
for _ in range(num_frames):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
glRotatef(1, 0, 1, 0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glColor3f(1, 1, 1)
draw_torus(torus_vertices, torus_indices)
pygame.display.flip()
# Cattura il frame corrente e aggiungilo all'array di frame
frame_data = capture_frame(*display)
frames.append(frame_data)
pygame.time.wait(frame_interval)
# Salva l'array di frame come una GIF animata
imageio.mimsave('torus_animation.gif', frames, fps=30)
pygame.quit()
if __name__ == "__main__":
main()