Python 实现三维建模工具

一、内容介绍

人类是那么得有创造力,我们创造、发明、设计、生产了一切大自然没有直接给予我们的东西使我们的生活变得更轻松更美好。在过去,我们只能在图纸上进行产品的创造与设计,而现在,有了计算机的帮助,有了 CAD(计算机辅助设计)软件,大大节省了我们的精力与时间成本,使我们的工作更高效,能够拥有更多时间去思考设计本身。
那么 CAD 软件是如何写出来的呢?CAD 软件种类繁多,但它们有一个共同的特点,就是对三维世界的建模,对三维世界中物体的控制,对三维设计的展示。

课程知识点

本课程项目完成过程中,我们将学习:
 OpenGL 坐标系的转换
 实现简单的用户输入事件回调机制
 设计模式中组合模式的使用
 基于包围盒的碰撞检测

二、实现原理及步骤

1.变换矩阵

在计算机图形学中,常常需要使用到不同的坐标系,比如世界坐标系、摄像机坐标系、视图坐标系等。坐标系之间的转换需要用到变换矩阵。我们可以不理会矩阵的细节,而将其看作一个函数,变换前的点的坐标作为函数的参数,通过这个公式:1-2.3-1 我们就可以得到坐标系变换后的点的坐标了。虽然说是坐标系变换,其实只要认为坐标系是“固定不动”的,就可以看成是坐标系中的物体在坐标系中变换了。移动、旋转、缩放被称作仿射变换,其对应矩阵就是令物体在坐标系中变换使用的。

2.OpenGL 坐标系的转换

一个 3D 模型映射到屏幕上会经过 5 次空间变换,如下图漫画所示,左上角为起始点:
漫画右半部分的坐标系转换基本可以通过 OpenGL 自带的函数帮助我们处理,从摄像机坐标系到齐次裁减坐标系的矩阵转换由 gluPerspective 函数调用完成,到视图坐标系的矩阵转换由 glViewport 函数调用完成。转换矩阵最终会存在 GL_PROJECTION 中,在本项目中,不需要了解这方面的细节内容。

当然,漫画左半部分的坐标系转换就需要我们自己处理了,从对象坐标系到摄像机坐标系的转换矩阵称作 ModelView 矩阵。
安装PyOpenGL:

pip3 install PyOpenGL==3.1.0  numpy==1.14.5 --user

此外,需要安装 freeglut:

sudo apt-get update
sudo apt-get install freeglut3 freeglut3-dev

首先新建文件 viewer.py,导入项目所需的库与方法(viewer.py文件代码见源代码部分)

实现场景类,在工作目录下新建 scene.py文件,(scene.py文件代码见源代码部分)

场景下的对象皆为节点,因此需要抽象出所有类型的对象的基类:Node 类(节点类)。在工作目录下创建 node.py 文件,导入需要的库(node.py文件代码见源代码部分)

每一个节点都有自己的颜色属性,我们新建一个 color.py 文件,保存颜色信息。(color.py文件代码见源代码部分)

新建一个文件 primitive.py,将渲染图元的函数列表写入文件中。( primitive.py文件代码见源代码部分)

平移与改变大小: 设计实现能够平移或者改变节点大小的接口,新建 transformation.py,实现生成平移矩阵与缩放矩阵的方法( transformation.py文件代码见源代码部分)

组合节点: 我们在 node.py 中创建 HierarchicalNode 类,这是一个包含子节点的的节点,它将子节点存储在 child_nodes 中,同时作为 Node 的子类,它也必须实现 render_self, 它的 render_self 函数就是简单地遍历调用子节点的 render_self。

三、源代码

