鱼C论坛

 找回密码
 立即注册
查看: 382|回复: 2

[已解决]请教一个关于python渲染可交互3D模型的问题

[复制链接]
发表于 2024-8-1 11:26:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
首先说一下想要实现的最终目标:
创建一个窗口,窗口内渲染人物的3D模型,可以做出随机动作、换装及变换表情;

目前遇到的问题和疑问:
1、目前渲染模型选用的方案是 pygame + OpenGL,但是OpenGL这个库太老了,相关的文档也不多,不知道有没有其他更流行的方案;
2、现在渲染出的模型没有蒙皮,只有一堆白色的顶点。。。。。
3、随机动作和换装还不知道应该如何实现,有没有大神给指明一条道路,感谢!!!!

总结:如果想要实现对3D模型更好的控制,我该了解一些什么,或者有没有什么比较流行的库,感谢!!!

目前的代码如下:


  1. import pygame
  2. from pygame.locals import *  # 导入 Pygame 本地常量
  3. from OpenGL.GL import *  # 导入 OpenGL 函数
  4. from OpenGL.GLU import *  # 导入 OpenGL 工具库函数
  5. import random  # 导入随机数生成模块
  6. import numpy as np  # 导入 NumPy 数组处理库


  7. # 加载 OBJ 文件函数
  8. def load_obj(filename):
  9.     vertices = []  # 顶点列表
  10.     faces = []  # 面列表
  11.     with open(filename) as f:  # 打开 OBJ 文件
  12.         for line in f:  # 逐行读取文件
  13.             if line.startswith("v "):  # 如果是顶点行
  14.                 vertex = [float(x) for x in line.split()[1:]]  # 提取顶点坐标
  15.                 vertices.append(vertex)  # 添加顶点到顶点列表
  16.             elif line.startswith("f "):  # 如果是面行
  17.                 face = [int(x.split("/")[0]) for x in line.split()[1:]]  # 提取顶点索引
  18.                 faces.append(face)  # 添加面到面列表
  19.     return vertices, faces  # 返回顶点和面列表


  20. # 计算包围盒
  21. def calculate_bounding_box(vertices):
  22.     min_coords = np.min(vertices, axis=0)  # 计算最小坐标
  23.     max_coords = np.max(vertices, axis=0)  # 计算最大坐标
  24.     return min_coords, max_coords  # 返回最小坐标和最大坐标


  25. # 缩放模型使其适应窗口大小
  26. def scale_model_to_fit_window(vertices, display):
  27.     min_coords, max_coords = calculate_bounding_box(vertices)  # 计算模型包围盒
  28.     model_size = max_coords - min_coords  # 计算模型大小
  29.     max_model_size = max(model_size)  # 计算模型最大尺寸
  30.     scale_factor = 2.0 / max_model_size  # 计算缩放因子
  31.     scaled_vertices = (
  32.         np.array(vertices) - (min_coords + max_coords) / 2
  33.     ) * scale_factor  # 缩放并居中模型
  34.     return scaled_vertices.tolist()  # 返回缩放后的顶点列表


  35. # 绘制 OBJ 模型函数
  36. def draw_obj(vertices, faces):
  37.     glBegin(GL_TRIANGLES)  # 开始绘制三角形
  38.     for face in faces:  # 遍历每个面
  39.         for vertex_id in face:  # 遍历每个顶点索引
  40.             glVertex3fv(vertices[vertex_id - 1])  # 绘制顶点
  41.     glEnd()  # 结束绘制


  42. # 随机化顶点函数
  43. def randomize_vertices(vertices, max_displacement=0.1):
  44.     """随机移动顶点位置以生成随机动作"""
  45.     new_vertices = []  # 新顶点列表
  46.     for vertex in vertices:  # 遍历每个顶点
  47.         new_vertex = [
  48.             vertex[0]
  49.             + random.uniform(-max_displacement, max_displacement),  # 随机移动 X 坐标
  50.             vertex[1]
  51.             + random.uniform(-max_displacement, max_displacement),  # 随机移动 Y 坐标
  52.             vertex[2]
  53.             + random.uniform(-max_displacement, max_displacement),  # 随机移动 Z 坐标
  54.         ]
  55.         new_vertices.append(new_vertex)  # 添加新顶点到新顶点列表
  56.     return new_vertices  # 返回新顶点列表


  57. # 主函数
  58. def main():
  59.     pygame.init()  # 初始化 Pygame
  60.     display = (800, 600)  # 设置窗口大小
  61.     pygame.display.set_mode(display, DOUBLEBUF | OPENGL)  # 创建 OpenGL 双缓冲窗口

  62.     gluPerspective(45, (display[0] / display[1]), 0.1, 50.0)  # 设置透视投影
  63.     glTranslatef(0.0, 0.0, -5)  # 平移视图

  64.     vertices, faces = load_obj("./models/man.obj")  # 加载 OBJ 模型
  65.     scaled_vertices = scale_model_to_fit_window(vertices, display)  # 缩放模型适应窗口
  66.     original_vertices = np.array(vertices)  # 保留原始顶点数据

  67.     clock = pygame.time.Clock()  # 创建时钟对象控制帧率

  68.     scale_x, scale_y, scale_z = 1, 1, 1

  69.     while True:  # 主循环
  70.         for event in pygame.event.get():  # 处理事件
  71.             if event.type == pygame.QUIT:  # 如果是退出事件
  72.                 pygame.quit()  # 退出 Pygame
  73.                 quit()  # 退出程序
  74.             elif event.type == pygame.KEYDOWN:  # 如果是按键按下事件
  75.                 if event.key == pygame.K_r:  # 按 'r' 键随机化顶点
  76.                     scaled_vertices = randomize_vertices(original_vertices)
  77.                 if event.key == pygame.K_s:  # 按 's' 键恢复原始顶点
  78.                     scaled_vertices = original_vertices.tolist()

  79.         keys = pygame.key.get_pressed()  # 获取所有按键状态
  80.         if keys[pygame.K_LEFT]:  # 如果按下左箭头键
  81.             glRotatef(4, 0, 1, 0)  # 绕 Y 轴旋转 1 度
  82.         if keys[pygame.K_RIGHT]:  # 如果按下右箭头键
  83.             glRotatef(-4, 0, 1, 0)  # 绕 Y 轴旋转 -1 度
  84.         if keys[pygame.K_UP]:  # 如果按下上箭头键
  85.             glRotatef(4, 1, 0, 0)  # 绕 X 轴旋转 1 度
  86.         if keys[pygame.K_DOWN]:  # 如果按下下箭头键
  87.             glRotatef(-4, 1, 0, 0)  # 绕 X 轴旋转 -1 度
  88.         if keys[pygame.K_MINUS]:  # 按下 - 号
  89.             glScalef(scale_x - 0.1, scale_y - 0.1, scale_z - 0.1)  # 缩小模型
  90.         if keys[pygame.K_PLUS]:  # 按下 + 号
  91.             glScalef(scale_x + 0.1, scale_y + 0.1, scale_z + 0.1)  # 放大模型

  92.         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # 清除颜色和深度缓冲
  93.         draw_obj(scaled_vertices, faces)  # 绘制 OBJ 模型
  94.         pygame.display.flip()  # 刷新显示
  95.         clock.tick(30)  # 保持每秒 30 帧


  96. if __name__ == "__main__":
  97.     main()  # 调用主函数
