Текстовая анимация с MATLAB

Моя упрощенная проблема заключается в анимации какого-либо текста на 3D-графике.

У меня есть куб,

vert = [1 1 0;0 1 0;0 1 1;1 1 1;0 0 1;1 0 1;1 0 0;0 0 0];
fac =  [1 2 3 4; 4 3 5 6; 6 7 8 5; 1 2 8 7; 6 7 1 4; 2 3 5 8];
patch('Faces',fac,'Vertices',vert,'FaceColor',[.8 .5 .2]); 
axis([0, 1, 0, 1, 0, 1]);
axis equal
axis off

enter image description here

Можно ли получить что-то вроде этого?

enter image description here

Использование text не помогает (он выглядит поддельным!),

Спасибо,

Ответ 1

Идея состоит в том, чтобы использовать отображение текстур как @Hoki, показанное. Я попытался реализовать это на моем конце, вот что я придумал.

Сначала нам понадобится 6 изображений для нанесения на грани куба. Это могут быть любые случайные изображения любого размера. Например:

% just a bunch of demo images from IPT toolbox
function imgs = get_images()
    imgs = {
        imread('autumn.tif');
        imread('coloredChips.png');
        imread('toysflash.png');
        imread('football.jpg');
        imread('pears.png');
        imread('peppers.png');
    };
end

Еще лучше, позвольте использовать онлайн-сервис, который возвращает образы заполнителей, содержащие цифры от 1 до 6:

% online API for placeholder images
function imgs = get_images()
    imgs = cell(6,1);
    clr = round(255*brighten(lines(6),0.75));
    for i=1:6
        %bg = randsample(['0':'9' 'a':'f'], 6, true);
        %fg = randsample(['0':'9' 'a':'f'], 6, true);
        bg = strjoin(cellstr(dec2hex(clr(i,:))).', '');
        fg = strjoin(cellstr(dec2hex(clr(7-i,:))).', '');
        [img,map] = imread(sprintf(...
            'http://placehold.it/100x100/%s/%s&text=%d', bg, fg, i));
        imgs{i} = im2uint8(ind2rgb(img,map));
    end
end

Вот результирующие изображения:

>> imgs = get_images();
>> montage(cat(4,imgs{:}))

digit_images

Затем создайте функцию, которая отображает куб единичного изображения с изображениями, отображаемыми в виде граней:

function h = get_unit_cube(imgs)
    % we need a cell array of 6 images, one for each face (they can be any size)
    assert(iscell(imgs) && numel(imgs)==6);

    % coordinates for unit cube
    [D1,D2,D3] = meshgrid([-0.5 0.5], [-0.5 0.5], 0.5);

    % texture mapped surfaces
    opts = {'FaceColor','texturemap', 'EdgeColor','none'};
    h = zeros(6,1);
    h(6) = surface(D1, flipud(D2), D3, imgs{6}, opts{:});           % Z = +0.5 (top)
    h(5) = surface(D1, D2, -D3, imgs{5}, opts{:});                  % Z = -0.5 (bottom)
    h(4) = surface(fliplr(D1), D3, flipud(D2), imgs{4}, opts{:});   % Y = +0.5 (right)
    h(3) = surface(D1, -D3, flipud(D2), imgs{3}, opts{:});          % Y = -0.5 (left)
    h(2) = surface(D3, D1, flipud(D2), imgs{2}, opts{:});           % X = +0.5 (front)
    h(1) = surface(-D3, fliplr(D1), flipud(D2), imgs{1}, opts{:});  % X = -0.5 (back)
end

Вот как это выглядит:

imgs = get_images();
h = get_unit_cube(imgs);
view(3), axis vis3d, rotate3d on

unit_cube


Теперь мы можем немного повеселиться, создавая интересные анимации. Рассмотрим следующее:

% create two separate unit cubes
figure('Renderer','OpenGL')
h1 = get_unit_cube(get_images());
h2 = get_unit_cube(get_images());
set([h1;h2], 'FaceAlpha',0.8)         % semi-transparent
view(3), axis vis3d off, rotate3d on

% create transformation objects, used as parents of cubes
t1 = hgtransform('Parent',gca);
t2 = hgtransform('Parent',gca);
set(h1, 'Parent',t1)
set(h2, 'Parent',t2)

% transform the second cube (scaled, rotated, then shifted)
M = makehgtform('translate', [-0.7 1.2 0.5]) * ...
    makehgtform('yrotate', 15*(pi/180)) * ...
    makehgtform('scale', 0.5);
set(t2, 'Matrix',M)

drawnow
axis on, axis([-2 2 -2 2 -0.7 1]), box on
xlabel x, ylabel y, zlabel z

% create animation by rotating cubes 5 times
% (1st rotated around z-axis, 2nd around its own z-axis in opposite
%  direction as well as orbiting the 1st)
for r = linspace(0,10*pi,90)
    R = makehgtform('zrotate', r);
    set(t1, 'Matrix',R)
    set(t2, 'Matrix',R\(M/R))
    pause(0.1)
end

orbiting_animation

Я использую hgtransform для управления преобразованиями, это гораздо эффективнее, чем постоянное изменение точек данных x/y/z графических объектов.

BTW Я использовал несколько разных изображений в анимации выше.


EDIT:

Позвольте заменить вращающиеся кубы изображениями планеты Земля, нанесенными на сферы. Прежде всего, это две функции для визуализации сфер (я заимствую код из эти examples в документации MATLAB):

get_earth_sphere1.m

function h = get_earth_sphere1()
    % read images of planet earth
    earth = imread('landOcean.jpg');
    clouds = imread('cloudCombined.jpg');

    % unit sphere with 35x35 faces
    [X,Y,Z] = sphere(35);
    Z = flipud(Z);
    a = 1.02;

    % render first sphere with earth mapped onto the surface,
    % then a second transparent surface with clouds layer
    if verLessThan('matlab','8.4.0')
        h = zeros(2,1);
    else
        h = gobjects(2,1);
    end
    h(1) = surface(X, Y, Z, earth, ...
        'FaceColor','texturemap', 'EdgeColor','none');
    h(2) = surface(X*a, Y*a, Z*a, clouds, ...
        'FaceColor','texturemap', 'EdgeColor','none', ...
        'FaceAlpha','texturemap', 'AlphaData',max(clouds,[],3));
end

get_earth_sphere2.m

function h = get_earth_sphere2()
    % load topographic data
    S = load('topo.mat');
    C = S.topo;
    cmap = S.topomap1;
    n = size(cmap,1);

    % convert altitude data and colormap to RGB image
    C = (C - min(C(:))) ./ range(C(:));   % scale to [0,1]
    C = ind2rgb(round(C*(n-1)+1), cmap);  % convert indexed to RGB

    % unit sphere with 50x50 faces
    [X,Y,Z] = sphere(50);

    % render sphere with earth mapped onto the surface
    h = surface(X, Y, Z, C, ...
        'FaceColor','texturemap', 'EdgeColor','none');
end

Анимация script похожа на предыдущую (с незначительными изменениями), поэтому я не собираюсь повторять ее. Вот результат:

planets

(На этот раз я использую новую графическую систему в R2014b)

Ответ 2

У меня есть решение, которое делает ok с помощью texture mapping.

Идея заключается в том, чтобы применить изображение к лицу и позволить Matlab заботиться обо всем остальном. Большим преимуществом является то, что Matlab позаботится обо всех перспективных аспектах, а рендеринг очень хорош. Небольшой недостаток заключается в том, что вы можете применять текстуру только к объектам surface, и поскольку вы хотите 6 разных изображений, вам нужно будет управлять 6 различными поверхностями.

В приведенном ниже коде показан пример того, как это сделать:

%% // read faces images
idxFaces = [1 2 3 4 5 6] ;
for iface = idxFaces
    imgface{iface} = imread( ['E:\ICONS\number_blue_' num2str(iface) '-150x150.png'] ) ;
end

%% // Define cube properties
cubeLenght = 1 ;
x = linspace(-cubeLenght/2,cubeLenght/2,21) ;
[X,Y] = meshgrid(x,x) ;
Zp = ones(size(X))*cubeLenght/2 ; Zm = Zp-cubeLenght ;

%// draw face surfaces (organised 2 by 2)
hcubeface(1) = surf(X,Y,Zp ,'CData',imgface{1},'FaceColor','texturemap','LineStyle','none') ; hold on
hcubeface(6) = surf(X,Y,Zm ,'CData',imgface{6},'FaceColor','texturemap','LineStyle','none') ;

hcubeface(2) = surf(X,Zp,Y ,'CData',imgface{2},'FaceColor','texturemap','LineStyle','none') ;
hcubeface(5) = surf(X,Zm,Y ,'CData',imgface{5},'FaceColor','texturemap','LineStyle','none') ;

hcubeface(3) = surf(Zp,X,Y ,'CData',imgface{3},'FaceColor','texturemap','LineStyle','none') ;
hcubeface(4) = surf(Zm,X,Y ,'CData',imgface{4},'FaceColor','texturemap','LineStyle','none') ;

axis square
axis off

Это отобразит следующий рисунок:
dice

Извините за фактические снимки, которые я получил первым, что у меня было. Вам нужно будет создать приятное изображение ваших кубических граней, а затем применить их, как показано выше.

Если вы вращаете куб с помощью интерактивного инструмента фигуры, у вас не будет проблем.
Если вы хотите повернуть его программно, у вас есть 2 варианта:

  • Если объект является единственным, отображаемым, то самым простым является только перемещение точки зрения (используя view или другие функции манипуляции с камерой).

  • Если у вас есть несколько объектов, и вы хотите только повернуть свой куб, вам придется поочередно поворачивать каждую поверхность. В этом случае я бы предложил написать вспомогательную функцию, которая будет вращать 6 граней для вас, учитывая угол и направление. Еще более аккуратным способом было бы управлять вашим кубом в классе, а затем добавить метод для его вращения (внутри он будет вращать 6 граней).


Изменить: просто для удовольствия, нижеприведенный фрагмент будет анимировать ваши кости (а не предварительный путь, но он показывает вам пример первого варианта выше).

%% // animate by changing point of view
azi = linspace(-180,180,100) ;
for iv =1:100
    view(azi(iv),azi(iv)+randi(5))
    pause(0.01)
end
view(45,45)

Запустите этот блок, чтобы увидеть свои танцы на кости;)


Изменить (снова)
И это пример того, как анимировать объект куба, вращая сам объект

%% // animate by rotating the object

%// this is necessary because the first rotation reset the axes to shading interp
rotate(hcubeface , [0 1 1] , 0.5)
for iface = 1:6 ; set( hcubeface(iface) , 'CData',imgface{iface}, 'FaceColor','texturemap' ) ; end

%// after that, no need to reset the texture map, enjoy as many rotations as you like
for iv =1:360
    axerot = rand(1,3) ;                % // pick a random axis to rotate around
    rotate(hcubeface , axerot , 0.5)    %// rotate the 6 faces by 0.5 degrees
    pause(0.01)
end

Обратите внимание, что я изменил определение куба, чтобы все поверхности лица обрабатывались в одном массиве. Это позволяет применить команду rotate ко всем поверхностям за один раз, просто отправив массив дескрипторов в качестве параметра.