viewer.py文件代码

 #-*- coding:utf-8 -*-from OpenGL.GL import glCallList, glClear, glClearColor, glColorMaterial, glCullFace, glDepthFunc, glDisable, glEnable,\glFlush, glGetFloatv, glLightfv, glLoadIdentity, glMatrixMode, glMultMatrixf, glPopMatrix, \glPushMatrix, glTranslated, glViewport, \GL_AMBIENT_AND_DIFFUSE, GL_BACK, GL_CULL_FACE, GL_COLOR_BUFFER_BIT, GL_COLOR_MATERIAL, \GL_DEPTH_BUFFER_BIT, GL_DEPTH_TEST, GL_FRONT_AND_BACK, GL_LESS, GL_LIGHT0, GL_LIGHTING, \GL_MODELVIEW, GL_MODELVIEW_MATRIX, GL_POSITION, GL_PROJECTION, GL_SPOT_DIRECTIONfrom OpenGL.constants import GLfloat_3, GLfloat_4from OpenGL.GLU import gluPerspective, gluUnProjectfrom OpenGL.GLUT import glutCreateWindow, glutDisplayFunc, glutGet, glutInit, glutInitDisplayMode, \glutInitWindowSize, glutMainLoop, \GLUT_SINGLE, GLUT_RGB, GLUT_WINDOW_HEIGHT, GLUT_WINDOW_WIDTH, glutCloseFuncimport numpyfrom numpy.linalg import norm, invimport randomfrom OpenGL.GL import glBegin, glColor3f, glEnd, glEndList, glLineWidth, glNewList, glNormal3f, glVertex3f, \GL_COMPILE, GL_LINES, GL_QUADSfrom OpenGL.GLU import gluDeleteQuadric, gluNewQuadric, gluSphereimport colorfrom scene import Scenefrom primitive import init_primitives, G_OBJ_PLANEfrom node import Sphere, Cube, SnowFigureclass Viewer(object):def __init__(self):""" Initialize the viewer. """#初始化接口,创建窗口并注册渲染函数self.init_interface()#初始化opengl的配置self.init_opengl()#初始化3d场景self.init_scene()#初始化交互操作相关的代码self.init_interaction()init_primitives()def init_interface(self):""" 初始化窗口并注册渲染函数 """glutInit()glutInitWindowSize(640, 480)glutCreateWindow("3D Modeller")glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)#注册窗口渲染函数glutDisplayFunc(self.render)def init_opengl(self):""" 初始化opengl的配置 """#模型视图矩阵self.inverseModelView = numpy.identity(4)#模型视图矩阵的逆矩阵self.modelView = numpy.identity(4)#开启剔除操作效果glEnable(GL_CULL_FACE)#取消对多边形背面进行渲染的计算(看不到的部分不渲染)glCullFace(GL_BACK)#开启深度测试glEnable(GL_DEPTH_TEST)#测试是否被遮挡,被遮挡的物体不予渲染glDepthFunc(GL_LESS)#启用0号光源glEnable(GL_LIGHT0)#设置光源的位置glLightfv(GL_LIGHT0, GL_POSITION, GLfloat_4(0, 0, 1, 0))#设置光源的照射方向glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, GLfloat_3(0, 0, -1))#设置材质颜色glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)glEnable(GL_COLOR_MATERIAL)#设置清屏的颜色glClearColor(0.4, 0.4, 0.4, 0.0)def init_scene(self):#创建一个场景实例self.scene = Scene()#初始化场景内的对象self.create_sample_scene()def create_sample_scene(self):cube_node = Cube()cube_node.translate(2, 0, 2)cube_node.color_index = 1self.scene.add_node(cube_node)sphere_node = Sphere()sphere_node.translate(-2, 0, 2)sphere_node.color_index = 3self.scene.add_node(sphere_node)hierarchical_node = SnowFigure()hierarchical_node.translate(-2, 0, -2)self.scene.add_node(hierarchical_node)def init_interaction(self):#初始化交互操作相关的代码,之后实现passdef main_loop(self):#程序主循环开始glutMainLoop()def render(self):#初始化投影矩阵self.init_view()#启动光照glEnable(GL_LIGHTING)#清空颜色缓存与深度缓存glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)#设置模型视图矩阵,这节课先用单位矩阵就行了。glMatrixMode(GL_MODELVIEW)glPushMatrix()glLoadIdentity()#渲染场景self.scene.render()#每次渲染后复位光照状态glDisable(GL_LIGHTING)glCallList(G_OBJ_PLANE)glPopMatrix()#把数据刷新到显存上glFlush()def init_view(self):""" 初始化投影矩阵 """xSize, ySize = glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT)#得到屏幕宽高比aspect_ratio = float(xSize) / float(ySize)#设置投影矩阵glMatrixMode(GL_PROJECTION)glLoadIdentity()#设置视口,应与窗口重合glViewport(0, 0, xSize, ySize)#设置透视,摄像机上下视野幅度70度#视野范围到距离摄像机1000个单位为止。gluPerspective(70, aspect_ratio, 0.1, 1000.0)#摄像机镜头从原点后退15个单位glTranslated(0, 0, -15)if __name__ == "__main__":viewer = Viewer()viewer.main_loop()

