如何在Plotly Mesh3D中消除三角面顶点顺序对平滑光照的影响?
如何通过规范三角面顶点顺序实现平滑网格着色?
绘制三角网格时,期望相邻面之间的着色过渡平滑,但效果有时正常有时异常,问题根源在于三角面顶点的定义顺序。修改单个三角面的顶点顺序(比如同一个三角面用(a,c,b)和(b,c,a)两种顺序),就会出现左侧为预期平滑效果、右侧着色断层的情况(如下图)。尝试过flatshading,但在大型网格中效果不佳;对三角顶点索引排序后,着色效果依然随机。

问题原因
Plotly的Mesh3d组件通过三角面的顶点顺序计算法向量:顶点按顺时针或逆时针排列时,法向量方向会完全相反。当相邻三角面的法向量方向不一致时,着色插值的计算逻辑会出现冲突,导致过渡断层;只有所有相邻面的法向量方向统一,才能实现平滑的着色过渡。
解决方案:统一三角面顶点环绕顺序
核心是为所有三角面指定统一的顶点环绕规则(比如采用右手定则:从网格外部看向三角面,顶点按逆时针顺序排列),具体步骤如下:
- 计算每个三角面的法向量
- 判断法向量是否符合统一方向(比如朝向网格外部,或与整体网格的中心方向一致)
- 若不符合,则反转该三角面的顶点顺序
代码实现与验证
以下是修正后的代码,添加了自动调整顶点顺序的函数,确保所有三角面的法向量方向统一:
import pandas as pd import plotly.graph_objects as go from plotly.subplots import make_subplots import numpy as np def fix_triangle_order(pts, tri): """修正三角面顶点顺序,确保法向量方向统一(朝向网格外部)""" # 获取三角面三个顶点的坐标 p0 = pts.iloc[tri[0]][['x','y','z']].values p1 = pts.iloc[tri[1]][['x','y','z']].values p2 = pts.iloc[tri[2]][['x','y','z']].values # 计算法向量 v1 = p1 - p0 v2 = p2 - p0 normal = np.cross(v1, v2) normal = normal / np.linalg.norm(normal) # 计算网格中心到三角面重心的向量,作为外部方向参考 centroid = (p0 + p1 + p2) / 3 mesh_center = pts[['x','y','z']].mean().values outward_dir = centroid - mesh_center outward_dir = outward_dir / np.linalg.norm(outward_dir) # 若法向量与外部方向夹角大于90度,反转顶点顺序 if np.dot(normal, outward_dir) < 0: return [tri[0], tri[2], tri[1]] return tri # 原始顶点与三角面数据 t1 = [0, 3, 1] t2 = [0, 1, 2] t2_alt = [0, 2, 1] # 顺序反转的三角面 t3 = [1, 3, 2] pts = pd.DataFrame({ 'x': [0, 1, 0, 2], 'y': [0, 1, 2, 1], 'z': [0, 0, 1, 0], }) # 修正所有三角面的顶点顺序 surf1_fixed = pd.DataFrame([fix_triangle_order(pts, t1), fix_triangle_order(pts, t2), fix_triangle_order(pts, t3)], columns=['a', 'b', 'c']) surf2_fixed = pd.DataFrame([fix_triangle_order(pts, t1), fix_triangle_order(pts, t2_alt), fix_triangle_order(pts, t3)], columns=['a', 'b', 'c']) fig = make_subplots(rows=1, cols=2, specs=[[{'type': 'mesh3d'}, {'type': 'mesh3d'}]]) fig.add_trace(go.Mesh3d( x=pts.x, y=pts.y, z=pts.z, i=surf1_fixed.b, j=surf1_fixed.a, k=surf1_fixed.c, color='blue' ), row=1, col=1) fig.add_trace(go.Mesh3d( x=pts.x, y=pts.y, z=pts.z, i=surf2_fixed.b, j=surf2_fixed.a, k=surf2_fixed.c, color='blue' ), row=1, col=2) fig.show()
效果说明
运行上述代码后,两个子图的着色效果都会保持一致的平滑过渡——无论原始三角面的顶点顺序如何,fix_triangle_order函数都会将其调整为符合统一规则的顺序,确保所有相邻面的法向量方向一致,从而消除着色断层。
内容的提问来源于stack exchange,提问作者Squanchy
相关产品推荐
相关产品推荐

