十分钟玩转3D绘图:WxGL完全手册(第二版)

Posted 天元浪子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十分钟玩转3D绘图:WxGL完全手册(第二版)相关的知识,希望对你有一定的参考价值。

文章目录

1 简介

WxGL是一个基于PyOpenGL的三维数据绘图工具包,以wx为显示后端,提供Matplotlib风格的应用方式。WxGL也可以和wxPython无缝结合,在wx的窗体上绘制三维模型。

WxGL提供了一套简洁易用、对用户友好的API,将OpenGL的复杂概念全部封装起来,使得用户可以更加专注于数据的处理,而无需在3D显示方面分散精力。WxGL还提供了着色器语言接口,允许用户定制着色器,以应对特殊的应用需求。

作为开源项目,WxGL遵循MIT开源软件许可协议。任何使用、复制、修改本软件以及出版发行、再授权、贩售本软件的行为,都必须遵守MIT授权协议的约束。

项目地址

中文文档

1.1 3D绘图工具包

wxgl.glplot作为WxGL的3D绘图工具包,提供了一系列功能完备的绘图函数,函数命名和使用都非常类似matplotlib.pyplot子模块。正如matplotlib.pyplot被简写为plt那样,glt被视为wxgl.glplot的别名。

1.2 集成应用

WxGL以wxPython为显示后端,wxgl.Scene继承自wx.glcanvas.GLCanvas类,因此WxGL可以和wxPython无缝结合,在wx的窗体上绘制三维模型。

2 安装和依赖

2.1 安装

WxGL模块使用pip命令安装。

pip install wxgl

如果环境中存在多个python解释器,请指定解释器安装。

python.exe -m pip install wxgl

2.2 依赖关系

WxGL依赖下列模块:

  • pyopengl - 推荐版本:3.1.5或更高
  • numpy - 推荐版本:1.18.2或更高
  • matplotlib - 推荐版本:3.1.2或更高
  • pillow - 推荐版本:8.2.0或更高
  • wxpython - 推荐版本:4.0.7.post2或更高
  • freetype-py - 推荐版本:2.2.0或更高
  • pynput - 推荐版本:1.7.6或更高
  • imageio - 推荐版本:2.8.0或更高

如果当前运行环境没有安装这些模块,安装程序将会自动安装它们。如果安装过程出现问题,或者安装完成后无法正常使用,请手动安装WxGL的依赖模块。

3 快速体验

3.1 熟悉的风格

下面这几行代码,绘制了一个中心在三维坐标系原点半径为1的纯色圆球。忽略模块名的话,这些代码和Matplotlib的风格几乎是完全一致的。

import wxgl.glplot as glt

glt.title('快速体验:$x^2+y^2+z^2=1$')
glt.uvsphere((0,0,0), 1, color='cyan')
glt.show()

弹出窗口显式如下。

3.2 子图布局

在一张画布上可以任意放置多个子图。下面的代码演示了子图布局函数subplot的经典用法,代码中的纹理图片在GitHub本项目的example路径下。

import wxgl
import wxgl.glplot as glt

glt.subplot(121)
glt.title('经纬度网格生成球体')
glt.uvsphere((0,0,0), 1, texture=wxgl.Texture('res/earth.jpg'))
glt.grid()

glt.subplot(122)
glt.title('正八面体迭代细分生成球体')
glt.isosphere((0,0,0), 1, color=(0,1,1), fill=False, iterations=5)
glt.grid()

glt.show()

弹出窗口显式如下。

3.3 Colorbar

对于数据快速可视化工具来说,Colorbar是必不可少的。下面的代码演示了Colorbar最简单的用法。

import numpy as np
import wxgl.glplot as glt

vs = np.random.random((300, 3))*2-1
color = np.random.random(300)
size = np.linalg.norm(vs, axis=1)
size = 30 * (size - size.min()) / (size.max() - size.min())

glt.title('随机生成的300个点')
glt.point(vs, color, cm='jet', alpha=0.8, size=size)
glt.colorbar('jet', [0, 100], loc='right', subject='高度')
glt.colorbar('Paired', [-50, 50], loc='bottom', subject='温度', margin_left=5)
glt.colorbar('rainbow', [0, 240], loc='bottom', subject='速度', margin_right=5)
glt.show()

