如何在MATLAB中实现3D图的平滑旋转?

8
如果我尝试使用plot3旋转摄像机围绕当前图形,则需要进行以下操作:
while true; camorbit(0.9,-0.1); drawnow; end

然后旋转会周期性地卡住一段时间(例子),即使在8核MacPro上也是如此。

我能让它更平滑吗?

编辑1:

虽然目前还没有解决我的原始问题的方案,但我已经使用getframe函数制作了更好的电影。不过,它不允许自由旋转录制,并且在Mac的MATLAB2010b中相当有缺陷。

%# fix wrong figure position in MATLAB2010b for Mac - depends on your layout
correctedPosition = get(gcf,'Position') + [21 -125 0 0];

fps = 60; sec = 10;

vidObj = VideoWriter('newfile.avi');
vidObj.Quality = 100;
vidObj.FrameRate = fps;

open(vidObj);
for i=1:fps*sec
  camorbit(0.9,-0.1);
  writeVideo(vidObj,getframe(gcf, correctedPosition));
end
close(vidObj);

编辑2:

我在MATLAB Central创建了一个类似的帖子。

编辑3:

您可以尝试自行下载我的其中一个图形

3个回答

4
我认为你正在绘制的大量点可能导致减速。一种选择是下采样。此外,您可以使用较低级别的函数进行绘制(请参见this related post以比较plot3/scatter3/line性能)。
考虑以下针对速度优化的动画:
[X Y Z] = sphere(64);
X = X(:); Y = Y(:); Z = Z(:);

%# set-up figure
hFig = figure('Backingstore','off', 'renderer','zbuffer');

%# use lower-level function LINE
line(0.50*[X,X], 0.50*[Y,Y], 0.50*[Z,Z], 'LineStyle','none', 'Marker','.', 'MarkerSize',1, 'Color','r')
line(0.75*[X,X], 0.75*[Y,Y], 0.75*[Z,Z], 'LineStyle','none', 'Marker','.', 'MarkerSize',1, 'Color','g')
line(1.00*[X,X], 1.00*[Y,Y], 1.00*[Z,Z], 'LineStyle','none', 'Marker','.', 'MarkerSize',1, 'Color','b')
view(3)

%# freeze the aspect ratio to override stretch-to-fill behaviour
axis vis3d

%# fix the axes limits manually
%#set(gca, 'xlim',[-1 1], 'ylim',[-1 1], 'zlim',[-1 1])
axis manual

%# maybe even remove the tick labels
%set(gca, 'xticklabel',[], 'yticklabel',[], 'zticklabel',[])

%# animate (until figure is closed)
while ishandle(hFig); camorbit(0.9,-0.1); drawnow; end

alt text

注意我们正在使用 Z 缓冲区渲染器,并关闭了Backstore属性。

编辑:

如果我理解正确,您想要做的是使用第三方应用程序录制屏幕录像,同时手动旋转图形,但在您的情况下,这些手动旋转是“跳跃”的。另一方面,在while循环中使用CAMORBIT / VIEW使您的图形动画化运行顺畅...

我提出了一种替代方案:首先使用鼠标旋转图形,并在每个步骤(方位角,俯仰角)写入这些视图配置。然后您可以使用VIEW函数重播它们,同时记录视频,类似于:

v = [...];   %# matrix where each row specify Az/El of view
for i=1:size(v,1)
    view( v(i,:) )
    drawnow
end

缺点是您需要使用鼠标进行小步操作(ROTATE3D对象不公开鼠标移动事件)
我编写了一个简单的函数来帮助您完成此过程。它加载保存的图形,启用3D旋转,并跟踪每个步骤的中间位置。完成后,按“完成”按钮以返回视图列表...
function v = rotationDemo(figFileName)
    views = [];                     %# list of views (Az,El)

    hFig = hgload(figFileName);     %# load the saved figure

    views(1,:) = get(gca,'View');   %# store initial view

    %# add a button, used to terminate the process
    hButton = uicontrol('Style','pushbutton', 'Position',[400 1 80 20], ...
                        'String','Done?', 'Callback',@buttonCallback);
    set(hFig, 'Toolbar','figure')   %# restore toolbar

    %# start 3d rotation, and handle post-callback to record intermediate views
    h = rotate3d(hFig);             %# get rotation object
    set(h, 'ActionPostCallback',@rotateCallback)
    set(h, 'Enable','on')           %# enable rotation

    msgbox('Rotate the view step-by-step', 'rotate3d', 'warn', 'modal')

    uiwait(hFig)                    %# wait for user to click button
    delete(hButton)                 %# delete button on finish
    set(h, 'Enable','off')          %# disable rotation
    v = round(views);               %# return the list of views

    %# callback functions
    function rotateCallback(o,e)
        views(end+1,:) = get(e.Axes,'View');  %# add current view to list
    end
    function buttonCallback(o,e)
        uiresume(gcbf)                        %# uiresume(hFig)
    end