scene.py代码

    class Scene(object):#放置节点的深度,放置的节点距离摄像机15个单位PLACE_DEPTH = 15.0def __init__(self):#场景下的节点队列self.node_list = list()def add_node(self, node):""" 在场景中加入一个新节点 """self.node_list.append(node)def render(self):""" 遍历场景下所有节点并渲染 """for node in self.node_list:node.render()

node.py代码

    import randomfrom OpenGL.GL import glCallList, glColor3f, glMaterialfv, glMultMatrixf, glPopMatrix, glPushMatrix, \GL_EMISSION, GL_FRONTimport numpyfrom primitive import G_OBJ_CUBE, G_OBJ_SPHEREfrom transformation import scaling, translationimport colorclass Node(object):def __init__(self):#该节点的颜色序号self.color_index = random.randint(color.MIN_COLOR, color.MAX_COLOR)#该节点的平移矩阵,决定了该节点在场景中的位置self.translation_matrix = numpy.identity(4)#该节点的缩放矩阵,决定了该节点的大小self.scaling_matrix = numpy.identity(4)def render(self):""" 渲染节点 """glPushMatrix()#实现平移glMultMatrixf(numpy.transpose(self.translation_matrix))#实现缩放glMultMatrixf(self.scaling_matrix)cur_color = color.COLORS[self.color_index]#设置颜色glColor3f(cur_color[0], cur_color[1], cur_color[2])#渲染对象模型self.render_self()glPopMatrix()def render_self(self):raise NotImplementedError("The Abstract Node Class doesn't define 'render_self'")def translate(self, x, y, z):self.translation_matrix = numpy.dot(self.translation_matrix, translation([x, y, z]))def scale(self, s):self.scaling_matrix = numpy.dot(self.scaling_matrix, scaling([s,s,s]))class Primitive(Node):def __init__(self):super(Primitive, self).__init__()self.call_list = Nonedef render_self(self):glCallList(self.call_list)class Sphere(Primitive):""" 球形图元 """def __init__(self):super(Sphere, self).__init__()self.call_list = G_OBJ_SPHEREclass Cube(Primitive):""" 立方体图元 """def __init__(self):super(Cube, self).__init__()self.call_list = G_OBJ_CUBEclass HierarchicalNode(Node):def __init__(self):super(HierarchicalNode, self).__init__()self.child_nodes = []def render_self(self):for child in self.child_nodes:child.render()class SnowFigure(HierarchicalNode):def __init__(self):super(SnowFigure, self).__init__()self.child_nodes = [Sphere(), Sphere(), Sphere()]self.child_nodes[0].translate(0, -0.6, 0)self.child_nodes[1].translate(0, 0.1, 0)self.child_nodes[1].scale(0.8)self.child_nodes[2].translate(0, 0.75, 0)self.child_nodes[2].scale(0.7)for child_node in self.child_nodes:child_node.color_index = color.MIN_COLOR