弹出窗口显式如下。

3.4 光照和材质

WxGL提供了BaseLight、SunLight、LampLight、SkyLight、SphereLight等多种光照方案,配合光洁度、粗糙度、金属度、透光度等参数,可模拟出不同的质感。

import wxgl
import wxgl.glplot as glt

glt.subplot(221)
glt.title('太阳光')
glt.torus((0,0,0), 1, 3, vec=(0,1,1), light=wxgl.SunLight(roughness=0, metalness=0, shininess=0.5))

glt.subplot(222)
glt.title('灯光')
pos = (3, 0.0, 3)
glt.torus((0,0,0), 1, 3, vec=(0,1,1), light=wxgl.LampLight(position=pos))
glt.point((pos,), color='white', size=20)

glt.subplot(223)
glt.title('户外光')
glt.torus((0,0,0), 1, 3, vec=(0,1,1), light=wxgl.SkyLight(sky=(1.0,1.0,1.0)))

glt.subplot(224)
glt.title('球谐光')
glt.torus((0,0,0), 1, 3, vec=(0,1,1), light=wxgl.SphereLight(5, factor=0.8))

glt.show()

弹出窗口显式如下。

3.5 让模型动起来

通过transform参数传递一个以累计渲染时长duration为参数的函数给模型,可以实现复杂的模型动画。相机巡航也以类似的方式实现。下面的代码中,两个子图均使用了射向右后方的平行灯光。当模型旋转时,由于相机和灯光位置不变,模型上的光照位置随之改变;而相机旋转时,由于模型和灯光的相对管不变,模型上的光照位置固定不变。

import wxgl
import wxgl.glplot as glt

tf = lambda duration : ((0, 1, 0, (0.02*duration)%360),)
cf = lambda duration : 'azim':(-0.02*duration)%360

tx = wxgl.Texture('res/earth.jpg')
light = wxgl.SunLight(direction=(1,0,-1))

glt.subplot(121)
glt.title('模型旋转')
glt.cylinder((0,1,0), (0,-1,0), 1, texture=tx, transform=tf, light=light)

glt.subplot(122)
glt.cruise(cf)
glt.title('相机旋转')
glt.cylinder((0,1,0), (0,-1,0), 1, texture=tx, light=light)

glt.show()

弹出窗口显式如下。记得点击播放按钮才能让模型动起来。

3.6 定制着色器

除了内置的绘图函数,WxGL还提供了GLSL接口,允许用户定制着色器代码。下面的代码演示了使用定制的顶点着色器和片元着色器的基本流程。

import wxgl
import wxgl.glplot as glt

vshader = """
	#version 330 core
	in vec4 a_Position;
    in vec4 a_Color;
	uniform mat4 u_ProjMatrix;
    uniform mat4 u_ViewMatrix;
    uniform mat4 u_ModelMatrix;
    out vec4 v_Color;
	void main()  
		gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position; 
		v_Color = a_Color;
	
"""

fshader = """
	#version 330 core
	in vec4 v_Color;
	void main()  
		gl_FragColor = v_Color; 
	 
"""

m = wxgl.Model(wxgl.TRIANGLE_STRIP, vshader, fshader) # 实例化模型,设置绘图方法和着色器源码
m.set_vertex('a_Position', [[-1,1,0],[-1,-1,0],[1,1,0],[1,-1,0]]) # 4个顶点坐标
m.set_color('a_Color', [[1,0,0],[0,1,0],[0,0,1],[0,1,1]]) # 4个顶点的颜色
m.set_proj_matrix('u_ProjMatrix') # 设置投影矩阵
m.set_view_matrix('u_ViewMatrix') # 设置视点矩阵
m.set_model_matrix('u_ModelMatrix') # 设置模型矩阵

glt.model(m) # 添加模型到画布
glt.show() # 显示画布

弹出窗口显式如下。

4 进阶应用

4.1 颜色

