效果:
一、只需打开命令行(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 = None
self.angle_x, self.angle_y = 20, 20
self.fps = 0
self._frames = 0
self._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 += 1
now = time.time()
if now - self._last_time > 1.0:
self.fps = self._frames / (now - self._last_time)
self._last_time = now
self._frames = 0
glClear(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.7
self.angle_x += dy * 0.7
self.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) / 32
x = 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.0
verts = [
(-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.6
glVertex3f(v[0]*s, v[1]*s, v[2]*s)
glEnd()
class DodecahedronGL(Base3DGL):
name = "炫彩十二面体"
def render_scene(self):
phi = (1 + 5 ** 0.5) / 2
a, b = 1, 1/phi
verts = [
[+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 = 32
for 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/N
x = 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/N
glVertex3f(math.cos(ang), math.sin(ang), z)
glEnd()
class ConeGL(Base3DGL):
name = "三色圆锥体"
def render_scene(self):
N = 32
for 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/N
glVertex3f(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/N
glVertex3f(math.cos(ang), math.sin(ang), -1)
glEnd()
class TorusGL(Base3DGL):
name = "彩虹圆环体"
def render_scene(self):
R, r = 1.0, 0.36
N, M = 32, 18
for 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) / N
t = 2 * math.pi * j / M
x = (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)*r
y = math.sin(t)*r
z = -1.1 + t/(6*math.pi)*2.2
glColor3f(abs(x), abs(y), abs(z))
glVertex3f(x, y, z)
glEnd()
class WaveSurfaceGL(Base3DGL):
name = "渐变波浪面"
def render_scene(self):
N = 20
for i in range(N):
glBegin(GL_TRIANGLE_STRIP)
for j in range(N+1):
x = -2 + 4*i/N
y = -2 + 4*j/N
z1 = math.sin(x*3+y*3+self.angle_x*0.05)*0.5
z2 = math.sin((x+4/N)*3+y*3+self.angle_y*0.06)*0.5
glColor3f(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)**3
y = 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) / 4
glColor3f(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 = 40
for i in range(N):
glBegin(GL_TRIANGLE_STRIP)
for j in range(N+1):
x = -2 + 4*i/N
y = -2 + 4*j/N
z1 = math.sin(x*4+y*4)*0.7
z2 = math.sin((x+4/N)*4+y*4)*0.7
glColor3f(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 = 25
for i in range(N):
glBegin(GL_TRIANGLE_STRIP)
for j in range(N+1):
x = -2 + 4*i/N
y = -2 + 4*j/N
z1 = (x**2 - y**2)/4
z2 = ((x+4/N)**2 - y**2)/4
glColor3f(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 = 24
for i in range(N):
lat0 = math.pi * (-0.5 + float(i) / N)
z0 = math.sin(lat0)*1.2 + 0.4*math.sin(lat0)**3
zr0 = math.cos(lat0)
lat1 = math.pi * (-0.5 + float(i+1) / N)
z1 = math.sin(lat1)*1.2 + 0.4*math.sin(lat1)**3
zr1 = math.cos(lat1)
glBegin(GL_QUAD_STRIP)
for j in range(N+1):
lng = 2 * math.pi * float(j) / N
x = 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 = 32
for i in range(N):
glBegin(GL_QUAD_STRIP)
for j in range(N+1):
u = 2*math.pi*i/N
v = 2*math.pi*j/N
x = (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.pi
r = 1.2 if i%2==0 else 0.6
glColor3f((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 = 60
w = 0.2
for i in range(N):
glBegin(GL_QUAD_STRIP)
for j in range(2):
phi = 2*math.pi*i/N
t = -w + 2*w*j
x = 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 = 30
for i in range(N):
glBegin(GL_LINE_STRIP)
for j in range(N+1):
r = 0.2 + 1.6 * i/N
theta = 2*math.pi*j/N
z = math.sin(r*4-theta*2)*0.3
glColor3f(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.pi
r = math.sin(3*theta)*1.5
x = 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/400
x = 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/32
x, 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 = 22
for i in range(N):
glBegin(GL_TRIANGLE_STRIP)
for j in range(N+1):
x = -2 + 4*i/N
y = -2 + 4*j/N
z1 = math.sin(x*2+y*2+t*2)*0.6
z2 = math.sin((x+4/N)*2+y*2+t*2)*0.6
glColor3f(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 + t
r = 1.1 + 0.2*math.sin(idx*6+t)
x = math.cos(ang)*r
y = math.sin(ang)*r
z = math.sin(t + idx/24)*0.6
glColor3f(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 = 18
for i in range(N):
glBegin(GL_TRIANGLE_STRIP)
for j in range(N+1):
x = -2 + 4*i/N
y = -2 + 4*j/N
z1 = math.sin(x*3+y*3+t)*0.5
z2 = math.sin((x+4/N)*3+y*3+t)*0.5
glColor3f(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 = 20
for i in range(N):
glBegin(GL_TRIANGLE_STRIP)
for j in range(N+1):
x = -2 + 4*i/N
y = -2 + 4*j/N
z1 = math.sin(x*3+y*3+t*1.5+i*0.2+j*0.2)*0.3
z2 = math.sin((x+4/N)*3+y*3+t*1.5+i*0.2+j*0.2)*0.3
glColor3f(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) / 32
x = 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.36
N, M = 32, 18
for 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) / N
t1 = 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) / 32
x = 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 = 24
for i in range(N):
lat0 = math.pi * (-0.5 + float(i) / N)
z0 = math.sin(lat0)*1.2 + 0.4*math.sin(lat0+t)**3
zr0 = math.cos(lat0)
lat1 = math.pi * (-0.5 + float(i+1) / N)
z1 = math.sin(lat1)*1.2 + 0.4*math.sin(lat1+t)**3
zr1 = math.cos(lat1)
glBegin(GL_QUAD_STRIP)
for j in range(N+1):
lng = 2 * math.pi * float(j) / N
x = 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 = 30
for i in range(N):
glBegin(GL_LINE_STRIP)
for j in range(N+1):
r = 0.2 + 1.6 * i/N
theta = 2*math.pi*j/N
z = math.sin(r*4-theta*2+t*2)*0.3
glColor3f(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/50
x = math.cos(t)*1.2
y = math.sin(t)*1.2
z = t/4
glColor3f(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 = 30
for i in range(N):
glBegin(GL_LINE_LOOP)
for j in range(N):
u = i*2*math.pi/N
v = j*2*math.pi/N
x = 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 = 30
for i in range(N):
glBegin(GL_LINE_LOOP)
for j in range(N):
u = i*2*math.pi/N
v = j*2/N
x = v*math.cos(u)
y = v*math.sin(u)
z = v**2
glColor3f(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 / 400
x = math.cos(t)**3
y = math.sin(t)**3
z = 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 = 32
e = 0.5
for i in range(N):
u = -math.pi/2 + i*math.pi/N
u2 = -math.pi/2 + (i+1)*math.pi/N
glBegin(GL_QUAD_STRIP)
for j in range(N+1):
v = -math.pi + 2*j*math.pi/N
x1 = 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/50
x = 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/100
x = 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/800
x = 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/800
x = 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/800
x = 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, 0
for i in range(5000):
r = np.random.random()
if r < 0.01:
x, y = 0, 0.16*y
elif r < 0.86:
x, y = 0.85*x + 0.04*y, -0.04*x + 0.85*y + 1.6
elif r < 0.93:
x, y = 0.2*x - 0.26*y, 0.23*x + 0.22*y + 1.6
else:
x, y = -0.15*x + 0.28*y, 0.26*x + 0.24*y + 0.44
points.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)
return
s /= 3
for 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/3
px = 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: return
x2 = x + math.cos(angle)*depth*0.11
y2 = y + math.sin(angle)*depth*0.11
glColor3f(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])/2
my = (p1[1]+p2[1])/2 - (p2[0]-p1[0])/2
m = (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)/40
y = (iy-60)/40
zx, zy = x, y
cX, cY = -0.7, 0.27015
n, max_n = 0, 30
while zx*zx + zy*zy < 4 and n < max_n:
tmp = zx*zx - zy*zy + cX
zy, zx = 2*zx*zy + cY, tmp
n += 1
glColor3f(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)/40
y0 = (iy-60)/40
x, y = 0, 0
n, max_n = 0, 30
while x*x + y*y < 4 and n < max_n:
xt = x*x - y*y + x0
y = 2*x*y + y0
x = xt
n += 1
glColor3f(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)
return
for 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)
return
glColor3f(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/30
glColor3f(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 + page5
return 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 + i
frame = 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_label
frame.widget = w
grid.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.widget
frame.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_())