如何用Matlab快速画出带有3D渲染效果的复杂曲面

时间:2024-02-24 14:34:07

   Matlab是一个很常用的理工科数学软件,我们平常会用它来画一些平面函数或者简单的曲线或者简单的3D平面图,但是通常很少用到它的稍微高级一点的画图功能。这里介绍一些高级渲染功能和画图技巧,先看结果。

写论文中可能会经常遇到这样的事情,想画一些高级点的漂亮的图不知道怎么画。当然我们通常可能会用solidworks, 3dsmax等等软件画这种3D的图,但是Matlab的好处是可以比较容易生成函数控制的复杂3D曲面形状,就比如上面的鸡蛋盒子一样的晶格图形,看似简单,用其他的软件还真的不太容易画。还有比如像下面这样的波浪图。想要画出这样的图,除了通常的一些画图命令外,还需要掌握一些小的tips,下面来逐一介绍。

 

1、 光照设置

以第二个图为例,它的Matlab代码如下

x=-6*pi:pi/100:6*pi;
y=x;
[X,Y]=meshgrid(x,y);
Z=5*(exp(-(X.^2+Y.^2)/100)+exp(-(X.^2+Y.^2)/4)).*cos(sqrt(X.^2+Y.^2));
figure;surf(X,Y,Z);shading flat; axis off;
view(15,45);axis([min(x),max(x),min(y),max(y),-6,12]);colormap(\'summer\');

%光照渲染

light(\'position\',[0,0,20],\'style\',\'local\',\'color\',\'w\');lighting phong;
light(\'position\',[0,0,20],\'style\',\'local\',\'color\',\'w\');lighting phong;

前面四句是定义二元函数,meshgrid函数是将两个一维数组转化为二维数组以便定义二元函数,此时X,Y,Z都是二维矩阵。

figure;surf(X,Y,Z);是画出3D的表面图,但是这个图通常是带有网格的,而且由于定义的数据间隔小,网格也会很密,整张图看起来一片漆黑。因此需要用shading flat命令来去掉这些网格,此时显示的结果如下图所示,可以看到没什么光泽,不是很好看。上面的view命令是用来设置观察的角度的,axis([min(x),max(x),min(y),max(y),-6,12])设置图显示的区域大小,axis off意思是不显示坐标轴,colormap用来选择Matlab自带的一些色调,比如summer、pink、gray、winter等等。

 

最后要介绍的就是light命令了,在Matlab command 窗口输入 help light命令就可以查看light函数的所有性质,读者可以自行查询,这里只介绍它的功能。light(\'position\',[0,0,20],\'style\',\'local\',\'color\',\'w\');lighting phong; 这段命令中,\'position\'是用来设置光源的位置,\'local\'表示光源是局域的光源而不是从无穷远处射来的平行光。lighting phong是设置光照的方式。用两次同样的light命令是提高光照的亮度,使图片看起来色泽更鲜亮。

 

2、着色设置

上面第一张的图中的球可以在极坐标下写出来,这里期待能够实现更复杂的着色控制,比如像下图这样的,所以代码稍微复杂些。

这个图的代码如下:

theta=-pi/2:pi/50:pi/2;
phi=0:pi/50:2*pi;
[Phi,Theta]=meshgrid(phi,theta);
x=R*cos(Theta).*cos(Phi);
y=R*cos(Theta).*sin(Phi);
z=R*sin(Theta);

%着色控制
color=zeros(length(theta),length(phi),3);
color(:,:,1) = cos(Theta)*cos(Theta0).*cos(Phi-Phi0)+sin(Theta)*sin(Theta0); % red
color(:,:,2) = sin(Theta)*sin(Theta0).*cos(Phi-Phi0)+cos(Theta)*cos(Theta0); % green
color(:,:,3) = sin(Theta)*sin(Theta0).*cos(Phi-Phi0); % blue
figure;
surf(x,y,z,color);shading flat;axis off;
view(15,45);axis([-1 1 -1 1 -1 1]);colormap(\'summer\');
light(\'position\',[0,0,20],\'style\',\'local\',\'color\',\'w\');lighting phong;
light(\'position\',[-20,0,0],\'style\',\'local\',\'color\',\'w\');lighting phong;

前面到x,y,z的定义和上面一样,都是定义二元函数。这里唯一的不同点在于color变量的使用,我们可以自定义每个数据点的红、绿、蓝三个颜色的数值,就可以组合出任意的颜色。这样就几乎可以把这个图涂上任意的想要的颜色。曲面形状拓展一下,还可以画一个铁蒺藜的图。

 

3、材质设定

 Material命令是设置3D 表面的材料的,具体点说主要是反射光的性质,它的命令格式为 material([ka kd ks]);分别设置环境光、漫反射、镜面反射的反射程度,ka,kd,ks的值均在0到1之间。组合设置一下可以得到更细腻的3D表面的质感。

 

 从上图可以看到设置material后物体表面对光线的反射会更柔和点。

 

4、透明度设置

 

透明度的设置可以直接通过surf命令surf(x,y,z,color,\'FaceAlpha\',0.3);来设置,这里的\'FaceAlpha\'就是设置面的透明度,数值在0到1之间,0表示完全透明,1表示完全不透明。

 

5、动画控制

最后是动画的控制,Matlab可以直接生成动画,动画的每一帧都是一个图,因此每个图都需要画出来。可以用getframe得到每一帧,用movie函数播放动画。下面有个完整的例子。

x=-6*pi:pi/20:6*pi;
y=x;
[X,Y]=meshgrid(x,y);
loops=200;
for j=1:loops
    Z=5*cos(j/20*pi-sqrt(X.^2+Y.^2)).*(exp(-(X.^2+Y.^2)/100)+exp(-(X.^2+Y.^2)/4));
    surf(X,Y,Z);axis off;shading flat;
    view(15,45);axis([min(x),max(x),min(y),max(y),-6,12]);colormap(\'summer\');
    light(\'position\',[0,0,20],\'style\',\'local\',\'color\',\'w\');lighting phong;
    light(\'position\',[0,0,20],\'style\',\'local\',\'color\',\'w\');lighting phong;
    drawnow;
    F(j)=getframe;
end
movie(F,1,20); % 播放1次,每秒钟20张图

% 将动画存为一个视频文件

aviobj = VideoWriter(\'water wave.avi\');
aviobj.FrameRate = 20;
open(aviobj);
writeVideo(aviobj,F);
close(aviobj);

动画的每一帧是存在F这个变量里的,然后用movie(F,1,20)来播放动画。首先需要用VideoWriter函数建立一个新的视频文件,这个视频文件在Matlab里的操作是一个对象aviobj,我们可以 设置Framerate为20,即每秒钟播放20帧。open(aviobj)和close(aviobj)是打开和关闭文件的操作,writeVideo(aviobj,F)是把F中的动画存在aviobj里,也就是存在water wave.avi文件里,matlab默认生成avi文件。之后打开water wave.avi文件就可以看到生成的动画了。

 

6、总结

 本文介绍了用Matlab画3D 物体和3D表面的一些方法。Matlab的优势是可以非常方便的用函数去构建复杂的3D物体和表面,使得可以非常简单地表达复杂的3D曲面,通过一些材质和光线渲染可以画出比较漂亮的图或者动画。其缺点是这样的图的数据量相对较大,所画的曲面不太容易导出为3D 模型,因此Matlab画这种图的主要作用可能是在科研上,而非画模型上。当然一种解决方案是,直接用Matlab写一个程序直接生成3D模型的数据文件,或许可以实现把Matlab的编程优势与3D建模软件的优势结合在一起,实现更复杂的3D建模。