效果:
一、只需打開命令行(Windows 可用 cmd),輸入:
pip install PyQt5 PyOpenGL numpy
二、用命令行進入保存 .py 文件的目錄,運行:
python openGL_3d_demo.py
三、建立python文件命名openGL_3d_demo.py后粘貼代碼,然后運行
import sys
import math
import time
import numpy as np
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QFont, QPalette, QColor
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QGridLayout,QTabWidget, QLabel, QFrame, QOpenGLWidget
)
from OpenGL.GL import *
from OpenGL.GLU import *# ========== 通用3D控件 ==========
class Base3DGL(QOpenGLWidget):name = "3D效果"def __init__(self, parent=None):super().__init__(parent)self.last_pos = Noneself.angle_x, self.angle_y = 20, 20self.fps = 0self._frames = 0self._last_time = time.time()self.setMinimumSize(220, 180)self.timer = QTimer(self)self.timer.timeout.connect(self.update)self.timer.start(16)def initializeGL(self):glClearColor(0.10, 0.11, 0.15, 1.0)glEnable(GL_DEPTH_TEST)glShadeModel(GL_SMOOTH)def resizeGL(self, w, h):glViewport(0, 0, w, h)glMatrixMode(GL_PROJECTION)glLoadIdentity()gluPerspective(40.0, w / float(h or 1), 1.0, 100.0)glMatrixMode(GL_MODELVIEW)def paintGL(self):self._frames += 1now = time.time()if now - self._last_time > 1.0:self.fps = self._frames / (now - self._last_time)self._last_time = nowself._frames = 0glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)glLoadIdentity()glTranslatef(0, 0, -9)glRotatef(self.angle_x, 1, 0, 0)glRotatef(self.angle_y, 0, 1, 0)self.render_scene()def mousePressEvent(self, event):self.last_pos = event.pos()def mouseMoveEvent(self, event):if self.last_pos:dx = event.x() - self.last_pos.x()dy = event.y() - self.last_pos.y()self.angle_y += dx * 0.7self.angle_x += dy * 0.7self.last_pos = event.pos()self.update()# ========== 50種3D效果 ==========# ----------- 第1頁(常見幾何體) -----------
class SphereGL(Base3DGL):name = "漸變球體"def render_scene(self):for i in range(30):lat0 = math.pi * (-0.5 + float(i) / 30)z0 = math.sin(lat0)zr0 = math.cos(lat0)lat1 = math.pi * (-0.5 + float(i + 1) / 30)z1 = math.sin(lat1)zr1 = math.cos(lat1)glBegin(GL_QUAD_STRIP)for j in range(32):lng = 2 * math.pi * float(j) / 32x = math.cos(lng)y = math.sin(lng)c1 = abs(x)c2 = abs(y)c3 = abs(z0)glColor3f(0.5*c1+0.2, 0.2+0.5*c2, 0.4+0.5*c3)glVertex3f(x * zr0, y * zr0, z0)c3b = abs(z1)glColor3f(0.5*c1+0.3, 0.4+0.5*c2, 0.3+0.5*c3b)glVertex3f(x * zr1, y * zr1, z1)glEnd()class CubeGL(Base3DGL):name = "彩色立方體"def render_scene(self):faces = [((1,1,1), (1,1,-1), (1,-1,-1), (1,-1,1)), # +X((-1,1,1), (-1,1,-1), (-1,-1,-1), (-1,-1,1)), # -X((1,1,1), (1,1,-1), (-1,1,-1), (-1,1,1)), # +Y((1,-1,1), (1,-1,-1), (-1,-1,-1), (-1,-1,1)), # -Y((1,1,1), (1,-1,1), (-1,-1,1), (-1,1,1)), # +Z((1,1,-1), (1,-1,-1), (-1,-1,-1), (-1,1,-1)) # -Z]colors = [(1,0,0), (0,1,0), (0,0,1), (1,1,0), (1,0,1), (0,1,1)]glBegin(GL_QUADS)for i, f in enumerate(faces):glColor3f(*colors[i])for v in f:glVertex3f(*v)glEnd()class OctahedronGL(Base3DGL):name = "多彩八面體"def render_scene(self):verts = [(1,0,0), (-1,0,0), (0,1,0), (0,-1,0), (0,0,1), (0,0,-1)]faces = [(0,2,4),(2,1,4),(1,3,4),(3,0,4),(0,2,5),(2,1,5),(1,3,5),(3,0,5)]colors = [(1,0,0),(0,1,0),(0,0,1),(1,1,0),(1,0,1),(0,1,1),(1,0.5,0.5),(0.5,1,0.5)]glBegin(GL_TRIANGLES)for i, f in enumerate(faces):glColor3f(*colors[i%len(colors)])for idx in f:glVertex3f(*verts[idx])glEnd()class IcosahedronGL(Base3DGL):name = "漸變二十面體"def render_scene(self):t = (1.0 + math.sqrt(5.0)) / 2.0verts = [(-1, t, 0), ( 1, t, 0), (-1, -t, 0), ( 1, -t, 0),( 0, -1, t), ( 0, 1, t), ( 0, -1, -t), ( 0, 1, -t),( t, 0, -1), ( t, 0, 1), (-t, 0, -1), (-t, 0, 1)]faces = [(0,11,5),(0,5,1),(0,1,7),(0,7,10),(0,10,11),(1,5,9),(5,11,4),(11,10,2),(10,7,6),(7,1,8),(3,9,4),(3,4,2),(3,2,6),(3,6,8),(3,8,9),(4,9,5),(2,4,11),(6,2,10),(8,6,7),(9,8,1)]glBegin(GL_TRIANGLES)for i, f in enumerate(faces):c = (i%3/2.0, (i*2)%5/5.0, (i*3)%7/7.0)glColor3f(*c)for idx in f:v = verts[idx]s = 0.6glVertex3f(v[0]*s, v[1]*s, v[2]*s)glEnd()class DodecahedronGL(Base3DGL):name = "炫彩十二面體"def render_scene(self):phi = (1 + 5 ** 0.5) / 2a, b = 1, 1/phiverts = [[+a,+a,+a], [+a,+a,-a], [+a,-a,+a], [+a,-a,-a],[-a,+a,+a], [-a,+a,-a], [-a,-a,+a], [-a,-a,-a],[0,+b+phi,+b], [0,+b+phi,-b], [0,-b-phi,+b], [0,-b-phi,-b],[+b,+0,+b+phi], [+b,+0,-b-phi], [-b,+0,+b+phi], [-b,+0,-b-phi],[+b+phi,+b,+0], [+b+phi,-b,+0], [-b-phi,+b,+0], [-b-phi,-b,+0]]faces = [[0,8,4,14,12], [0,12,16,2,10], [0,10,6,18,8], [8,18,19,5,4], [12,14,15,17,16],[2,16,17,1,13], [10,2,13,11,6], [6,11,7,19,18], [4,5,15,14], [1,17,15,5,9], [1,9,3,13], [3,11,13,9,7,19]]for i, face in enumerate(faces):glBegin(GL_POLYGON)c = [(i%3)/2.0, (i*2)%5/5.0, (i*3)%7/7.0]glColor3f(*c)for idx in face:v = verts[idx]glVertex3f(*[0.36*e for e in v])glEnd()class CylinderGL(Base3DGL):name = "三色圓柱體"def render_scene(self):N = 32for j, color in enumerate([(1,0,0), (0,1,0), (0,0,1)]):glColor3f(*color)glBegin(GL_QUAD_STRIP)for i in range(j*N//3,(j+1)*N//3+1):ang = 2*math.pi*i/Nx = math.cos(ang)y = math.sin(ang)glVertex3f(x, y, 1)glVertex3f(x, y, -1)glEnd()for z, c in [(1, (0.7,0.8,0.4)), (-1, (0.5,0.9,0.6))]:glColor3f(*c)glBegin(GL_TRIANGLE_FAN)glVertex3f(0,0,z)for i in range(N+1):ang = 2*math.pi*i/NglVertex3f(math.cos(ang), math.sin(ang), z)glEnd()class ConeGL(Base3DGL):name = "三色圓錐體"def render_scene(self):N = 32for cidx, color in enumerate([(1,0,0),(0,1,0),(0,0,1)]):glColor3f(*color)glBegin(GL_TRIANGLE_FAN)glVertex3f(0,0,1.3)for i in range(cidx*N//3,(cidx+1)*N//3+1):ang = 2*math.pi*i/NglVertex3f(math.cos(ang), math.sin(ang), -1)glEnd()glColor3f(0.2, 0.6, 0.4)glBegin(GL_TRIANGLE_FAN)glVertex3f(0,0,-1)for i in range(N+1):ang = 2*math.pi*i/NglVertex3f(math.cos(ang), math.sin(ang), -1)glEnd()class TorusGL(Base3DGL):name = "彩虹圓環體"def render_scene(self):R, r = 1.0, 0.36N, M = 32, 18for i in range(N):glBegin(GL_QUAD_STRIP)for j in range(M+1):for k in [0, 1]:s = 2 * math.pi * (i + k) / Nt = 2 * math.pi * j / Mx = (R + r * math.cos(t)) * math.cos(s)y = (R + r * math.cos(t)) * math.sin(s)z = r * math.sin(t)glColor3f(abs(math.cos(s)), abs(math.sin(t)), abs(math.cos(t)))glVertex3f(x, y, z)glEnd()class SpiralGL(Base3DGL):name = "多彩螺旋線"def render_scene(self):glBegin(GL_LINE_STRIP)for idx, t in enumerate(np.linspace(0, 6*math.pi, 120)):r = 1.1 + 0.2*math.sin(t*6)x = math.cos(t)*ry = math.sin(t)*rz = -1.1 + t/(6*math.pi)*2.2glColor3f(abs(x), abs(y), abs(z))glVertex3f(x, y, z)glEnd()class WaveSurfaceGL(Base3DGL):name = "漸變波浪面"def render_scene(self):N = 20for i in range(N):glBegin(GL_TRIANGLE_STRIP)for j in range(N+1):x = -2 + 4*i/Ny = -2 + 4*j/Nz1 = math.sin(x*3+y*3+self.angle_x*0.05)*0.5z2 = math.sin((x+4/N)*3+y*3+self.angle_y*0.06)*0.5glColor3f(0.5+0.5*j/N,0.5+0.5*i/N,0.5+0.25*(i+j)/N)glVertex3f(x, y, z1)glVertex3f(x+4/N, y, z2)glEnd()# ----------- 第2頁(函數曲面,特殊線型等) -----------
class HeartSurfaceGL(Base3DGL):name = "心形面"def render_scene(self):glBegin(GL_POINTS)for i in range(0, 90, 1):for j in range(0, 360, 3):theta = math.radians(i)phi = math.radians(j)x = 16 * math.sin(theta)**3y = 13 * math.cos(theta) - 5 * math.cos(2*theta) - 2 * math.cos(3*theta) - math.cos(4*theta)z = 16 * math.sin(theta)**3 * math.sin(phi) / 4glColor3f(abs(math.sin(theta)), abs(math.cos(phi)), abs(math.sin(phi)))glVertex3f(x * 0.07, y * 0.07, z * 0.09)glEnd()class SineWaveGL(Base3DGL):name = "三色正弦波曲面"def render_scene(self):N = 40for i in range(N):glBegin(GL_TRIANGLE_STRIP)for j in range(N+1):x = -2 + 4*i/Ny = -2 + 4*j/Nz1 = math.sin(x*4+y*4)*0.7z2 = math.sin((x+4/N)*4+y*4)*0.7glColor3f(abs(math.sin(x)), abs(math.cos(y)), abs(z1))glVertex3f(x, y, z1)glVertex3f(x+4/N, y, z2)glEnd()class SaddleGL(Base3DGL):name = "馬鞍曲面"def render_scene(self):N = 25for i in range(N):glBegin(GL_TRIANGLE_STRIP)for j in range(N+1):x = -2 + 4*i/Ny = -2 + 4*j/Nz1 = (x**2 - y**2)/4z2 = ((x+4/N)**2 - y**2)/4glColor3f(abs(z1), abs(x/2), abs(y/2))glVertex3f(x, y, z1)glVertex3f(x+4/N, y, z2)glEnd()class EggShapeGL(Base3DGL):name = "蛋形曲面"def render_scene(self):N = 24for i in range(N):lat0 = math.pi * (-0.5 + float(i) / N)z0 = math.sin(lat0)*1.2 + 0.4*math.sin(lat0)**3zr0 = math.cos(lat0)lat1 = math.pi * (-0.5 + float(i+1) / N)z1 = math.sin(lat1)*1.2 + 0.4*math.sin(lat1)**3zr1 = math.cos(lat1)glBegin(GL_QUAD_STRIP)for j in range(N+1):lng = 2 * math.pi * float(j) / Nx = math.cos(lng)y = math.sin(lng)glColor3f((j%3)/2, (i%5)/4, (j%7)/6)glVertex3f(x * zr0, y * zr0, z0)glVertex3f(x * zr1, y * zr1, z1)glEnd()class KleinBottleGL(Base3DGL):name = "克萊因瓶-彩色"def render_scene(self):N = 32for i in range(N):glBegin(GL_QUAD_STRIP)for j in range(N+1):u = 2*math.pi*i/Nv = 2*math.pi*j/Nx = (2/15)*(3+5*math.cos(u))*math.sin(u) + (2/15)*2*math.cos(u)*math.sin(v)y = (2/15)*(3+5*math.cos(u))*math.cos(u) + (2/15)*2*math.sin(u)*math.sin(v)z = (2/15)*2*math.cos(v)glColor3f(abs(math.sin(u)), abs(math.sin(v)), abs(math.cos(u)))glVertex3f(x, y, z)glEnd()class StarGL(Base3DGL):name = "星形體-三色"def render_scene(self):glBegin(GL_TRIANGLE_FAN)glColor3f(1,0,0)glVertex3f(0,0,1.1)for i in range(13):ang = i/12*2*math.pir = 1.2 if i%2==0 else 0.6glColor3f((i%3)/2,(i%4)/3,(i%5)/4)glVertex3f(r*math.cos(ang), r*math.sin(ang), -0.7)glEnd()class MobiusGL(Base3DGL):name = "莫比烏斯環"def render_scene(self):N = 60w = 0.2for i in range(N):glBegin(GL_QUAD_STRIP)for j in range(2):phi = 2*math.pi*i/Nt = -w + 2*w*jx = math.cos(phi) + t*math.cos(phi/2)*math.cos(phi)y = math.sin(phi) + t*math.cos(phi/2)*math.sin(phi)z = t*math.sin(phi/2)glColor3f(abs(math.cos(phi)), abs(math.sin(phi)), abs(t))glVertex3f(x, y, z)glEnd()class RippleGL(Base3DGL):name = "漣漪"def render_scene(self):N = 30for i in range(N):glBegin(GL_LINE_STRIP)for j in range(N+1):r = 0.2 + 1.6 * i/Ntheta = 2*math.pi*j/Nz = math.sin(r*4-theta*2)*0.3glColor3f(abs(math.sin(r)), abs(math.cos(theta)), abs(z))glVertex3f(r*math.cos(theta), r*math.sin(theta), z)glEnd()class RoseGL(Base3DGL):name = "玫瑰線"def render_scene(self):glBegin(GL_LINE_STRIP)for i in range(600):theta = i/100 * math.pir = math.sin(3*theta)*1.5x = r*math.cos(theta)y = r*math.sin(theta)z = math.cos(5*theta)glColor3f(abs(math.sin(theta)), abs(math.cos(theta)), abs(z))glVertex3f(x, y, z)glEnd()class TrefoilGL(Base3DGL):name = "三葉結"def render_scene(self):glBegin(GL_LINE_STRIP)for i in range(400):t = 2*math.pi*i/400x = math.sin(t) + 2*math.sin(2*t)y = math.cos(t) - 2*math.cos(2*t)z = -math.sin(3*t)glColor3f(abs(math.sin(t)), abs(math.cos(t)), abs(z))glVertex3f(x, y, z)glEnd()# ----------- 第3頁(動態變體/動畫) -----------
class BubbleGL(Base3DGL):name = "泡泡球"def render_scene(self):t = time.time()for i in range(12, 40, 3):glBegin(GL_LINE_LOOP)r = 1+0.2*math.sin(t+i)for j in range(32):ang = 2*math.pi*j/32x, y, z = r*math.sin(i)*math.cos(ang), r*math.sin(i)*math.sin(ang), r*math.cos(i)glColor3f(abs(math.sin(ang)), abs(math.cos(i)), abs(math.cos(ang)))glVertex3f(x/15, y/15, z/15)glEnd()class AnimatedWaveGL(Base3DGL):name = "動態波浪"def render_scene(self):t = time.time()N = 22for i in range(N):glBegin(GL_TRIANGLE_STRIP)for j in range(N+1):x = -2 + 4*i/Ny = -2 + 4*j/Nz1 = math.sin(x*2+y*2+t*2)*0.6z2 = math.sin((x+4/N)*2+y*2+t*2)*0.6glColor3f(0.5+0.5*math.sin(t+i), 0.6+0.4*math.cos(t+j), 0.6+0.4*math.cos(i+j))glVertex3f(x, y, z1)glVertex3f(x+4/N, y, z2)glEnd()class AnimatedSpiralGL(Base3DGL):name = "動畫螺旋"def render_scene(self):t = time.time()glBegin(GL_LINE_STRIP)for idx in range(120):ang = idx/20 + tr = 1.1 + 0.2*math.sin(idx*6+t)x = math.cos(ang)*ry = math.sin(ang)*rz = math.sin(t + idx/24)*0.6glColor3f(abs(math.sin(ang)), abs(math.cos(idx)), abs(z))glVertex3f(x, y, z)glEnd()class AnimatedWaveSurfaceGL(Base3DGL):name = "動畫波浪面"def render_scene(self):t = time.time()N = 18for i in range(N):glBegin(GL_TRIANGLE_STRIP)for j in range(N+1):x = -2 + 4*i/Ny = -2 + 4*j/Nz1 = math.sin(x*3+y*3+t)*0.5z2 = math.sin((x+4/N)*3+y*3+t)*0.5glColor3f(abs(math.sin(t+x)), abs(math.cos(t+y)), abs(z1))glVertex3f(x, y, z1)glVertex3f(x+4/N, y, z2)glEnd()class AnimatedTwistGL(Base3DGL):name = "動態扭曲體"def render_scene(self):t = time.time()N = 20for i in range(N):glBegin(GL_TRIANGLE_STRIP)for j in range(N+1):x = -2 + 4*i/Ny = -2 + 4*j/Nz1 = math.sin(x*3+y*3+t*1.5+i*0.2+j*0.2)*0.3z2 = math.sin((x+4/N)*3+y*3+t*1.5+i*0.2+j*0.2)*0.3glColor3f(0.5+0.5*math.sin(t+i), 0.6+0.4*math.cos(t+j), 0.8+0.2*math.cos(i+j))glVertex3f(x, y, z1)glVertex3f(x+4/N, y, z2)glEnd()class AnimatedSphereGL(Base3DGL):name = "動態呼吸球"def render_scene(self):t = time.time()for i in range(30):lat0 = math.pi * (-0.5 + float(i) / 30)z0 = math.sin(lat0)zr0 = math.cos(lat0)lat1 = math.pi * (-0.5 + float(i + 1) / 30)z1 = math.sin(lat1)zr1 = math.cos(lat1)glBegin(GL_QUAD_STRIP)for j in range(32):lng = 2 * math.pi * float(j) / 32x = math.cos(lng)y = math.sin(lng)s = 1.1 + 0.25*math.sin(t+j*0.13+i*0.11)glColor3f(abs(x), abs(y), abs(z0))glVertex3f(x * zr0 * s, y * zr0 * s, z0 * s)glVertex3f(x * zr1 * s, y * zr1 * s, z1 * s)glEnd()class AnimatedTwistTorusGL(Base3DGL):name = "動態扭曲圓環"def render_scene(self):t = time.time()R, r = 1.0, 0.36N, M = 32, 18for i in range(N):glBegin(GL_QUAD_STRIP)for j in range(M+1):for k in [0, 1]:s = 2 * math.pi * (i + k) / Nt1 = 2 * math.pi * j / M + 0.5*math.sin(t+s)x = (R + r * math.cos(t1)) * math.cos(s)y = (R + r * math.cos(t1)) * math.sin(s)z = r * math.sin(t1)glColor3f(abs(math.cos(s)), abs(math.sin(t1)), abs(math.sin(s)))glVertex3f(x, y, z)glEnd()class AnimatedExplosionGL(Base3DGL):name = "動態爆炸球"def render_scene(self):t = time.time()for i in range(20, 30):lat0 = math.pi * (-0.5 + float(i) / 30)z0 = math.sin(lat0)zr0 = math.cos(lat0)lat1 = math.pi * (-0.5 + float(i+1) / 30)z1 = math.sin(lat1)zr1 = math.cos(lat1)glBegin(GL_LINE_STRIP)for j in range(32):lng = 2 * math.pi * float(j) / 32x = math.cos(lng)y = math.sin(lng)f = 1.0+0.6*math.sin(t*2+i+j)glColor3f(abs(x*f), abs(y*f), abs(z0*f))glVertex3f(x * zr0*f, y * zr0*f, z0*f)glEnd()class AnimatedEggGL(Base3DGL):name = "動態蛋形"def render_scene(self):t = time.time()N = 24for i in range(N):lat0 = math.pi * (-0.5 + float(i) / N)z0 = math.sin(lat0)*1.2 + 0.4*math.sin(lat0+t)**3zr0 = math.cos(lat0)lat1 = math.pi * (-0.5 + float(i+1) / N)z1 = math.sin(lat1)*1.2 + 0.4*math.sin(lat1+t)**3zr1 = math.cos(lat1)glBegin(GL_QUAD_STRIP)for j in range(N+1):lng = 2 * math.pi * float(j) / Nx = math.cos(lng)y = math.sin(lng)glColor3f((j%3)/2, (i%5)/4, (j%7)/6)glVertex3f(x * zr0, y * zr0, z0)glVertex3f(x * zr1, y * zr1, z1)glEnd()class AnimatedRippleGL(Base3DGL):name = "動畫漣漪"def render_scene(self):t = time.time()N = 30for i in range(N):glBegin(GL_LINE_STRIP)for j in range(N+1):r = 0.2 + 1.6 * i/Ntheta = 2*math.pi*j/Nz = math.sin(r*4-theta*2+t*2)*0.3glColor3f(abs(math.sin(r+t)), abs(math.cos(theta)), abs(z))glVertex3f(r*math.cos(theta), r*math.sin(theta), z)glEnd()# ----------- 第4頁(極坐標/分形/特殊體) -----------
class HelixGL(Base3DGL):name = "三色螺旋彈簧"def render_scene(self):glBegin(GL_LINE_STRIP)for i in range(300):t = i/50x = math.cos(t)*1.2y = math.sin(t)*1.2z = t/4glColor3f(abs(math.sin(t)), abs(math.cos(t)), abs(z)%1)glVertex3f(x, y, z)glEnd()class HyperboloidGL(Base3DGL):name = "雙曲面"def render_scene(self):N = 30for i in range(N):glBegin(GL_LINE_LOOP)for j in range(N):u = i*2*math.pi/Nv = j*2*math.pi/Nx = math.cosh(v)*math.cos(u)y = math.cosh(v)*math.sin(u)z = math.sinh(v)glColor3f(abs(math.sin(u)), abs(math.cos(v)), abs(z)%1)glVertex3f(x/6, y/6, z/6)glEnd()class ParaboloidGL(Base3DGL):name = "拋物面"def render_scene(self):N = 30for i in range(N):glBegin(GL_LINE_LOOP)for j in range(N):u = i*2*math.pi/Nv = j*2/Nx = v*math.cos(u)y = v*math.sin(u)z = v**2glColor3f(abs(math.sin(u)), abs(math.cos(v)), abs(z)%1)glVertex3f(x, y, z/4)glEnd()class AstroidGL(Base3DGL):name = "阿斯托洛伊德"def render_scene(self):glBegin(GL_LINE_STRIP)for i in range(400):t = 2 * math.pi * i / 400x = math.cos(t)**3y = math.sin(t)**3z = math.cos(3*t)glColor3f(abs(x), abs(y), abs(z))glVertex3f(x, y, z/2)glEnd()class SuperquadricGL(Base3DGL):name = "超二次曲面"def render_scene(self):N = 32e = 0.5for i in range(N):u = -math.pi/2 + i*math.pi/Nu2 = -math.pi/2 + (i+1)*math.pi/NglBegin(GL_QUAD_STRIP)for j in range(N+1):v = -math.pi + 2*j*math.pi/Nx1 = math.copysign(abs(math.cos(u))**e, math.cos(u))*math.copysign(abs(math.cos(v))**e, math.cos(v))y1 = math.copysign(abs(math.cos(u))**e, math.cos(u))*math.copysign(abs(math.sin(v))**e, math.sin(v))z1 = math.copysign(abs(math.sin(u))**e, math.sin(u))x2 = math.copysign(abs(math.cos(u2))**e, math.cos(u2))*math.copysign(abs(math.cos(v))**e, math.cos(v))y2 = math.copysign(abs(math.cos(u2))**e, math.cos(u2))*math.copysign(abs(math.sin(v))**e, math.sin(v))z2 = math.copysign(abs(math.sin(u2))**e, math.sin(u2))glColor3f(abs(x1), abs(y1), abs(z1))glVertex3f(x1, y1, z1)glColor3f(abs(x2), abs(y2), abs(z2))glVertex3f(x2, y2, z2)glEnd()class ButterflyGL(Base3DGL):name = "蝴蝶線"def render_scene(self):glBegin(GL_LINE_STRIP)for i in range(800):t = i/50x = math.sin(t)*(math.exp(math.cos(t)) - 2*math.cos(4*t) - math.sin(t/12)**5)y = math.cos(t)*(math.exp(math.cos(t)) - 2*math.cos(4*t) - math.sin(t/12)**5)z = math.sin(3*t)glColor3f(abs(x)%1, abs(y)%1, abs(z)%1)glVertex3f(x/3, y/3, z/2)glEnd()class LissajousGL(Base3DGL):name = "利薩茹空間曲線"def render_scene(self):glBegin(GL_LINE_STRIP)for i in range(800):t = i/100x = math.sin(3*t + math.pi/2)y = math.sin(4*t)z = math.sin(5*t)glColor3f(abs(x), abs(y), abs(z))glVertex3f(x, y, z)glEnd()class VivianiGL(Base3DGL):name = "維維亞尼曲線"def render_scene(self):glBegin(GL_LINE_STRIP)for i in range(800):t = 2*math.pi*i/800x = 1 + math.cos(t)y = math.sin(t)z = 2*math.sin(t/2)glColor3f(abs(x), abs(y), abs(z/2))glVertex3f(x/2, y/2, z/2)glEnd()class HypocycloidGL(Base3DGL):name = "內擺線"def render_scene(self):glBegin(GL_LINE_STRIP)for i in range(800):t = 2*math.pi*i/800x = 2*math.cos(t) + math.cos(2*t)y = 2*math.sin(t) - math.sin(2*t)z = math.sin(3*t)glColor3f(abs(x/2), abs(y/2), abs(z/2))glVertex3f(x/2, y/2, z/2)glEnd()class EpicycloidGL(Base3DGL):name = "外擺線"def render_scene(self):glBegin(GL_LINE_STRIP)for i in range(800):t = 2*math.pi*i/800x = 2*math.cos(t) - math.cos(2*t)y = 2*math.sin(t) - math.sin(2*t)z = math.cos(3*t)glColor3f(abs(x/2), abs(y/2), abs(z/2))glVertex3f(x/2, y/2, z/2)glEnd()# ----------- 第5頁(分形/變幻/多樣體) -----------
class FernGL(Base3DGL):name = "分形蕨葉"def render_scene(self):points = []x, y = 0, 0for i in range(5000):r = np.random.random()if r < 0.01:x, y = 0, 0.16*yelif r < 0.86:x, y = 0.85*x + 0.04*y, -0.04*x + 0.85*y + 1.6elif r < 0.93:x, y = 0.2*x - 0.26*y, 0.23*x + 0.22*y + 1.6else:x, y = -0.15*x + 0.28*y, 0.26*x + 0.24*y + 0.44points.append((x, y))glBegin(GL_POINTS)for px, py in points:glColor3f(0, 0.6+0.3*px, 0.2+0.5*py)glVertex3f((px-2)*0.5, (py-3)*0.5, 0)glEnd()class SierpinskiGL(Base3DGL):name = "謝爾賓斯基地毯"def render_scene(self):def draw_carpet(x, y, s, depth):if depth == 0:glColor3f(1,0.6,0.2)glVertex3f(x, y, 0)returns /= 3for dx in [-s, 0, s]:for dy in [-s, 0, s]:if dx != 0 or dy != 0:draw_carpet(x+dx, y+dy, s, depth-1)glBegin(GL_POINTS)draw_carpet(0,0,1.0,4)glEnd()class KochCurveGL(Base3DGL):name = "科赫雪花"def render_scene(self):def koch(p1, p2, depth):if depth == 0:glColor3f(0.2,0.5,1)glVertex3f(*p1)glVertex3f(*p2)else:p3 = ((2*p1[0]+p2[0])/3, (2*p1[1]+p2[1])/3, 0)p4 = ((p1[0]+2*p2[0])/3, (p1[1]+2*p2[1])/3, 0)ang = math.atan2(p2[1]-p1[1], p2[0]-p1[0]) - math.pi/3px = p3[0] + math.cos(ang)*(abs(p4[0]-p3[0]))py = p3[1] + math.sin(ang)*(abs(p4[0]-p3[0]))p5 = (px, py, 0)koch(p1, p3, depth-1)koch(p3, p5, depth-1)koch(p5, p4, depth-1)koch(p4, p2, depth-1)glBegin(GL_LINES)koch((-0.8,0.5,0), (0.8,0.5,0), 3)koch((0.8,0.5,0), (0,-0.8,0), 3)koch((0,-0.8,0), (-0.8,0.5,0), 3)glEnd()class FractalTreeGL(Base3DGL):name = "分形樹"def render_scene(self):def draw_tree(x, y, angle, depth):if depth == 0: returnx2 = x + math.cos(angle)*depth*0.11y2 = y + math.sin(angle)*depth*0.11glColor3f(0.3,0.2+0.08*depth,0.1+0.03*depth)glVertex3f(x, y, 0)glVertex3f(x2, y2, 0)draw_tree(x2, y2, angle-math.pi/6, depth-1)draw_tree(x2, y2, angle+math.pi/6, depth-1)glBegin(GL_LINES)draw_tree(0,-0.7,math.pi/2,6)glEnd()class DragonCurveGL(Base3DGL):name = "龍曲線"def render_scene(self):def dragon(p1, p2, depth):if depth == 0:glColor3f(0.7,0.2,1)glVertex3f(*p1)glVertex3f(*p2)else:mx = (p1[0]+p2[0])/2 + (p2[1]-p1[1])/2my = (p1[1]+p2[1])/2 - (p2[0]-p1[0])/2m = (mx,my,0)dragon(p1, m, depth-1)dragon(p2, m, depth-1)glBegin(GL_LINES)dragon((-0.7,0,0), (0.7,0,0), 12)glEnd()class JuliaGL(Base3DGL):name = "Julia分形"def render_scene(self):glBegin(GL_POINTS)for ix in range(120):for iy in range(120):x = (ix-60)/40y = (iy-60)/40zx, zy = x, ycX, cY = -0.7, 0.27015n, max_n = 0, 30while zx*zx + zy*zy < 4 and n < max_n:tmp = zx*zx - zy*zy + cXzy, zx = 2*zx*zy + cY, tmpn += 1glColor3f(n/max_n, 0.2+0.6*n/max_n, 1-n/max_n)glVertex3f(x, y, 0)glEnd()class MandelbrotGL(Base3DGL):name = "Mandelbrot分形"def render_scene(self):glBegin(GL_POINTS)for ix in range(120):for iy in range(120):x0 = (ix-60)/40y0 = (iy-60)/40x, y = 0, 0n, max_n = 0, 30while x*x + y*y < 4 and n < max_n:xt = x*x - y*y + x0y = 2*x*y + y0x = xtn += 1glColor3f(n/max_n, 1-n/max_n, 0.7*n/max_n)glVertex3f(x0, y0, 0)glEnd()class PeanoGL(Base3DGL):name = "皮亞諾曲線"def render_scene(self):def peano(x, y, dx, dy, depth):if depth == 0:glColor3f(0.3+0.6*x,0.7-0.3*y,0.5+0.3*y)glVertex3f(x, y, 0)returnfor i in [0,1,2]:for j in [0,1,2]:peano(x+i*dx/3, y+j*dy/3, dx/3, dy/3, depth-1)glBegin(GL_POINTS)peano(-0.7,-0.7,1.4,1.4,3)glEnd()class CantorGL(Base3DGL):name = "康托三分集"def render_scene(self):def cantor(x, y, l, depth):if depth == 0:glColor3f(0.8,0.8,0.2)glVertex3f(x, y, 0)glVertex3f(x+l, y, 0)returnglColor3f(0.8,0.5,0.1+0.1*depth)glVertex3f(x, y, 0)glVertex3f(x+l, y, 0)cantor(x, y-0.1, l/3, depth-1)cantor(x+2*l/3, y-0.1, l/3, depth-1)glBegin(GL_LINES)cantor(-0.8, 0.8, 1.6, 6)glEnd()class PolygonFanGL(Base3DGL):name = "漸變扇形多邊形"def render_scene(self):glBegin(GL_TRIANGLE_FAN)glColor3f(1,1,1)glVertex3f(0,0,0)for i in range(31):ang = 2*math.pi*i/30glColor3f(abs(math.sin(ang)), abs(math.cos(ang)), 0.5+0.5*math.sin(ang*3))glVertex3f(math.cos(ang), math.sin(ang), 0)glEnd()# ========== 工廠函數 ==========
def get_3d_widget_by_index(idx):page1 = [SphereGL, CubeGL, OctahedronGL, IcosahedronGL, DodecahedronGL,CylinderGL, ConeGL, TorusGL, SpiralGL, WaveSurfaceGL]page2 = [HeartSurfaceGL, SineWaveGL, SaddleGL, EggShapeGL, KleinBottleGL,StarGL, MobiusGL, RippleGL, RoseGL, TrefoilGL]page3 = [BubbleGL, AnimatedWaveGL, AnimatedSpiralGL, AnimatedWaveSurfaceGL, AnimatedTwistGL,AnimatedSphereGL, AnimatedTwistTorusGL, AnimatedExplosionGL, AnimatedEggGL, AnimatedRippleGL]page4 = [HelixGL, HyperboloidGL, ParaboloidGL, AstroidGL, SuperquadricGL,ButterflyGL, LissajousGL, VivianiGL, HypocycloidGL, EpicycloidGL]page5 = [FernGL, SierpinskiGL, KochCurveGL, FractalTreeGL, DragonCurveGL,JuliaGL, MandelbrotGL, PeanoGL, CantorGL, PolygonFanGL]allcls = page1 + page2 + page3 + page4 + page5return allcls[idx]()# ========== 主窗口 ==========
class All3DEffectsGLDemo(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("50種3D效果合集(GPU+FPS+鼠標旋轉)")self.setGeometry(100, 60, 1600, 900)self.init_ui()def init_ui(self):widget = QWidget()vbox = QVBoxLayout(widget)self.setCentralWidget(widget)title = QLabel("🌈 50種3D效果演示 (OpenGL+FPS實時) 🌈")title.setFont(QFont("微軟雅黑", 24, QFont.Bold))title.setAlignment(Qt.AlignCenter)title.setStyleSheet("""color:#43e97b; background: #191a25;border-radius:14px; margin:10px; padding:20px;border: 2px solid #647dee;""")vbox.addWidget(title)tabs = QTabWidget()tabs.setTabPosition(QTabWidget.West)tabs.setStyleSheet("""QTabBar::tab {background: #232338;color: #a3e0ff;font: 16px "微軟雅黑";border: 2px solid #647dee;border-radius: 12px;margin: 6px;min-width: 110px;min-height: 50px;padding: 8px 18px;}QTabBar::tab:selected {background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #38f9d7);color: #161623;font: bold 18px "微軟雅黑";border: 3px solid #43e97b;}QTabBar::tab:hover {background: #304674;color: #fff;}QTabWidget::pane {border: 2px solid #647dee;border-radius: 16px;margin: 8px;}""")vbox.addWidget(tabs)self.widget_list = []for page in range(5):tab = QWidget()grid = QGridLayout(tab)for i in range(10):idx = page*10 + iframe = QFrame()frame.setFrameShape(QFrame.StyledPanel)frame.setStyleSheet("background:#22242e; border-radius:18px;")vlay = QVBoxLayout(frame)w = get_3d_widget_by_index(idx)self.widget_list.append(w)vlay.setContentsMargins(2,2,2,2)vlay.addWidget(w)fps_label = QLabel()fps_label.setFont(QFont("微軟雅黑", 10))fps_label.setStyleSheet("color:#43e97b; margin-left:3px;")fps_label.setAlignment(Qt.AlignLeft)vlay.addWidget(fps_label)frame.fps_label = fps_labelframe.widget = wgrid.addWidget(frame, i//5, i%5)tabs.addTab(tab, f"第{page+1}頁")for i in range(5): grid.setColumnStretch(i, 1)for i in range(2): grid.setRowStretch(i, 1)self.fps_timer = QTimer(self)self.fps_timer.timeout.connect(self.update_fps_labels)self.fps_timer.start(400)def update_fps_labels(self):for tab_index in range(5):tab = self.centralWidget().layout().itemAt(1).widget().widget(tab_index)grid = tab.layout()for i in range(10):frame = grid.itemAt(i).widget()w = frame.widgetframe.fps_label.setText(f"{w.name}\nFPS: {w.fps:.1f}")if __name__ == '__main__':app = QApplication(sys.argv)app.setStyle("Fusion")palette = QPalette()palette.setColor(QPalette.Window, QColor("#191a25"))palette.setColor(QPalette.WindowText, QColor("#e0eafc"))app.setPalette(palette)win = All3DEffectsGLDemo()win.show()sys.exit(app.exec_())