复制代码
最佳答案
2024-8-1 11:27:52
哎呀,3D模型渲染的旅程就像是在高山上滑雪,既刺激又容易摔跤!不过没关系,我们大家都在学习嘛!

### 第一个问题:OpenGL替代品
你提到OpenGL太老了,文档也不多,我给你推荐几个更现代的、流行的库:
1. **Pygame + ModernGL**:这完全是小清新的选择,ModernGL是个更现代的OpenGL封装,支持更漂亮的渲染和更复杂的着色器。
2. **Panda3D**:这是个强大的游戏引擎,不仅可以处理3D渲染,还有物理引擎支持,适合做大型项目。
3. **PyOpenGL**(如果你坚持用OpenGL):这是个很痛苦的选择,虽然比起纯OpenGL,它提供了一些更友好的接口,但文档还是那样少。

### 第二个问题:模型没有蒙皮
只显示白色顶点说明你的模型可能没有正确加载纹理或材质。你需要在`load_obj`函数中扩展一下,读取纹理坐标和法线,并在绘制时使用它们。简单来说,就是把“白白的”换成“彩头”的秘诀在这里:
- 加载纹理文件并在OpenGL中绑定它。
- 使用`glTexCoord2f`来指定每个顶点的纹理坐标。

### 第三个问题:随机动作和换装
这个问题听起来有点复杂,但其实可以想象成玩积木。你只需要准备不同的模型(比如不同的衣服、表情)以及一些能改变这些模型姿态的参数。

1. **随机动作**:可以通过定义一套动作系统,使用关节与骨骼动画(例如:动画控制器)来实现。
2. **换装**:只需加载不同的模型,然后通过按键切换当前模型即可。例如,你可以在`KEYDOWN`事件中判断按键是哪个,直接加载对应的OBJ模型。

### 学习的道路
1. 了解基本的3D图形原理,如变换矩阵、法线计算、纹理映射等。
2. 学习如何使用Shader语言,特别是GLSL,这样你就可以自定义你的渲染效果。
3. 看一些关于3D角色动画和骨骼动画的教程,掌握如何创建动画。