primitive.py代码

                          GL_COMPILE, GL_LINES, GL_QUADSfrom OpenGL.GLU import gluDeleteQuadric, gluNewQuadric, gluSphereG_OBJ_PLANE = 1G_OBJ_SPHERE = 2G_OBJ_CUBE = 3def make_plane():glNewList(G_OBJ_PLANE, GL_COMPILE)glBegin(GL_LINES)glColor3f(0, 0, 0)for i in range(41):glVertex3f(-10.0 + 0.5 * i, 0, -10)glVertex3f(-10.0 + 0.5 * i, 0, 10)glVertex3f(-10.0, 0, -10 + 0.5 * i)glVertex3f(10.0, 0, -10 + 0.5 * i)# AxesglEnd()glLineWidth(5)glBegin(GL_LINES)glColor3f(0.5, 0.7, 0.5)glVertex3f(0.0, 0.0, 0.0)glVertex3f(5, 0.0, 0.0)glEnd()glBegin(GL_LINES)glColor3f(0.5, 0.7, 0.5)glVertex3f(0.0, 0.0, 0.0)glVertex3f(0.0, 5, 0.0)glEnd()glBegin(GL_LINES)glColor3f(0.5, 0.7, 0.5)glVertex3f(0.0, 0.0, 0.0)glVertex3f(0.0, 0.0, 5)glEnd()# Draw the Y.glBegin(GL_LINES)glColor3f(0.0, 0.0, 0.0)glVertex3f(0.0, 5.0, 0.0)glVertex3f(0.0, 5.5, 0.0)glVertex3f(0.0, 5.5, 0.0)glVertex3f(-0.5, 6.0, 0.0)glVertex3f(0.0, 5.5, 0.0)glVertex3f(0.5, 6.0, 0.0)# Draw the Z.glVertex3f(-0.5, 0.0, 5.0)glVertex3f(0.5, 0.0, 5.0)glVertex3f(0.5, 0.0, 5.0)glVertex3f(-0.5, 0.0, 6.0)glVertex3f(-0.5, 0.0, 6.0)glVertex3f(0.5, 0.0, 6.0)# Draw the X.glVertex3f(5.0, 0.0, 0.5)glVertex3f(6.0, 0.0, -0.5)glVertex3f(5.0, 0.0, -0.5)glVertex3f(6.0, 0.0, 0.5)glEnd()glLineWidth(1)glEndList()def make_sphere():glNewList(G_OBJ_SPHERE, GL_COMPILE)quad = gluNewQuadric()gluSphere(quad, 0.5, 30, 30)gluDeleteQuadric(quad)glEndList()def make_cube():glNewList(G_OBJ_CUBE, GL_COMPILE)vertices = [((-0.5, -0.5, -0.5), (-0.5, -0.5, 0.5), (-0.5, 0.5, 0.5), (-0.5, 0.5, -0.5)),((-0.5, -0.5, -0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), (0.5, -0.5, -0.5)),((0.5, -0.5, -0.5), (0.5, 0.5, -0.5), (0.5, 0.5, 0.5), (0.5, -0.5, 0.5)),((-0.5, -0.5, 0.5), (0.5, -0.5, 0.5), (0.5, 0.5, 0.5), (-0.5, 0.5, 0.5)),((-0.5, -0.5, 0.5), (-0.5, -0.5, -0.5), (0.5, -0.5, -0.5), (0.5, -0.5, 0.5)),((-0.5, 0.5, -0.5), (-0.5, 0.5, 0.5), (0.5, 0.5, 0.5), (0.5, 0.5, -0.5))]normals = [(-1.0, 0.0, 0.0), (0.0, 0.0, -1.0), (1.0, 0.0, 0.0), (0.0, 0.0, 1.0), (0.0, -1.0, 0.0), (0.0, 1.0, 0.0)]glBegin(GL_QUADS)for i in range(6):glNormal3f(normals[i][0], normals[i][1], normals[i][2])for j in range(4):glVertex3f(vertices[i][j][0], vertices[i][j][1], vertices[i][j][2])glEnd()glEndList()def init_primitives():make_plane()make_sphere()make_cube()

transformation.py代码

def translation(displacement):t = numpy.identity(4)t[0, 3] = displacement[0]t[1, 3] = displacement[1]t[2, 3] = displacement[2]return tdef scaling(scale):s = numpy.identity(4)s[0, 0] = scale[0]s[1, 1] = scale[1]s[2, 2] = scale[2]s[3, 3] = 1return s

