しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

【Win10 Pre】 遠近法で UWP アプリ内で疑似的に UI を 3D 表示する

少し前に公開された Microsoft の Productivity Future Vision をご覧になったことがあるでしょうか

これが本当に実現したらえらいことですが、その可能性を感じさせてくれる HoloLends の複合現実に利用できるかもしれない Transform3D、CompositeTransform3D、PerspectiveTransform3D という APIWindows 10 追加されました

これは遠近法によって疑似的にアプリ内の UI を 3D 表示するための機能のようです

さっそく試してみました

Windows 10 Insider Preview Build 10240 時点での情報のため、正式リリース後仕様等が変更になっている可能性があります

f:id:matatabi_ux:20150720222150g:plain

ぐりぐりと立方体が回転していますが、こういった描画が以前よりも簡単になり、次のような XAML だけでできました

<Page
    x:Class="UWPSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:UWPSample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:i="using:Microsoft.Xaml.Interactivity"
    xmlns:core="using:Microsoft.Xaml.Interactions.Core"
    mc:Ignorable="d">
    
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="RotationStates">
                <VisualState x:Name="RotateState">
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="RootTransform" 
                             Storyboard.TargetProperty="RotationY" 
                             From="0" To="360" 
                             Duration="0:0:8" 
                             RepeatBehavior="Forever"/>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Back" 
                                           Storyboard.TargetProperty="Visibility" 
                                           Duration="0:0:8"
                                           RepeatBehavior="Forever">
                            <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Collapsed"/>
                            <DiscreteObjectKeyFrame KeyTime="0:0:2" Value="Visible"/>
                            <DiscreteObjectKeyFrame KeyTime="0:0:5.8" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Front" 
                                           Storyboard.TargetProperty="Visibility" 
                                           Duration="0:0:8"
                                           RepeatBehavior="Forever">
                            <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"/>
                            <DiscreteObjectKeyFrame KeyTime="0:0:1.8" Value="Collapsed"/>
                            <DiscreteObjectKeyFrame KeyTime="0:0:6.2" Value="Visible"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Left" 
                                           Storyboard.TargetProperty="Visibility"
                                           Duration="0:0:8"
                                           RepeatBehavior="Forever">
                            <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Collapsed"/>
                            <DiscreteObjectKeyFrame KeyTime="0:0:4.2" Value="Visible"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Right" 
                                           Storyboard.TargetProperty="Visibility"
                                           Duration="0:0:8"
                                           RepeatBehavior="Forever">
                            <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"/>
                            <DiscreteObjectKeyFrame KeyTime="0:0:3.8" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <i:Interaction.Behaviors>
            <core:EventTriggerBehavior EventName="Loaded">
                <core:GoToStateAction StateName="RotateState"/>
            </core:EventTriggerBehavior>
        </i:Interaction.Behaviors>

        <Grid.Transform3D>
            <PerspectiveTransform3D/>
        </Grid.Transform3D>

        <Grid Margin="40" Width="300">
            <Grid.Transform3D>
                <CompositeTransform3D x:Name="RootTransform" CenterX="150" CenterZ="-150"/>
            </Grid.Transform3D>
            <Image x:Name="Back" Source="Assets/panel-A.png" Stretch="Uniform">
                <Image.Transform3D>
                    <CompositeTransform3D CenterX="150" CenterZ="-150"  RotationY="180"/>
                </Image.Transform3D>
            </Image>
            <Image x:Name="Left" Source="Assets/panel-B.png" Stretch="Uniform">
                <Image.Transform3D>
                    <CompositeTransform3D CenterX="150" CenterZ="-150" RotationY="90"/>
                </Image.Transform3D>
            </Image>
            <Image x:Name="Right" Source="Assets/panel-B.png" Stretch="Uniform">
                <Image.Transform3D>
                    <CompositeTransform3D CenterX="150" CenterZ="-150" RotationY="270"/>
                </Image.Transform3D>
            </Image>
            <Image x:Name="Front" Source="Assets/panel-A.png" Stretch="Uniform">
                <Image.Transform3D>
                    <CompositeTransform3D CenterX="150" CenterZ="-150" RotationY="0"/>
                </Image.Transform3D>
            </Image>
        </Grid>

    </Grid>
</Page>

ルート要素の Grid に PerspectiveTransform3D を指定し、子要素を遠近法で 3D 表示するようにしています

4 つの Image とそれを囲む Grid には CompositeTransform3D が指定されていて Center~ で回転の中心点座標、Rotation~ で各軸の回転角度を指定しました

画面が読み込まれたら Loaded イベントが起きるので、これをトリガーにして VisualState を RotateState に遷移させ、StoryBoard で全体を回転させたり、裏に回った画像を非表示にしたりしています

残念ながら Z 座標の奥行に応じた UI の重ね合わせまではやってくれないので、そこは自力で Canvas.ZIndex などを操作してやるしかないみたいです

HoloLends 表示用の 3D 描画 API とか UWP に今後増えるかもしれませんね