WxGL支持十六进制的颜色、预定义的颜色,以及浮点型元组、列表或numpy数组表示的RGB/RGBA颜色。下面这些写法都是合法的。

  • ‘#F3D6E9’, ‘#de3f80’
  • ‘#C6F’, ‘#ab8’
  • ‘red’, ‘blue’, ‘cyan’
  • (1.0, 0.8, 0.2), (1.0, 0.8, 0.2, 1.0)
  • [1.0, 0.8, 0.2], [1.0, 0.8, 0.2, 1.0]
  • numpy.array([1.0, 0.8, 0.2]), numpy.array([1.0, 0.8, 0.2, 1.0])

预定义的颜色共计148种。函数wxgl.color_list返回预定义的颜色列表,函数wxgl.color_help返回预定义的颜色中英文对照表。

  • aliceblue - 爱丽丝蓝
  • antiquewhite - 古董白
  • aqua - 青
  • aquamarine - 碧绿
  • azure - 青白
  • beige - 米
  • bisque - 橘黄
  • black - 黑
  • blanchedalmond - 杏仁白
  • blue - 蓝
  • blueviolet - 蓝紫
  • brown - 褐
  • burlywood - 硬木褐
  • cadetblue - 军服蓝
  • chartreuse - 查特酒绿
  • chocolate - 巧克力
  • coral - 珊瑚红
  • cornflowerblue - 矢车菊蓝
  • cornsilk - 玉米穗黄
  • crimson - 绯红
  • cyan - 青
  • darkblue - 深蓝
  • darkcyan - 深青
  • darkgoldenrod - 深金菊黄
  • darkgray - 暗灰
  • darkgreen - 深绿
  • darkgrey - 暗灰
  • darkkhaki - 深卡其
  • darkmagenta - 深品红
  • darkolivegreen - 深橄榄绿
  • darkorange - 深橙
  • darkorchid - 深洋兰紫
  • darkred - 深红
  • darksalmon - 深鲑红
  • darkseagreen - 深海藻绿
  • darkslateblue - 深岩蓝
  • darkslategray - 深岩灰
  • darkslategrey - 深岩灰
  • darkturquoise - 深松石绿
  • darkviolet - 深紫
  • deeppink - 深粉
  • deepskyblue - 深天蓝
  • dimgray - 昏灰
  • dimgrey - 昏灰
  • dodgerblue - 湖蓝
  • firebrick - 火砖红
  • floralwhite - 花卉白
  • forestgreen - 森林绿
  • fuchsia - 洋红
  • gainsboro - 庚氏灰
  • ghostwhite - 幽灵白
  • gold - 金
  • goldenrod - 金菊
  • gray - 灰
  • green - 绿
  • greenyellow - 黄绿
  • grey - 灰
  • honeydew - 蜜瓜绿
  • hotpink - 艳粉
  • indianred - 印度红
  • indigo - 靛蓝
  • ivory - 象牙白
  • khaki - 卡其
  • lavender - 薰衣草紫
  • lavenderblush - 薰衣草红
  • lawngreen - 草坪绿
  • lemonchiffon - 柠檬绸黄
  • lightblue - 浅蓝
  • lightcoral - 浅珊瑚红
  • lightcyan - 浅青
  • lightgoldenrodyellow - 浅金菊黄
  • lightgray - 亮灰
  • lightgreen - 浅绿
  • lightgrey - 亮灰
  • lightpink - 浅粉
  • lightsalmon - 浅鲑红
  • lightseagreen - 浅海藻绿
  • lightskyblue - 浅天蓝
  • lightslategray - 浅岩灰
  • lightslategrey - 浅岩灰
  • lightsteelblue - 浅钢青
  • lightyellow - 浅黄
  • lime - 绿
  • limegreen - 青柠绿
  • linen - 亚麻
  • magenta - 洋红
  • maroon - 栗
  • mediumaquamarine - 中碧绿
  • mediumblue - 中蓝
  • mediumorchid - 中洋兰紫
  • mediumpurple - 中紫
  • mediumseagreen - 中海藻绿
  • mediumslateblue - 中岩蓝
  • mediumspringgreen - 中嫩绿
  • mediumturquoise - 中松石绿
  • mediumvioletred - 中紫红
  • midnightblue - 午夜蓝
  • mintcream - 薄荷乳白
  • mistyrose - 雾玫瑰红
  • moccasin - 鹿皮
  • navajowhite - 土著白
  • navy - 藏青
  • oldlace - 旧蕾丝白
  • olive - 橄榄
  • olivedrab - 橄榄绿
  • orange - 橙
  • orangered - 橘红
  • orchid - 洋兰紫
  • palegoldenrod - 白金菊黄
  • palegreen - 白绿
  • paleturquoise - 白松石绿
  • palevioletred - 白紫红
  • papayawhip - 番木瓜橙
  • peachpuff - 粉朴桃
  • peru - 秘鲁红
  • pink - 粉
  • plum - 李紫
  • powderblue - 粉末蓝
  • purple - 紫
  • rebeccapurple - 丽贝卡紫
  • red - 红
  • rosybrown - 玫瑰褐
  • royalblue - 品蓝
  • saddlebrown - 鞍褐
  • salmon - 鲑红
  • sandybrown - 沙褐
  • seagreen - 海藻绿
  • seashell - 贝壳白
  • sienna - 土黄赭
  • silver - 银
  • skyblue - 天蓝
  • slateblue - 岩蓝
  • slategray - 岩灰
  • slategrey - 岩灰
  • snow - 雪白
  • springgreen - 春绿
  • steelblue - 钢青
  • tan - 日晒褐
  • teal - 鸭翅绿
  • thistle - 蓟紫
  • tomato - 番茄红
  • turquoise - 松石绿
  • violet - 紫罗兰
  • wheat - 麦
  • white - 白
  • whitesmoke - 烟雾白
  • yellow - 黄
  • yellowgreen - 暗黄绿

