如何为Poly3DCollection按顶点标量场线性渐变设置面颜色
问题概述
我有一个由四边形单元组成的网格,网格单元的节点上定义有标量场。我当前使用Poly3DCollection绘制该网格,希望根据标量场值为单元着色。但目前我只能对构成每个3D多边形的顶点标量值取平均来设置面颜色,找不到实现面颜色随各顶点标量值平滑线性渐变的方法。已有同类多边形渐变填充方案仅适用于2D路径与多边形,无法适配3D场景。
复现代码
下方是可复现当前场景的最小可运行示例(MWE)。如代码所示,当前设置3D多边形面颜色时,需要先对四个顶点定义的标量场取平均值,目标是实现面颜色根据顶点处的标量场值平滑渐变。
import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.mplot3d.art3d import Poly3DCollection # 创建画布与3D坐标轴 fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # 定义单元顶点坐标 vertices = [np.array([[.25, 0., 0.], [.75, 0., 0.], [.75, .3, .5], [.25, .3, .5]]), np.array([[.25, .3, .5], [.75, .3, .5], [.75, .6, .5], [.25, .6, .5]]), np.array([[.25, .6, .5], [.75, .6, .5], [.75, .9, 1.], [.25, .9, 1.]])] # 生成顶点上的随机标量场 scalar = np.random.rand(3, 4) # 保证重合顶点的标量值一致 scalar[1, 0:2] = scalar[0, 2:] scalar[2, 0:2] = scalar[1, 2:] # 对面内顶点标量取平均 averaged_scalar = np.mean(scalar, axis=1) # 创建3D多边形集合 pc = Poly3DCollection(vertices, linewidths=.1) # 初始化色图映射 m = mpl.cm.ScalarMappable(cmap=mpl.cm.jet) m.set_array(scalar) m.set_clim(vmin=np.amin(averaged_scalar), vmax=np.amax(averaged_scalar)) rgba_array = m.to_rgba(averaged_scalar) pc.set_facecolor([(i[0], i[1], i[2]) for i in rgba_array]) # 设置边颜色 pc.set_edgecolor('k') # 将多边形添加到坐标轴 ax.add_collection3d(pc) # 展示图像 plt.show()

解决方案
Poly3DCollection原生仅支持统一设置单个面的颜色,不支持面内逐顶点颜色插值,无法直接实现渐变效果。要实现3D网格面内随顶点标量值线性平滑渐变的着色效果,可换用Matplotlib自带的plot_trisurf接口:该接口支持传入逐顶点标量值,自动在三角单元内完成线性颜色插值,只需将原有四边形单元拆分为两个三角单元、保证共享顶点的坐标与标量值完全一致,即可得到无断层的平滑渐变着色效果。
修改后的可运行代码如下:
import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np # 创建画布与3D轴 fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # 去重后的所有网格顶点坐标 unique_verts = np.array([ [.25, 0., 0.], # 顶点0 [.75, 0., 0.], # 顶点1 [.75, .3, .5], # 顶点2 [.25, .3, .5], # 顶点3 [.75, .6, .5], # 顶点4 [.25, .6, .5], # 顶点5 [.75, .9, 1.], # 顶点6 [.25, .9, 1.] # 顶点7 ]) # 四边形拆分后的三角面顶点索引(每个四边形拆为2个三角形) tri_faces = np.array([ [0, 1, 2], [0, 2, 3], # 第一个四边形 [3, 2, 4], [3, 4, 5], # 第二个四边形 [5, 4, 6], [5, 6, 7] # 第三个四边形 ]) # 逐顶点标量场,保证共享顶点值一致 np.random.seed(42) # 固定随机种子方便复现 vert_scalar = np.zeros(len(unique_verts)) vert_scalar[0:4] = np.random.rand(4) vert_scalar[4:6] = np.random.rand(2) vert_scalar[6:8] = np.random.rand(2) # 绘制三角网格,自动插值逐顶点颜色 surf = ax.plot_trisurf( unique_verts[:,0], unique_verts[:,1], unique_verts[:,2], triangles=tri_faces, array=vert_scalar, cmap=mpl.cm.jet, edgecolor='k', linewidth=0.1, antialiased=True ) # 添加色条方便对照数值 fig.colorbar(surf, ax=ax, shrink=0.7) plt.show()
渲染后的效果不存在按面平均导致的色块边界,颜色会沿网格表面随标量场值连续平滑变化。
内容的提问来源于stack exchange,提问作者fma
相关产品推荐
相关产品推荐

