系列文章
基础
Python+OpenGL绘制3D模型(一)Python 和 PyQt环境搭建
Python+OpenGL绘制3D模型(二)程序框架PyQt5
Python+OpenGL绘制3D模型(三)程序框架PyQt6
Python+OpenGL绘制3D模型(四)绘制线段
Python+OpenGL绘制3D模型(五)绘制三角型
Python+OpenGL绘制3D模型(六)材质文件载入和贴图映射
Python+OpenGL绘制3D模型(七)制作3dsmax导出插件
Python+OpenGL 杂谈(一)
高级
用 DeepDraw 几何引擎开发建筑设计软件(一):SketchUp 替代
用 DeepDraw 几何引擎开发建筑设计软件(二):下载DeepDraw
用 DeepDraw 几何引擎开发建筑设计软件(三):用代码绘制线段
用 DeepDraw 几何引擎开发建筑设计软件(四):用代码绘制面
用 DeepDraw 几何引擎开发建筑设计软件(五):创建选择工具
用 DeepDraw 几何引擎开发建筑设计软件(六):读取模型文件
用 DeepDraw 几何引擎开发建筑设计软件(七):InputPoint输入捕获器
用 DeepDraw 几何引擎开发建筑设计软件(八):画线工具
未完待续…
前言
Sketchup作为目前设计院最为流行的设计软件(非工程制图软件),深受设计师的喜爱,软件小巧,而功能强大,有不少为之开发的插件应运而生,不过呢,关于底层数据结构和工作原理相关的文章少之又少,本文意在填补一下这方面的空缺。
Python+OpenGL绘制3D模型(五)绘制三角型
运行效果
文章目录
- 系列文章
- 前言
- Python+OpenGL绘制3D模型(五)绘制三角型
- 一、绘制三角型
- 二、绘制轮廓线
- 三、运行
- 四、Z-Buffer问题
- 五、最终效果
- 六、全部代码
- 系列文章预告
一、绘制三角型
绘制面的最小单元是三角形,如果你在想,为什么不用OpenGL的多边形来绘制。很好的问题,根据实践,所有的3D软件,都是把多边形转成三角型来绘制的,而且转换的过程所用到的Tessellation算法非常耗时,要把3D空间的模型转到2D空间才能计算。绘制多边形,你要考虑所有顶点是不是在一个平面上,多边形是不是凸多边形,线是否有交叉,中间是否有开孔,等等。。
所以3D绘图,还是老老实实用三角面吧
defdraw_tirangle(p1,p2,p3,gl):gl.glBegin(gl.GL_TRIANGLES)gl.glVertex3d(p1.x(),p1.y(),p1.z())gl.glVertex3d(p2.x(),p2.y(),p2.z())gl.glVertex3d(p3.x(),p3.y(),p3.z())gl.glEnd()一个4边型是由2个三角型组成的,所以绘制1个面需要完整的调用2次三角形绘制
defdraw_single_face(p1,p2,p3,p4,gl):gl.glBegin(gl.GL_TRIANGLES)gl.glVertex3d(p1.x(),p1.y(),p1.z())gl.glVertex3d(p2.x(),p2.y(),p2.z())gl.glVertex3d(p3.x(),p3.y(),p3.z())gl.glEnd()gl.glBegin(gl.GL_TRIANGLES)gl.glVertex3d(p1.x(),p1.y(),p1.z())gl.glVertex3d(p2.x(),p2.y(),p2.z())gl.glVertex3d(p3.x(),p3.y(),p3.z())gl.glEnd()一个完整的立方体,由6个面组成,我们现在来把它们全部画出来
defdraw_box_faces(gl):p1=QVector3D(-1,-1,0)p2=QVector3D(+1,-1,0)p3=QVector3D(+1,+1,0)p4=QVector3D(-1,+1,0)p5=QVector3D(-1,-1,2)p6=QVector3D(+1,-1,2)p7=QVector3D(+1,+1,2)p8=QVector3D(-1,+1,2)draw_single_face(p1,p2,p3,p4,gl)draw_single_face(p5,p6,p7,p8,gl)draw_single_face(p1,p2,p6,p5,gl)draw_single_face(p3,p4,p8,p7,gl)draw_single_face(p2,p3,p7,p6,gl)draw_single_face(p4,p1,p5,p8,gl)二、绘制轮廓线
现在的程序,是没有开启明暗处理和灯光的,所以绘制的所有面都是一个颜色的,画出来的东西是一片混沌,区分不出不同的面,现在我们用单色面+线框的方式来绘制,我们把盒子的所有线断都绘制出来
defdraw_box_lines(gl):p1=QVector3D(-1,-1,0)p2=QVector3D(+1,-1,0)p3=QVector3D(+1,+1,0)p4=QVector3D(-1,+1,0)p5=QVector3D(-1,-1,2)p6=QVector3D(+1,-1,2)p7=QVector3D(+1,+1,2)p8=QVector3D(-1,+1,2)# 一个立方体有12条边draw_single_line(p1,p2,gl)draw_single_line(p2,p3,gl)draw_single_line(p3,p4,gl)draw_single_line(p4,p1,gl)draw_single_line(p5,p6,gl)draw_single_line(p6,p7,gl)draw_single_line(p7,p8,gl)draw_single_line(p8,p5,gl)draw_single_line(p1,p5,gl)draw_single_line(p2,p6,gl)draw_single_line(p3,p7,gl)draw_single_line(p4,p8,gl)defdraw_single_line(p1,p2,gl):gl.glBegin(gl.GL_LINES)gl.glVertex3d(p1.x(),p1.y(),p1.z())gl.glVertex3d(p2.x(),p2.y(),p2.z())gl.glEnd()三、运行
运行后,我们得到一个方形的盒子,很漂亮,用这么少的代码,就可以达到如此的效果
四、Z-Buffer问题
我们已经绘制出模型和轮廓了,但是有个很明显的问题,模型的轮廓有时候是虚线,有时候是实线,闪来闪去,看着很不舒服
这是3D编辑软件都会遇到的一个问题,因为3D软件需要显示模型的线框,但是画线和画面在遇到同一个像素时,就会出现Z-Buffer的值一样,那么这个像素最后是画线时的颜色还是画面时的颜色是个不确定的值。这在3D领域有个专有名词叫做z-fighting,就是说2个面靠的很近很近的时候,这2个面就会来回闪硕的现象。
解决这个问题有个技巧,叫做z-buff bias,也叫做z-buff偏移,具体的办法是在绘制面的时候算出的z-buff值稍微增加一个很小的单位,那么在画线的时候,这个地方的Z值就肯定比画线的Z值小那么一点,就能够保证线能正常显示,不会和面发生z-buff冲突
五、最终效果
六、全部代码
fromPyQt5.QtGuiimportQVector3D############# draw# 绘图主入口############defdraw(gl):# Python+OpenGL绘制3D模型(四) 绘制线段#draw_rect(gl)# Python+OpenGL绘制3D模型(五) 绘制三角面draw_filled_cube(gl)############# draw_filed_cube# 绘制填充的方形############defdraw_filled_cube(gl):# 设置z-buff偏移gl.glEnable(gl.GL_POLYGON_OFFSET_FILL)gl.glPolygonOffset(1,1)# 绘制填充面gl.glColor3f(0.9,0.83,0.6)draw_box_faces(gl)# 关闭z-buff偏移gl.glDisable(gl.GL_POLYGON_OFFSET_FILL)# 绘制线框gl.glColor3f(0.0,0.0,0.0)draw_box_lines(gl)############# draw_box_faces# 画面 - 中间的填充部分############defdraw_box_faces(gl):p1=QVector3D(-1,-1,0)p2=QVector3D(+1,-1,0)p3=QVector3D(+1,+1,0)p4=QVector3D(-1,+1,0)p5=QVector3D(-1,-1,2)p6=QVector3D(+1,-1,2)p7=QVector3D(+1,+1,2)p8=QVector3D(-1,+1,2)draw_single_face(p1,p2,p3,p4,gl)draw_single_face(p5,p6,p7,p8,gl)draw_single_face(p1,p2,p6,p5,gl)draw_single_face(p3,p4,p8,p7,gl)draw_single_face(p2,p3,p7,p6,gl)draw_single_face(p4,p1,p5,p8,gl)defdraw_single_face(p1,p2,p3,p4,gl):gl.glBegin(gl.GL_TRIANGLES)gl.glVertex3d(p1.x(),p1.y(),p1.z())gl.glVertex3d(p2.x(),p2.y(),p2.z())gl.glVertex3d(p3.x(),p3.y(),p3.z())gl.glEnd()gl.glBegin(gl.GL_TRIANGLES)gl.glVertex3d(p3.x(),p3.y(),p3.z())gl.glVertex3d(p4.x(),p4.y(),p4.z())gl.glVertex3d(p1.x(),p1.y(),p1.z())gl.glEnd()############# draw_box_lines# 画面 外侧的线############defdraw_box_lines(gl):p1=QVector3D(-1,-1,0)p2=QVector3D(+1,-1,0)p3=QVector3D(+1,+1,0)p4=QVector3D(-1,+1,0)p5=QVector3D(-1,-1,2)p6=QVector3D(+1,-1,2)p7=QVector3D(+1,+1,2)p8=QVector3D(-1,+1,2)# 一个立方体有12条边draw_single_line(p1,p2,gl)draw_single_line(p2,p3,gl)draw_single_line(p3,p4,gl)draw_single_line(p4,p1,gl)draw_single_line(p5,p6,gl)draw_single_line(p6,p7,gl)draw_single_line(p7,p8,gl)draw_single_line(p8,p5,gl)draw_single_line(p1,p5,gl)draw_single_line(p2,p6,gl)draw_single_line(p3,p7,gl)draw_single_line(p4,p8,gl)defdraw_single_line(p1,p2,gl):gl.glBegin(gl.GL_LINES)gl.glVertex3d(p1.x(),p1.y(),p1.z())gl.glVertex3d(p2.x(),p2.y(),p2.z())gl.glEnd()############# draw_rect# 绘制线段组成的方形############defdraw_rect(gl):gl.glColor3f(1.0,0.0,0.0)gl.glBegin(gl.GL_LINES)gl.glVertex3d(-1,0,0)gl.glVertex3d(+1,0,0)gl.glEnd()gl.glBegin(gl.GL_LINES)gl.glVertex3d(-1,0,2)gl.glVertex3d(+1,0,2)gl.glEnd()gl.glBegin(gl.GL_LINES)gl.glVertex3d(-1,0,0)gl.glVertex3d(-1,0,2)gl.glEnd()gl.glBegin(gl.GL_LINES)gl.glVertex3d(+1,0,0)gl.glVertex3d(+1,0,2)gl.glEnd()系列文章预告
目标是开发一个Sketchup Viewer,能够显示Sketchup的.skp文件中的3D模型
Corona渲染器照片级渲染效果