4.2 颜色映射

将数据值域范围内的不同数值映射为不同的颜色,是数据可视化的常用手段。WxGL的颜色映射表继承自Matplotlib库。

尽管WxGL提供了颜色映射函数wxgl.cmap,但通常情况下用户只需要提供一个颜色映射表名而无需显式地调用该函数——除非用户使用定制的着色器绘制模型。

WxGL提供了7大类共计82种颜色映射表,每种映射表名字之后附加’_r’,可以获得该映射表的反转版本。函数wxgl.cmap_list返回颜色映射方案列表,函数wxgl.cmap_help返回颜色映射方案分类列表。

  • 视觉均匀类:viridis, plasma, inferno, magma, cividis
  • 单调变化类:Greys, Purples, Blues, Greens, Oranges, Reds, YlOrBr, YlOrRd, OrRd, PuRd, RdPu, BuPu, GnBu, PuBu, YlGnBu, PuBuGn, BuGn, YlGn
  • 近似单调类:binary, gist_yarg, gist_gray, gray, bone, pink, spring, summer, autumn, winter, cool, Wistia, hot, afmhot, gist_heat, copper
  • 亮度发散类:PiYG, PRGn, BrBG, PuOr, RdGy, RdBu, RdYlBu, RdYlGn, Spectral, coolwarm, bwr, seismic
  • 颜色循环类:twilight, twilight_shifted, hsv
  • 分段阶梯类:Pastel1, Pastel2, Paired, Accent, Dark2, Set1, Set2, Set3, tab10, tab20, tab20b, tab20c
  • 专属定制类:flag, prism, ocean, gist_earth, terrain, gist_stern, gnuplot, gnuplot2, CMRmap, cubehelix, brg, gist_rainbow, rainbow, jet, nipy_spectral, gist_ncar

4.3 视点系统

和OpenGL的习惯一致,WxGL采用右手坐标系:x轴的正方向指向屏幕右侧,y轴正方向只想屏幕上方,z轴正方向指向屏幕外部。

视点坐标系原点始终在屏幕中心。默认视点坐标系原点在世界坐标系的(0, 0, 0)位置,即视点坐标系和世界坐标系保持一致。

模型空间的xyz轴动态范围均为为(-1, 1),相机在z轴正方向(0, 0, 5)位置,视锥体的左右下上前后面分别为(-1, 1, -1, 1, 3, 1000)。如果模型的尺寸小于或超出xyz轴动态范围,则相机位置和视锥体自动按比例缩放——除非将模型的inside参数设置为False,视点坐标系原点也会自动调整到模型的几何中心。

4.4 画布参数