color.py代码

    MAX_COLOR = 9MIN_COLOR = 0COLORS = { # RGB Colors0:  (1.0, 1.0, 1.0),1:  (0.05, 0.05, 0.9),2:  (0.05, 0.9, 0.05),3:  (0.9, 0.05, 0.05),4:  (0.9, 0.9, 0.0),5:  (0.1, 0.8, 0.7),6:  (0.7, 0.2, 0.7),7:  (0.7, 0.7, 0.7),8:  (0.4, 0.4, 0.4),9:  (0.0, 0.0, 0.0),}

四、 结果及分析

在本节课的完整版代码中,追加了立方体类节点与坐标平面图,但由于没有设置 ModelView,所以这个角度看不清坐标平面图,在下节课引入轨迹球后就能够全方位的观察这个世界啦。

Python 实现三维建模工具(上)相关推荐

  1. 透视城市“生命线” MapGIS地下管线三维建模工具

    城市是繁忙的,昨夜的浮尘还未落定,新一天的晨曦又接替了灯光,人们匆匆地开启了新一天的生活,在车水马龙中穿梭,在楼宇林立间工作生活,一切都是匆匆而又有条不紊.繁忙的背后还是繁忙,水.电.燃气.热量.信息 ...

  2. Python实现3D建模工具(下)

    用户接口 我们希望与场景实现两种交互,一种是你可以操纵场景从而能够从不同的角度观察模型,一种是你拥有添加与操作修改模型对象的能力.为了实现交互,我们需要得到键盘与鼠标的输入,GLUT允许我们在键盘或鼠 ...

  3. 地下管线三维建模工具 MagicPipe3D V2.5

    地下管线三维建模工具MagicPipe3D实现了从二维地下管网矢量数据到三维管网BIM模型的一键式转换,基于GIS交互式构建,建模过程包括二维数据提取.三维参数配置.几何建模.语义建模.模型组织.可视 ...

  4. 草图大师三维建模工具SketchUp Pro 2023 中文版下载

    SketchUp Pro是一款功能强大的三维建模软件,它能够帮助用户快速地创建.修改和共享3D模型.该软件具有直观的界面和易于使用的工具,使用户可以轻松地进行模型设计.渲染和动画制作.同时,Sketc ...

  5. python实现3d建模工具_3D One 2.5引爆新思维,用趣味编程来实现3D建模!

    3D One2.5正式版终于和大家见面啦!新版本全新推出趣味编程,让3D模型也能通过编程逻辑来完成,丰富你的创新想象力.这给有计划开展编程和3D设计课的学校提供了支持,在2018年高中新课标提出的加强 ...

  6. 轻便易用的三维建模软件

    用python写了个简易的三维建模软件Draft,抛砖引玉一下,小伙伴们可以在此基础上完善功能.采用wxPython库和OpenGL,由于OpenGL只支持Linux,所以软件的编写调试运行都是在Li ...

  7. python三维建模和cad比较_对比Revit和CAD三维建模的不同

    众所周知,Revit是为实现BIM而设计的三维建模软件,因其强大的功能已经得到广大工程设计师的认可.利用Revit可以快速搭建BIM三维模型,参数化构件和项目文件都可以重复调用,从而提高设计效率,减少 ...

  8. 计算机学3d建模吗,计算机三维建模与动画基础

    计算机三维建模与动画基础 语音 编辑 锁定 讨论 上传视频 <计算机三维建模与动画基础>是2008年清华大学出版社出版的图书,作者是张烈,骆春慧. 书    名 计算机三维建模与动画基础 ...

  9. Rhinoceros mac版(犀牛三维建模软件)中文版

    犀牛Rhinoceros mac版是非常受欢迎的一款三维建模软件,rhinoceros mac 中文版可应用于三维动画制作.机械设计.建筑设计.工业制造等,具备全面的NURBS.网格.分析.制图等工具 ...

  10. Rhinoceros 5 mac版(犀牛三维建模软件)汉化破解版

    犀牛Rhinoceros 5 mac版是非常受欢迎的一款三维建模软件,rhinoceros mac 破解版可应用于三维动画制作.机械设计.建筑设计.工业制造等,具备全面的NURBS.网格.分析.制图等 ...

最新文章

  1. java设计优化--观察者模式
  2. ICA(独立成分分析)笔记
  3. Hadoop-2.4.1学习之edits和fsimage查看器
  4. [转]掌控像素的虚实
  5. 网站架构相关PPT、文章整理
  6. 如何在SAP S/4HANA Cloud系统里创建employee
  7. 什么是Docker?看这一篇文章就够了
  8. 数据结构基础(7) --循环队列的设计与实现
  9. C#LeetCode刷题之#242-有效的字母异位词(Valid Anagram)
  10. recycleview 自动循环滚动_划重点 | 不能不知的滚动轴承知识—轴承分类(续)
  11. 深入理解5种IO模型
  12. docker 打包部署 python项目_Docker如何部署Python项目的实现详解
  13. python创建变量revenue_Python pandas.DataFrame.le函数方法的使用
  14. Win10下安装并配置Python环境变量以及pip的安装、更新与使用
  15. Head First Java 目录结构
  16. 软件常见的各种版本英文缩写
  17. /*模拟一个简单的购房商贷月供计算器,按照以下公式计算总利息和每个月还款金额: 总利息=贷款金额*利率; 每月还款金额=(贷款金额+总利息)/贷款年限
  18. cesium粒子特效
  19. DLL输出类使用研究手记(ZZ)
  20. 关于谷歌中国的最新声明

热门文章

  1. Jasmine JavaScript测试 - toBe vs toEqual
  2. web前端学习13-19(HTML常用标签)
  3. VC安装产生eula.1028.txt等文件的问题
  4. 公历转农历的程序(代码转载于网络)
  5. 使用超链接实现企业QQ在线客服
  6. 安卓手机格式化怎么弄_一加6/7/7Pro怎么从氢OS安卓10降级安卓9系统-完美降级教程...
  7. 计算机图片显示简单原理
  8. 内存颗粒位宽和容量_DDR4内存颗粒--美光篇
  9. Android 11 正式发布:更方便的操作和更安全的隐私
  10. 3.17服务器维护,2016年3月17日服务器停机维护公告