拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MATLAB surf函数详解:从基础语法到三维曲面高级美化

MATLAB surf函数详解:从基础语法到三维曲面高级美化 1. 先理解surf它到底画的是什么1.1 一张图理解曲面绘制的本质如果你是用MATLAB做数据处理、做科研绘图、做工程仿真或者毕业设计里需要把结果“可视化”出来那surf这个函数你迟早要碰。很多人第一次用它是因为读数据、算完Z矩阵之后想看看“这堆数到底长什么样”——于是敲下surf(Z)结果出来一个灰蒙蒙的、带黑线的、看着像铁丝网又像彩色塑料膜的玩意儿。这基本上就是每个新手面对surf的第一印象丑、看不懂、不知道怎么调。先说清楚surf到底在干什么。它本质上是把一个二维矩阵Z(m×n)里的每一个数值当成三维空间中的高度值然后在一个平面上把对应的点画出来再用平面网格拼成一张曲面。这张面上高度越高的地方网格顶点越往上顶高度越低的地方越往下凹。所以surf生成的东西不是“线框”而是一张被填充了颜色的实际曲面。这个和mesh的区别很关键mesh只画线框是“透”的surf画填充面是“实”的。同一个数据用mesh看到的是骨架用surf看到的才是完整的表面形态。举个例子MATLAB内置了一个经典数据peaks它生成一个带两个高峰一个低谷的曲面。直接用surf(peaks)看一眼你立刻就能理解“把矩阵画成地形图”这件事它就像把一个山丘的等高线图变成了真正能转动的立体模型。Z peaks(30); % 30x30 的矩阵模拟一个起伏地形 surf(Z);这一句代码是你踏上三维可视化这条路的第一步。后面所有高级操作包括调颜色、加光照、控制视角都是在这一句的基础上往细节里抠。1.2 什么场景必须用surf什么场景该换别的surf不是万能的。我见过不少人拿着一个稀疏散点数据集硬上surf结果出来的图全是大窟窿还跑来问“为什么我的图破破烂烂的”。那是因为surf适合处理的是规则网格数据——也就是说X、Y是网格坐标Z是在每个网格点上的高度值。如果你的数据是(xi, yi, zi)这样的散点没有规则网格那先考虑scatter3画散点或者用griddata把散点插值到网格上再surf不要硬画。实际场景里surf最常用在这么几个方向二维函数可视化比如想要观察一个数学函数在平面范围内的起伏形态有限元计算结果展示比如一个平板受力后的位移场、温度场雷达、通信、声学等领域的二维分布数据比如方向图、声压分布深度图、高程图这类地形数据配合colormap可以模拟出卫星地形图的视觉效果。如果你只是想快速看一下三维曲线那plot3就够了如果想画柱状堆叠或者类别数据那更适合bar3。surf的真正价值是把“连续的、规则的、二维分布的数据”变成一张可以旋转、可以打光、可以从任意角度看细节的曲面模型。2. surf语法拆解从基础调用到完整参数2.1 最常用的三种调用方式surf的语法看着多但剥开来看核心就三种形态。第一种最省事的写法surf(Z);这时候MATLAB会自动把Z矩阵的行当Y轴列当X轴坐标范围就是矩阵的索引值。适合你手里只有一堆高度数据、不关心横纵坐标具体刻度的情况。比如上面对peaks调用的就是这个形式。第二种带坐标的写法surf(X, Y, Z);这里X、Y必须是矩阵且尺寸和Z一致。X和Y一般由meshgrid生成。这是最标准的用法因为只有带上真实的坐标信息曲面上的位置才有物理意义。x -2:0.1:2; y -2:0.1:2; [X, Y] meshgrid(x, y); Z X.^2 - Y.^2; % 马鞍面 surf(X, Y, Z);第三种带颜色数据的完整写法surf(X, Y, Z, C);C是另一个和Z同尺寸的矩阵它决定每个网格位置上的颜色而不是直接用Z的高度决定颜色。这一点很实用比如你的Z是地形高度但你想用温度数据给曲面着色画出的是地形曲面颜色却对应温度分布这样能在一张图里表达两个维度的信息。这种情况在科研作图里非常常见。2.2 meshgrid到底干了什么每个新手都会被meshgrid卡一下。用大白话讲X轴坐标是一堆数Y轴坐标也是一堆数但你画的是曲面不是曲线所以需要知道“平面上的每一个点”对应的 (x, y) 组合是什么。meshgrid做的就是这件事把一维的x数组和y数组扩展成二维的坐标矩阵。x 1:3 y 10:10:30 [X, Y] meshgrid(x, y)运行一下你会发现X矩阵每一行都是1 2 3Y矩阵每一列都是10 20 30。这样X(2,3)和Y(2,3)组合起来就是平面上第2行第3列那个点的坐标。所以后面计算Z f(X, Y)就能一步算出整个网格上的高度值。提示如果不小心把meshgrid的两个参数写反了也就是[X,Y] meshgrid(y, x)那么生成的X、Y会交换画的曲面会横竖颠倒而且后续用到X、Y的地方全跟着错。这种低级错误排查起来特别费眼建议在写代码前先明确x对应列、y对应行。2.3 高级调用给曲面绑定颜色数据C上面提到surf(X, Y, Z, C)可以单独指定颜色数据。我再展开讲讲这个用法背后的小细节。默认情况下MATLAB根据Z值的大小自动映射颜色高的地方偏暖低的地方偏冷默认colormap是parula蓝到黄。但很多场景下颜色不一定非要跟高度挂钩。假设你有一个二维水流速度场Z是河床高度C是流速分布那么一张图上既能看到河床起伏又能通过颜色直观看出哪里流速快、哪里流速慢。这类“双变量可视化”靠surf的C参数实现比单独画两张图要直观得多。还有一种常见操作直接用surf(X, Y, Z, ones(size(Z)))把所有颜色设成同一个值然后配合自定义colormap把整张曲面固定成一种颜色。这在做技术报告、需要统一色调的插图时很实用。2.4 一条命令的变化surfc、surfl与shadingsurf还有几个近亲我建议你顺手学了因为它们解决的问题各不相同。surfc是surf加等高线投影在曲面下方自动画出等高线非常适合看地形数据——一方面可以看到三维起伏另一方面在XY平面上还能看到精确的等高线刻度。surfc(peaks);surfl是带光照的surf它会在生成曲面前自动加上光源模拟效果上比普通surf立体很多缺点是默认配色偏灰颜色表达不够鲜明需要配合colormap一起调。surfl(peaks); colormap(gray); shading interp;shading命令则是surf的好帮手它控制的是每个网格面的着色方式shading faceted默认方式每个小网格面用纯色填充边缘保留黑色线框shading flat每个网格面用纯色填充但没有黑色边缘线比faceted干净一些shading interp相邻网格顶点颜色间做渐变插值整个曲面看起来平滑连续没有明显的网格分界线。如果想让曲面显得细腻、高级我基本每次都加一句shading interp。它不需要额外计算只是渲染方式不同却能立刻把“像素块”变成“丝绸面”。3. 颜色设置从默认配色到专业级绘图3.1 colormap的原理和选择surf曲面图的颜色不是“画”上去的而是通过色图映射得到的。MATLAB里有一个叫colormap的东西它本质上是一个颜色查找表竖向排列的一组颜色从底部到顶部依次代表数值范围的低端到高端。曲面每个网格点根据它的数据值在颜色表里找到对应位置的那一个颜色然后填充。你敲一行colormap(parula)是在换一套颜色表这个决定直接影响整张图的信息表达方式。MATLAB早期默认是jet彩虹色高饱和、蓝红绿黄跨度极大视觉很热闹但科学界多年来一直在批判这种配色原因是它的颜色明暗变化不单调会让观者产生“某些数值区间更突出”的错觉而且对色盲读者不友好。现在的默认parula是从黄到深蓝的渐变色亮度变化均匀是MATLAB官方推荐的默认方案。我在实际绘图中的选择经验是这样的数据是连续地形、场分布这类首选parula或者turbo细节清楚需要灰度图导出配期刊需求用gray想模拟热力图、火力分布用hot这种色图从黑到红再到黄白像热成像不想用彩虹色又想表达很强对比可以试试viridis要在线下载或自建观感柔和色盲友好。3.2 用C矩阵控制每个网格的颜色前面提过C参数可以绑定颜色数据这里再说深一层。很多人在surf之后调用colormap发现颜色变化范围对不上原因就出在颜色数据范围和数据值的映射上。MATLAB把C矩阵中的数值范围映射到colormap的完整色带上C最小值对应色带最底部颜色C最大值对应最顶部颜色。默认情况下如果没有传C那就用Z当C。如果你想调整“哪种颜色对应哪个数值范围”就用clim老版本是caxis来手动指定颜色映射的范围surf(X, Y, Z, C); clim([min_val, max_val]); % 让颜色只映射到你关心的一段数值区间这句话的作用很多人没意识到。比如数据整体范围是0到100但你关心的细节集中在20到40之间如果全程用0到100来映射颜色那20到40这段的颜色差异会被压缩得几乎看不出来。把clim设成[20, 40]这部分细节立刻“撑开”颜色层次就出来了。代价是超出这个区间的部分会统一显示成两端颜色不再有梯度。所以在科研数据里要谨慎使用别为了让图好看而误导读图的人。3.3 色标范围与colorbar细节colorbar不是可有可无的装饰。三维曲面图上颜色编码如果不配一个可读的色标读者只能看到“有颜色变化”却没有办法知道颜色到底对应什么数值。加一行colorbar就完事但这里面也有讲究。surf(peaks); colormap(parula); colorbar;出来了默认色标它显示的是曲面上的颜色对应的一列数值刻度。如果你觉得默认刻度不够美观可以用colorbar(Ticks, [tick1, tick2, ...])手动设定刻度位置或者用colorbar(TickLabels, {...})修改刻度文字。比如数据是压强你想显示成“100 kPa、200 kPa”这种带单位的样式用后者就很方便。另一个值得记的小技巧色标标签用ylabel(colorbar, Pressure (kPa))加上去这样图的自明性直接上一个档次不用读者再猜。三维图本身信息量大配上一个清晰的色标整个图才算闭环。注意如果你在同一张figure里用了多个subplot并且每个subplot都画了surf那每个subplot的colorbar默认是根据各自数据范围生成的。如果你希望几个子图的颜色代表相同的数值范围必须在每个subplot里统一设置clim否则同一个色标下对应关系完全不一样那这张对比图就废了。4. 细节展示实战光照、视角、透明度与网格控制4.1 光照对象让曲面有立体感的唯一正解surf默认画出来的曲面即使有颜色看起来也“平”——因为颜色是直接根据数值映射的没有考虑曲面本身在不同角度下的反光差异。人眼感知三维立体靠的就是光影凸起的地方亮凹陷的地方暗。surf在这种感知上帮不了忙它需要你主动加一盏“灯”。MATLAB里加灯分两步light(Position, [1, 1, 1]); % 光源放在右上方的立体坐标 lighting gouraud; % 选择光滑光照模式 material metal; % 材质金属质感会让曲面反光更强灯光位置Position是一个三维坐标表示光源在空间中的位置。[1, 1, 1]表示放在右前上方这样曲面会呈现自然的亮部和暗部。lighting有三个可选参数flat、gouraud、phong新版本中phong基本由gouraud代替。我推荐gouraud它在三角形网格顶点上计算光照再插值到面上效果平滑自然。material控制材质属性常见的有shiny高反射亮部集中适合展示光学器件类数据dull漫反射强整体柔和适合展示粗糙表面metal金属反光对比强烈适合结构件或工程展示。加完光照曲面会有明显的明暗转折立体感瞬间就出来了。这是我每次给surf做“深度美化”时最先做的一步比调一百行颜色参数都来得快。4.2 视角与观察方向怎么把细节“调”出来surf的优势在于交互旋转——图形窗口里直接用鼠标拖拽就能转视角。但如果你要导出图片、写报告就不能指望交互式体验了必须学会用view固定视角。view(az, el);az是方位角单位是度表示观察者在水平面上绕Z轴旋转的角度。0度表示从X轴正向看90度从Y轴正向看。el是仰角表示视线与水平面的夹角正数是从上方俯视负数是从下方仰视。常用的视角组合view(0, 90); % 从正上方垂直往下看相当于二维俯视图 view(90, 0); % 从Y轴方向水平看侧视 view(-37.5, 30);% 经典三维视角类似默认值 view(45, 45); % 等轴测视角适合展示全局形态还有一个细节axis equal和axis tight是两个经常被忽略但影响巨大的命令。axis tight让坐标轴贴近数据避免画面边缘有一大片空白axis equal让三个轴的比例一致防止曲面被拉伸变形。在三维图中用axis tight更利于聚焦数据本身。4.3 边缘、网格与透明度的取舍surf默认在每个小网格面边缘会画一圈黑线。数据分辨率低比如10×10网格时黑线会让曲面看起来像“彩色铁丝网”分辨率高时黑线又会把画面弄得又密又脏。两种情况下我都建议把边缘线去掉或者调淡surf(X, Y, Z); shading interp; % 去掉边缘线改用渐变着色 % 或者在surf里直接设置属性 s surf(X, Y, Z); s.EdgeColor none;EdgeColor是surf对象的一个属性可以直接用点语法改。更细一点还能单独调边缘透明度s.EdgeAlpha 0.2; % 半透明边缘保留网格线但不喧宾夺主如果你希望保留网格线来表现数据的采样疏密把EdgeAlpha设成0.1到0.3之间是个平衡点。完全主观但工程绘图里这招好用。透明度还有一个场景非常实用当你的曲面有遮挡你想同时看前表面和背后的结构时用alpha命令alpha(0.7); % 整体透明度0.7能看到曲面后面的东西不过要提醒一句透明度和光照同时使用的组合要注意透明曲面在有些渲染模式下会出现奇怪的穿透效果。如果遇到这种情况把透明度调回接近1或者改用AlphaData控制局部透明度问题基本能解决。5. 常见问题与排查技巧实录5.1 高频问题速查表这段我直接做成表格方便你日后遇到问题时快速定位。现象原因解决思路画出来的曲面像锯齿、有明显的棱角Z矩阵分辨率太低网格太少网格点加到50×50以上或用interp2对Z平滑插值曲面颜色一块一块的边界色差很大默认faceted着色每个面一个颜色加shading interp平滑颜色过渡颜色和Z高度不匹配高点和低点却一样颜色可能在surf里显式传了CC和Z来源不一致去掉C参数或检查C的数据范围是否合理曲面明明有起伏看起来却很平缺少光照人眼感知不到立体用light、lighting gouraud、material metal图形窗口里转视角很卡网格点太多渲染开销大先降采样画图例如ZZ(1:2:end,1:2:end)或只用surf的ReduceData显示导出图片模糊默认分辨率低用exportgraphics(gcf, out.png, Resolution, 300)导出边缘黑线密密麻麻看不清颜色EdgeColor默认给每个面画黑线设置EdgeColornone或EdgeAlpha0.1多个subplot图形色彩深浅不一致各子图clim范围不同在绘图前统一clim([全局最小值, 全局最大值])曲面背后的内容看不到默认是不透明曲面alpha(0.5)或对前后方单独设置透明度5.2 进阶避坑心得我前面说到的很多都是操作层面下面这几个坑是我在实际带项目时真正踩过、花了时间才搞明白的单独拎出来讲。第一个坑prod了数据再surf结果曲线被“退化”了。我有一个学生做三维应力场分布原始结果在一个节点上算完的应力矩阵特别大画图时为了省内存用了Z imresize(Z, 0.5)这类降采样操作。结果颜色层次丢失细节全糊。后来调整为“先算原始高分辨率数据画图时再用插值方式显示低分辨率渲染”效果就好很多。核心经验是surf显示的分辨率和计算分辨率可以分离。你完全可以算一个500×500的Z矩阵画图时用surf(..., Z(1:5:end,1:5:end))降低渲染负担然后shading interp把颜色光滑回去。这样既不卡视觉效果也还在。第二个坑光加到图上之后颜色就“变淡”了。很多新手加了light之后发现曲面颜色不再像之前那么饱和。这是正常的因为光照让一部分面变暗了。解法是调整材质反光参数而不是放弃光照。你可以设置s.AmbientStrength 0.6; % 环境光强度调暗部可见性 s.DiffuseStrength 0.4; % 漫反射强度调颜色柔和度 s.SpecularStrength 0.2; % 镜面高光强度这三组参数可以精确控制“光”和“颜色”之间的配比。一般报告插图我喜欢AmbientStrength0.5、DiffuseStrength0.6、SpecularStrength0.3既立体又保色。第三个坑坐标轴比例不对曲面严重变形。如果X的范围是0到10Y的范围是0到10000那么surf出来的曲面在Y方向会被拉得很夸张。想要保持真实几何比例用axis equal; daspect([1, 1, 1]);第一句让三个轴范围等比例第二句强制数据单位比例相同。但要注意如果三个维度的数值量级差得太离谱这两个命令会导致图像窄成一条线此时说明你的数据本身不适合直接做三维几何展示考虑用对数坐标轴或者归一化后再画。第四个坑也算是个冷门经验surf之后想把figure转成动画或者截图不要用print尤其是新版MATLAB。新版推荐exportgraphics导出质量稳定不会出现线和点变粗、颜色漂移的问题。导出矢量图用exportgraphics(gcf, out.pdf, ContentType, vector)插入论文里清晰度碾压截图。5.3 布局建议一张图怎么放才专业做了这么多种优化最后说下成图时的排版思路。一张专业的surf图读图的人需要几个信息曲面是什么数据、颜色代表什么、坐标轴单位、以及必要的图注。我的固定套路是figure(Color, w); surf(X, Y, Z, C); shading interp; colormap(parula); clim([cmin, cmax]); xlabel(X Label (unit)); ylabel(Y Label (unit)); zlabel(Z Label (unit)); cb colorbar; ylabel(cb, Color Variable (unit)); view(45, 35); light(Position, [2 2 5]); lighting gouraud; material dull; axis tight; set(gca, FontSize, 11); exportgraphics(gcf, surface_plot.png, Resolution, 300);这套代码反复用基本能覆盖我百分之八十的三维作图需求。你完全可以把它存成一个模板每次只要换掉X、Y、Z、C和几个标签就行省时省力还不容易漏参数。我个人在实际操作中的体会是surf最怕的不是语法难而是“功能都会但组合不出效果”。语法只用十分钟就能看完真正拉开差距的是对颜色、光照、视角这三个维度的组合理解。每次拿到新数据我先问自己三个问题这个数据哪一段变化最重要颜色是想表达跟高度一致的信息还是跟另一个变量绑定从哪个角度观察最能呈现特征想清楚了再开画基本一次成型。多画几组数据之后你就会发现三维曲面图其实没那么玄组合拳打熟了就是一套肌肉记忆。
返回列表