以下面的方式导入wxgl.glplot子模块之后,默认的画布就已经被创建,一个和画布同样大小的子图也被同步创建。用户可以直接在此画布的当前子图上绘制3D模型。

import wxgl.glplot as glt

此时,画布默认的尺寸(size)为1152x648像素,默认的背景色(style)是太空蓝,相机默认的投影方式(proj)为透视投影,默认的的方位角(azim)和高度角(elev)都是0°,默认的的方位角限位器(azim_range)和高度角限位器(elev_range)都是-180°~180°。

方位角是相机位置与视点坐标系原点连线在xOz平面上的投影和z轴正方向的夹角,遵从右手定则(右手握拳,伸开拇指,拇指指向y轴正方向,其余四指指向方位角的正方向)。高度角是相机位置与视点坐标系原点连线与xOz平面的夹角,相机俯视时高度角为正。

方位角限位器(azim_range)和高度角限位器(elev_range)用来设置相机的方位角和高度角的动态范围。

通常显卡对于OpenGL都有很好的兼容性,因此画布默认开启反走样(smooth)。如果模型渲染出现非期望的网格,请尝试关闭反走样(将smooth设置为False)。

在调用wxgl.glplot其他函数之前,使用wxgl.glplot.figure可以改变画布的默认参数。例如:

glt.figure(size=(1600,960), azim=30, elev=20, elev_range=(-90,90), smooth=False)

4.5 UI隐藏的技巧

运行wxgl.glplot.show函数后,在弹出的UI窗口中,可以做如下操作。

  • 拖拽鼠标可以改变相机的方位角和高度角,滚动滚轮可以缩放投影面
  • 按住Ctr键拖拽鼠标,可以改变视点坐标系原点
  • 按住Ctr滚动滚轮,可以改变相机与视点坐标系原点之间的距离
  • 按Esc键,恢复各视区初始的相机姿态
  • 在模型上移动鼠标,底部状态栏显式视点坐标,以及相机位置、方位角、高度角
  • 在模型上点击鼠标邮件,模型在高亮和正常之间切换,表示该模型被选中或放弃选中,同时底部状态栏显式模型选中数量
  • 如有模型被选中,点击“显式/隐藏”,隐藏所有被选中的模型,同时选中状态被清除
  • 如无模型被选中,点击“显式/隐藏”,显示所有隐藏的模型
  • 如果使用了模型动画函数或相机巡航函数,点击“参数设置”按钮选择文件格式后,点击“播放动画”按钮,可以生成GIF或视频文件

4.6 子图布局

子图布局函数wxgl.glplot.subplot接受整数参数,字符串参数,也接受4元组参数。

import numpy as np
import wxgl
import wxgl.glplot as glt

glt.figure(elev=-20)
    
glt.subplot(221) # 整数参数,表示两行两列的第1个位置(左上)
glt.title('独立三角面方法:isolate', size=80)
vs = np.array([[-1,1,1],[-1,-1,1],[1,1,1],[1,-1,1],[1,1,-1],[1,-1,-1],[-1,1,-1],[-1,-1,-1]])
indices = np.array([0,1,3,0,3,2,2,3,5,2,5,4,4,5,7,4,7,6,6,7,1,6,1,0])
color = np.array([[1,0,0],[1,0,1],[1,1,1],[1,1,0],[0,1,1],[0,1,0],[0,0,0],[0,0,1]])
glt.surface(vs, color=color, indices=indices, method='isolate')
glt.yrange((-1.2, 1.2))

glt.subplot('223') # 字符串参数,表示两行两列的第3个位置(左下)
glt.title('带状三角面方法:strip', size=80)
vs = np.array([[-1,1,1],[-1,-1,1],[1,1,1],[1,-1,1],[1,1,-1],[1,-1,-1],[-1,1,-1],[-1,-1,-1],[-1,1,1],[-1,-1,1]])
color = np.array([[1,0,0],[1,0,1],[1,1,1],[1,1,0],[0,1,1],[0,1,0],[0,0,0],[0,0,1],[1,0,0],[1,0,1]])
glt.surface(vs, color=color, method='strip')
glt.yrange((-1.2, 1.2))