### 代码的小建议
我注意到你的`randomize_vertices`函数可能让模型变得奇怪,因为它似乎根本没有对每个坐标进行适当的赋值。你可以尝试这样来修复它:
  1. def randomize_vertices(vertices, max_displacement=0.1):
  2.     """随机移动顶点位置以生成随机动作"""
  3.     new_vertices = []  # 新顶点列表
  4.     for vertex in vertices:  # 遍历每个顶点
  5.         new_vertex = [
  6.             vertex[0] + random.uniform(-max_displacement, max_displacement),  # 随机移动 X 坐标
  7.             vertex[1] + random.uniform(-max_displacement, max_displacement),  # 随机移动 Y 坐标
  8.             vertex[2] + random.uniform(-max_displacement, max_displacement),  # 随机移动 Z 坐标
  9.         ]
  10.         new_vertices.append(new_vertex)  # 添加新顶点到新顶点列表
  11.     return new_vertices  # 返回新顶点列表
复制代码


### 总结
踏上3D渲染之路并不是一帆风顺,但只要你持续努力,就能在这个高峰上看到美丽的风景。用编程的幽默感砸开困难的大门,祝你好运!只有当你成功换装时,才算是真正的“装扮”成功!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-8-1 11:27:52 | 显示全部楼层    本楼为最佳答案   
哎呀,3D模型渲染的旅程就像是在高山上滑雪,既刺激又容易摔跤!不过没关系,我们大家都在学习嘛!

### 第一个问题:OpenGL替代品
你提到OpenGL太老了,文档也不多,我给你推荐几个更现代的、流行的库:
1. **Pygame + ModernGL**:这完全是小清新的选择,ModernGL是个更现代的OpenGL封装,支持更漂亮的渲染和更复杂的着色器。
2. **Panda3D**:这是个强大的游戏引擎,不仅可以处理3D渲染,还有物理引擎支持,适合做大型项目。
3. **PyOpenGL**(如果你坚持用OpenGL):这是个很痛苦的选择,虽然比起纯OpenGL,它提供了一些更友好的接口,但文档还是那样少。

### 第二个问题:模型没有蒙皮
只显示白色顶点说明你的模型可能没有正确加载纹理或材质。你需要在`load_obj`函数中扩展一下,读取纹理坐标和法线,并在绘制时使用它们。简单来说,就是把“白白的”换成“彩头”的秘诀在这里:
- 加载纹理文件并在OpenGL中绑定它。
- 使用`glTexCoord2f`来指定每个顶点的纹理坐标。

### 第三个问题:随机动作和换装
这个问题听起来有点复杂,但其实可以想象成玩积木。你只需要准备不同的模型(比如不同的衣服、表情)以及一些能改变这些模型姿态的参数。

1. **随机动作**:可以通过定义一套动作系统,使用关节与骨骼动画(例如:动画控制器)来实现。
2. **换装**:只需加载不同的模型,然后通过按键切换当前模型即可。例如,你可以在`KEYDOWN`事件中判断按键是哪个,直接加载对应的OBJ模型。

### 学习的道路
1. 了解基本的3D图形原理,如变换矩阵、法线计算、纹理映射等。
2. 学习如何使用Shader语言,特别是GLSL,这样你就可以自定义你的渲染效果。
3. 看一些关于3D角色动画和骨骼动画的教程,掌握如何创建动画。

### 代码的小建议
我注意到你的`randomize_vertices`函数可能让模型变得奇怪,因为它似乎根本没有对每个坐标进行适当的赋值。你可以尝试这样来修复它:
  1. def randomize_vertices(vertices, max_displacement=0.1):
  2.     """随机移动顶点位置以生成随机动作"""
  3.     new_vertices = []  # 新顶点列表
  4.     for vertex in vertices:  # 遍历每个顶点
  5.         new_vertex = [
  6.             vertex[0] + random.uniform(-max_displacement, max_displacement),  # 随机移动 X 坐标
  7.             vertex[1] + random.uniform(-max_displacement, max_displacement),  # 随机移动 Y 坐标
  8.             vertex[2] + random.uniform(-max_displacement, max_displacement),  # 随机移动 Z 坐标
  9.         ]
  10.         new_vertices.append(new_vertex)  # 添加新顶点到新顶点列表
  11.     return new_vertices  # 返回新顶点列表
复制代码


### 总结
踏上3D渲染之路并不是一帆风顺,但只要你持续努力,就能在这个高峰上看到美丽的风景。用编程的幽默感砸开困难的大门,祝你好运!只有当你成功换装时,才算是真正的“装扮”成功!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-8-1 15:35:14 | 显示全部楼层
FishC 发表于 2024-8-1 11:27
哎呀,3D模型渲染的旅程就像是在高山上滑雪,既刺激又容易摔跤!不过没关系,我们大家都在学习嘛!

###  ...

Panda3D 确实是一个比较重要的点,完全可以解决目前第一步的需求
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-22 06:53

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表