1. 计算机图形学实验概述
作为一名计算机图形学课程的实践环节,山东大学软件学院的这门实验课程设计了多个富有挑战性的项目。实验分为三个小型实验和两个大型综合实验,学生需要从中各选一个完成。小型实验包括Mesh网格处理、光线追踪算法实现和B样条曲线绘制;大型实验则是太阳系天体系统模拟和汽车运动模拟。
我最终选择了Mesh网格处理和太阳系模拟这两个实验。Mesh网格作为三维图形的基础数据结构,掌握其原理和操作方法对后续图形编程至关重要;而太阳系模拟则能综合运用坐标系变换、光照模型等核心图形学知识,实现一个完整的动态场景。
提示:在开始实验前,强烈建议先熟悉Python环境和OpenGL库的基本使用。实验代码托管在GitHub仓库,采用conda管理依赖环境,这对保证实验可复现性非常重要。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 实验环境配置与准备
2.1 Conda虚拟环境搭建
由于图形学实验依赖特定版本的Python库,使用conda创建隔离的虚拟环境是最佳实践。我推荐安装Miniconda3作为基础环境管理工具:
bash复制# 下载并安装Miniconda(以Linux为例)
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
# 创建专用环境
conda create -n sdu_cg python=3.8
conda activate sdu_cg
环境创建后,需要安装以下核心依赖库:
- PyOpenGL:Python绑定的OpenGL接口
- PyOpenGL-accelerate:OpenGL加速模块
- numpy:矩阵运算支持
- glfw:窗口系统管理
bash复制pip install PyOpenGL PyOpenGL-accelerate numpy glfw
2.2 实验代码获取与验证
实验代码已开源在GitHub仓库,可以通过以下命令克隆到本地:
bash复制git clone https://github.com/Ting-Song-q/SDU_CG_exp.git
cd SDU_CG_exp
代码仓库包含多个实验的独立目录,每个实验都有详细的README说明文件。建议先运行示例代码验证环境配置是否正确:
bash复制python examples/simple_triangle.py
如果能看到一个彩色三角形在窗口中渲染,说明基础环境已配置成功。
3. Mesh网格处理实验详解
3.1 网格数据结构基础
Mesh网格是三维图形中最常用的几何表示方法,由顶点(Vertex)、边(Edge)和面(Face)组成。实验中我们主要处理三角网格,即每个面由三个顶点构成。
在OpenGL中,网格数据通常以顶点数组和索引数组的形式存储:
python复制vertices = [
# 位置坐标(x,y,z) 颜色(r,g,b)
[-0.5, -0.5, 0.0, 1.0, 0.0, 0.0],
[0.5, -0.5, 0.0, 0.0, 1.0, 0.0],
[0.0, 0.5, 0.0, 0.0, 0.0, 1.0]
]
indices = [0, 1, 2] # 定义三角形绘制顺序
3.2 网格加载与渲染
实验提供了基本的网格加载函数,支持.obj格式的模型文件。关键实现代码如下:
python复制def load_obj(file_path):
vertices = []
faces = []
with open(file_path) as f:
for line in f:
if line.startswith('v '):
vertices.append(list(map(float, line[2:].split())))
elif line.startswith('f '):
faces.append([int(i.split('/')[0])-1 for i in line[2:].split()])
return vertices, faces
渲染网格时需要注意:
- 启用深度测试避免渲染顺序问题
- 设置适当的投影矩阵
- 绑定顶点缓冲对象(VBO)和索引缓冲对象(EBO)
python复制glEnable(GL_DEPTH_TEST)
# 设置透视投影
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, (width/height), 0.1, 50.0)
# 绑定缓冲
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)
3.3 网格操作与变形
实验要求实现基础的网格变形操作,包括平移、旋转和缩放。这些操作通过变换矩阵实现:
python复制def translate(mesh, dx, dy, dz):
translation = np.array([
[1, 0, 0, dx],
[0, 1, 0, dy],
[0, 0, 1, dz],
[0, 0, 0, 1]
])
return np.dot(mesh.vertices, translation.T)
更复杂的变形如波浪效果可以通过顶点着色器实时计算:
glsl复制#version 330 core
uniform float time;
in vec3 position;
void main() {
float wave = sin(position.x * 2.0 + time) * 0.1;
gl_Position = vec4(position.x, position.y + wave, position.z, 1.0);
}
4. 太阳系模拟实验实现
4.1 天体系统建模
太阳系模拟需要构建包含太阳、行星和卫星的层级系统。每个天体抽象为包含以下属性的对象:
python复制class CelestialBody:
def __init__(self, radius, distance, orbit_period, rotation_period, color):
self.radius = radius # 天体半径
self.distance = distance # 轨道半径
self.orbit_period = orbit_period # 公转周期(天)
self.rotation_period = rotation_period # 自转周期(天)
self.color = color # 表面颜色
self.angle = 0 # 当前轨道角度
self.rotation = 0 # 当前自转角度
天体间的层级关系通过父子节点表示,例如地球作为太阳的子节点,月球作为地球的子节点。
4.2 坐标系变换实现
太阳系模拟的核心是正确应用坐标系变换。OpenGL采用模型-视图-投影(Model-View-Projection)矩阵体系:
- 首先绘制太阳(位于坐标系原点)
- 对每个行星:
- 应用公转变换(绕太阳旋转)
- 应用轨道平移(将行星移到正确距离)
- 应用自转变换
- 绘制行星
- 对每个卫星:
- 先应用父行星的所有变换
- 再应用卫星自身的变换
python复制def draw_planet(planet):
glPushMatrix()
# 公转
glRotatef(planet.angle, 0, 1, 0)
# 轨道位置
glTranslatef(planet.distance, 0, 0)
# 自转
glRotatef(planet.rotation, 0, 1, 0)
# 绘制球体
glColor3f(*planet.color)
quad = gluNewQuadric()
gluSphere(quad, planet.radius, 32, 32)
glPopMatrix()
4.3 光照与材质效果
为了增强真实感,实验需要实现基本的光照模型。太阳作为光源,行星表面反射光线:
python复制def setup_lighting():
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
# 太阳光源位置和颜色
glLightfv(GL_LIGHT0, GL_POSITION, [0, 0, 0, 1])
glLightfv(GL_LIGHT0, GL_DIFFUSE, [1, 1, 1, 1])
# 启用颜色材质
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)
行星材质属性可以这样设置:
python复制glMaterialfv(GL_FRONT, GL_SPECULAR, [0.5, 0.5, 0.5, 1])
glMaterialf(GL_FRONT, GL_SHININESS, 50)
5. 实验调试与优化技巧
5.1 常见问题排查
-
黑屏无显示:
- 检查视口设置是否正确
- 确认投影矩阵和模型视图矩阵已正确初始化
- 验证着色器程序是否编译成功
-
模型显示异常:
- 检查顶点数据是否上传正确
- 确认顶点属性指针配置正确
- 验证索引缓冲区是否绑定
-
光照效果不明显:
- 确认法线数据是否正确
- 检查材质属性设置
- 验证光源位置和方向
5.2 性能优化建议
- 批量绘制:将多个对象的绘制调用合并,减少OpenGL状态切换
- 显示列表:对静态对象使用显示列表缓存绘制命令
- 细节层次(LOD):根据距离动态调整模型精度
- 视锥剔除:只绘制在视野范围内的对象
python复制# 显示列表示例
sphere_dl = glGenLists(1)
glNewList(sphere_dl, GL_COMPILE)
quad = gluNewQuadric()
gluSphere(quad, 1.0, 32, 32)
glEndList()
# 绘制时直接调用显示列表
glCallList(sphere_dl)
6. 实验扩展与进阶方向
完成基础实验后,可以考虑以下扩展方向:
- 添加纹理贴图:为行星表面添加真实纹理
- 实现阴影效果:使用阴影映射技术
- 引入粒子系统:模拟彗尾或星环
- 支持交互操作:通过鼠标键盘控制视角
纹理贴图实现示例:
python复制def load_texture(file):
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
image = Image.open(file)
img_data = np.array(list(image.getdata()), np.uint8)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
image.width, image.height,
0, GL_RGB, GL_UNSIGNED_BYTE, img_data)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
return texture
在图形学实验中,理解底层原理比单纯实现功能更重要。建议在完成每个实验后,思考以下问题:
- 这种实现方式的优缺点是什么?
- 在性能上有哪些瓶颈?
- 如何改进现有实现?