glt.subplot((0.5, 0, 0.5, 1)) # 子图左下角坐标(0.5, 0),宽度0.5, 高度1,即窗口右半部
glt.title('连续三角面方法:fan')
theta = np.radians(np.linspace(30, 150, 21))
xs, ys, zs = 2*np.cos(theta), 2*np.sin(theta)-0.5, np.zeros(21)
zs[1::2] += 0.2 
vs = np.vstack(((0,-0.8,0), np.stack((xs,ys-0.8,zs), axis=1)))
glt.surface(vs, method='fan', light=wxgl.SphereLight(9), transform=lambda duration:((0, 1, 0, (0.05*duration)%360),))
vs = np.vstack(((0,1.2,0), np.stack((xs,ys+1.2,zs), axis=1)))
glt.surface(vs, method='fan', light=wxgl.SphereLight(9), transform=lambda duration:((0, 1, 0, -(0.05*duration)%360),))

glt.show()

弹出窗口显式如下。

4.7 多个Colorbar

在一个子图上,可以放置多个Colorbar,位置由Colorbar函数wxgl.glplot.colorbar的loc参数指定。loc参数只有’right’和’bottom’两个选项,即子图右侧和子图底部。

import numpy as np
import wxgl.glplot as glt

vs = np.random.random((300, 3))*2-1
color = np.random.random(300)
size = np.linalg.norm(vs, axis=1)
size = 30 * (size - size.min()) / (size.max() - size.min())

glt.title('随机生成的300个点')
glt.point(vs, color, cm='jet', alpha=0.8, size=size)
glt.colorbar('jet', [0, 100], loc='right', subject='高度', endpoint=True)
glt.colorbar('hsv', [3, 8], loc='right', subject='厚度', endpoint=True)
glt.colorbar('Paired', [-50, 50], loc='bottom', subject='温度', margin_left=5)
glt.colorbar('rainbow', [0, 240], loc='bottom', subject='速度', margin_right=5)
glt.show()

弹出窗口显式如下。

4.8 定制轴名称和轴刻度

坐标网格和坐标轴刻度函数wxgl.glplot.colorbar提供了若干关键字参数,用来定制坐标网格和坐标轴刻度。比如参数xlabel/ylabel/zlabel用于设置坐标轴名称,参数xd/yd/zd用于调整标轴标注密度,参数xf/yf/zf则用于格式化坐标轴的标注。

xf/yf/zf缺省默认以str函数作为格式化函数,用户可以通过自定义函数或lambda函数定制刻度文本的样式。

import numpy as np
import wxgl.glplot as glt

z, x = np.mgrid[1:-1:100j,-1:1:100j]
y = x*x + z*z
glt.mesh(x, y, z, color=y, cm='hsv')

xf = lambda x:'%0.1f°'%(x*50)
yf = lambda y:'%0.3fKm'%y

glt.grid(xlabel='经度', ylabel='高度', zlabel='纬度', yd=2, xf=xf, yf=yf)
glt.show()

弹出窗口显式如下。

4.9 透明和不透明模型

对于(半)透明模型,需要显式地设置其不透明参数opacity为False。WxGL在渲染(半)透明模型时自动关闭深度缓冲区并按模型深度从深至浅依次渲染。(半)透明模型深度由该模型所有顶点的z值的均值决定。3D文本模型作为(半)透明模型,无需设置opacity参数。

import numpy as np
import wxgl.glplot as glt

glt.cube((0,0,0), 1.2)
glt.cylinder((-1,0,0), (1,0,0), 0.5, color=(1.0,1.0,0.1,0.7), opacity=False)
glt.text3d('WxGL', [[-1,1,0.5], [-1,-1,0.5], [1,1,0.5], 十分钟玩转3D绘图:WxGL完全手册(第二版)

代码大全的介绍

极简教程|20 分钟玩转Ansible系列手册!

Unity 3D 学习 《Unity 3D 游戏开发》(第二版 宣雨凇著) 书中一些名字注解

Unity 3D 学习 《Unity 3D 游戏开发》(第二版 宣雨凇著) 书中一些名字注解

Unity 3D 学习 《Unity 3D 游戏开发》(第二版 宣雨凇著) 书中一些名字注解