end

alt text

你可以调用上述函数,然后重新播放动画:
v = rotationDemo('smooth_rotation.fig');
for i=1:size(v,1)
    view(v(i,:))
    drawnow
end

我们可以通过简单的插值来平滑过渡:
v = rotationDemo('smooth_rotation.fig');
n = size(v,1);
nn = linspace(1,n,100)';     %'# use 100 steps
vv = round( [interp1(v(:,1),nn) interp1(v(:,2),nn)] );
for i=1:size(vv,1)
    view(vv(i,:))
    DRAWNOW                  %# or PAUSE(..) to slow it down
end

作为一个附注,我应该提到ROTATE3D和CAMORBIT有不同的效果。ROTATE3D改变当前轴的View属性,而CAMORBIT控制当前轴的相机属性CameraTarget/CameraPosition/CameraUpVector。

看起来你是对的,OpenGL渲染器仍然比绘图器快。在这种情况下,似乎zbuffer渲染器甚至更快。 - Amro
@Amro,非常感谢您详细的回答!也许我错了,但是似乎按照您的建议,在挂起之间旋转更加平滑,但它们仍然以相同的持续时间发生!我无法降采样图 - 它必须保持现在的状态。 - Andrei Fokau
1
@Andrei Fokau:你试过用LINE而不是PLOT3吗?我相信它会给你带来显著的提升。在上面的例子中,我们正在绘制大约12000个点,而且在我的三年旧笔记本电脑上运行非常流畅。 - Amro
1
@Andrei Fokau:只是为了明确,您是直接查看动画窗口还是将其记录到AVI文件中,然后遇到挂起(如果是,请在代码的该部分发表评论)?因为即使有那么多点,对我来说仍然很流畅(没有突然跳跃),但显然运行速度较慢。 - Amro
@Amro,当我手动旋转或使用camorbit()旋转时,它会卡住。制作电影是我的最初目的,而getframe允许通过camorbit进行平滑旋转来制作这样的电影。然而,对于自由手旋转,它并没有帮助,而我想要这样的功能。 - Andrei Fokau
显示剩余4条评论

2
我在正常的MATLAB图中也注意到了你所说的相同的抽搐运动。但是当我尝试运行Amro的代码并创建一个电影(*.AVI),它在我的Mac笔记本电脑上看起来很流畅。
我使用的电影制作代码如下:
%在制作电影时添加了figure“可见”属性,如下所示:
hFig = figure('Backingstore','off','visible','off','renderer','zbuffer');
%然后,我用一个简单的AVI生产循环替换了Amro的while循环,如下所示:
aviobj=avifile('test.avi'); %创建AVI文件 for I=1:360 camorbit(0.9,-0.1); drawnow; aviobj=addframe(aviobj,hFig); %将帧添加到AVI文件中 end aviobj=close(aviobj); %关闭AVI文件 close(hFig); %关闭hFig
问题: 减少一些点或在渲染图之前创建密度图是否有帮助?
关于各种渲染选项的参考文献:[http://www.mathworks.com/support/tech-notes/1200/1201.html]
我希望上面的评论能对您有所帮助。

谢谢你的示例!我可以制作一个流畅的电影。问题是我无法记录平稳的手绘旋转(通过第三方应用程序制作电影)。你能试着旋转我的示例吗?http://goo.gl/LA1Cg - Andrei Fokau
1
Andrei,你的例子在我的Mac笔记本上运行顺畅。你能否通过指定整个电影的视图路径来创建一个电影呢?例如,使用循环,view(az,el)和pause等方式。这样,你就可以创建一个流畅的电影,无需手动操作(制作电影时不要忘记关闭屏幕保护程序)。或者,如果你有MATLAB编译器,就可以将代码编译成独立应用程序,并允许用户交互式旋转图片。最后,我想可能是轴的“AspectRatio”没有固定,但你的图形已经固定了,所以这不是问题所在。无论如何,祝你好运。 - Y.T.

1

我不知道这个建议是否能帮到你,但出于某种原因,我使用 pause(0.001) 要比使用 drawnow 更成功地强制更新图形窗口。

你也可以尝试看看是否使用 rotate3d 更快。

核数并不像你想象的那么重要,因为 Matlab 中许多函数并不支持多线程。


一个解决方法是继续按照您现在的方式进行,但将图形窗口写入电影文件。然后您可以回放电影。

谢谢您的建议。我已经尝试过了,但它并没有改变行为。 - Andrei Fokau
是的,我刚刚在我的问题中添加了这个选项。这种方法有一些限制,但它可以工作。 - Andrei Fokau
@Andrei - 最后一件要尝试的事情是调整图形窗口的大小。当窗口最大化时,我有时会出现暂停,而在默认大小时则没有。 - Marc
是的,我已经尝试过这个了,但是直到一个非常微小的数字,什么都没有真正改变。 - Andrei Fokau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接