3D 変換の概要

このトピックでは、Windows Presentation Foundation (WPF) グラフィックス システムで 3D モデルに変換を適用する方法について説明します。 変換を使うと、モデルを定義する基本の値を変更することなく、モデルの位置、サイズ、向きを変更できます。

3D 座標空間

Windows Presentation Foundation (WPF) での 3D グラフィックス コンテンツは Viewport3D 要素にカプセル化されており、2 次元要素の構造体に含めることができます。 グラフィックス システムは、Viewport3D を、Windows Presentation Foundation (WPF) 内の他の多くの要素と同じ 2 次元のビジュアル要素として処理します。 Viewport3D は、3 次元シーンのウィンドウ (ビューポート) として機能します。 より正確には、3D シーンが投影されるサーフェイスです。 同じシーン グラフ内で他の 2D 描画オブジェクトと共に Viewport3D を使うことができますが、Viewport3D 内の 2D オブジェクトと 3D オブジェクトを相互に貫通させることはできません。 以下の説明で、座標空間は Viewport3D 要素に含まれています。

2D グラフィックス用の Windows Presentation Foundation (WPF) 座標系の原点は、レンダリング サーフェイス (通常は画面) の左上にあります。 2D システムでは、x 軸の正の値は右に向かって大きくなり、y 軸の正の値は下に向かって大きくなります。 一方、3D 座標系では、原点は画面の中央にあり、x 軸の正の値は右に向かって大きくなりますが、y 軸の正の値は上に向かって大きくなり、z 軸の正の値は原点から手前に向かって大きくなります。

Coordinate systems
座標系の比較

これらの軸によって定義される空間は、Windows Presentation Foundation (WPF) 内の 3D オブジェクトのための静止した基準枠です。 この空間内にモデルを構築し、それらを表示するためのライトとカメラを作成するときは、この静止した基準枠 ("ワールド空間") と、モデルに変換を適用するときにモデルごとに作成するローカルな基準枠を区別することをお勧めします。 また、ワールド空間内のオブジェクトは、ライトとカメラの設定により、まったく違って見えたり、またはまったく見えなくなることがありますが、カメラの位置によってワールド空間内のオブジェクトの場所が変化することはないことに注意してください。

モデルの変換

モデルを作成するとき、モデルにはシーン内で特定の位置があります。 モデルをシーン内で移動したり、回転したり、そのサイズを変更したりするのに、モデル自体を定義する頂点を変更するのは実用的ではありません。 そのような場合は、2D と同じように、モデルに変換を適用します。

各モデル オブジェクトが備えている Transform プロパティを使って、モデルの移動、回転、サイズ変更を行うことができます。 変換を適用するときは、ベクトルにより、または変換で指定する値により、モデルのすべてのポイントをオフセットします。 つまり、モデルが定義されている座標空間 ("モデル空間") を変換するのであって、シーン全体の座標系 ("ワールド空間") 内でモデルのジオメトリを構成する値を変更するのではありません。

平行移動変換

3D 変換は、抽象基本クラス Transform3D を継承します。これには、アフィン変換クラス TranslateTransform3DScaleTransform3DRotateTransform3D が含まれます。 また、Windows Presentation Foundation (WPF) の 3D システムには MatrixTransform3D クラスも用意されており、より簡潔な行列演算で同じ変換を指定できます。

TranslateTransform3D は、Model3D 内のすべての点を、OffsetXOffsetYOffsetZ の各プロパティで指定したオフセット ベクターの方向に移動します。 たとえば、立方体の 1 つの頂点が (2,2,2) にあるものとすると、オフセット ベクトル (0,1.6,1) により頂点 (2,2,2) は (2,3.6,3) に移動します。 モデル空間内での立方体の頂点はまだ (2,2,2) にありますが、モデル空間の (2,2,2) がワールド空間では (2,3.6,3) になるように、モデル空間とワールド空間の関係が変更されています。

Translation figure
オフセットでの平行移動

次のコード例は、平行移動を適用する方法を示しています。

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <DockPanel>
    <Viewbox>
      <Canvas Width="600" Height="201">

        <!-- The Viewport3D provides a rendering surface for 3-D visual content. -->
        <Viewport3D Name="MyAnimatedObject"
          ClipToBounds="True" Width="600" Height="150"
          Canvas.Left="0" Canvas.Top="10">

          <!-- Defines the camera used to view the 3D object. -->
          <Viewport3D.Camera>
            <PerspectiveCamera x:Name="myPerspectiveCamera" Position="0,0,2" LookDirection="0,0,-1" 
             FieldOfView="60" />
          </Viewport3D.Camera>

          <!-- The ModelVisual3D children contain the 3D models -->
          <Viewport3D.Children>

            <!-- This ModelVisual3D defines the light cast in the scene. Without light, the
                 3D object cannot be seen. -->
            <ModelVisual3D>
              <ModelVisual3D.Content>
                <DirectionalLight Color="#FFFFFF" Direction="-0.612372,-0.5,-0.612372" />
              </ModelVisual3D.Content>
            </ModelVisual3D>
            <ModelVisual3D>
              <ModelVisual3D.Content>
                <GeometryModel3D>

                  <!-- The geometry specifes the shape of the 3D plane. In this case, a flat sheet is created. -->
                  <GeometryModel3D.Geometry>
                    <MeshGeometry3D
                     TriangleIndices="0,1,2 3,4,5 "
                     Normals="0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 "
                     TextureCoordinates="0,0 1,0 1,1 1,1 0,1 0,0 "
                     Positions="-0.5,-0.5,0.5 0.5,-0.5,0.5 0.5,0.5,0.5 0.5,0.5,0.5 -0.5,0.5,0.5 -0.5,-0.5,0.5 " />
                  </GeometryModel3D.Geometry>

                  <!-- The material specifies the material applied to the plane. In this case it is a linear gradient.-->
                  <GeometryModel3D.Material>
                    <MaterialGroup>
                      <DiffuseMaterial>
                        <DiffuseMaterial.Brush>
                          <SolidColorBrush Color="Cyan" Opacity="0.3"/>
                        </DiffuseMaterial.Brush>
                      </DiffuseMaterial>
                    </MaterialGroup>
                  </GeometryModel3D.Material>
                  <!-- The Transform specifies how to transform the 3D object. The OffsetX property is animated
                       in the Storyboard below. -->
                  <GeometryModel3D.Transform>
                    <TranslateTransform3D x:Name="myTranslateTransform3D" OffsetX="0" OffsetY="0" OffsetZ="0" />
                  </GeometryModel3D.Transform>
                </GeometryModel3D>
              </ModelVisual3D.Content>
            </ModelVisual3D>
          </Viewport3D.Children>
          <!-- Trigger the TranslateTransform3D animation when the 3D object loads. -->
          <Viewport3D.Triggers>
            <EventTrigger RoutedEvent="Viewport3D.Loaded">
              <BeginStoryboard>
                <Storyboard>

                  <!-- This animation animates the OffsetX property of the TranslateTransform3D. -->
                  <DoubleAnimation
                   Storyboard.TargetName="myTranslateTransform3D" 
                   Storyboard.TargetProperty="OffsetX" 
                   To="-0.8" 
                   AutoReverse="True" RepeatBehavior="Forever" />

                  <!-- If you want to animate OffsetY and/or OffsetZ, create similar DoubleAnimations
                       respectively. -->

                </Storyboard>
              </BeginStoryboard>
            </EventTrigger>
          </Viewport3D.Triggers>
        </Viewport3D>
      </Canvas>
    </Viewbox>
  </DockPanel>
</Page>

スケール変換

ScaleTransform3D は、中心点を基準にして、モデルのスケールを、指定されたスケール ベクトルだけ変更します。 一様なスケールを指定すると、モデルは X、Y、Z 軸の方向に同じ比率だけ拡大縮小され、それに比例してモデルのサイズが変化します。 たとえば、変換の ScaleXScaleYScaleZ の各プロパティを 0.5 に設定すると、モデルのサイズが半分になり、同じプロパティを 2 に設定すると、3 軸すべてのスケールが倍になります。

Uniform ScaleTransform3D
ScaleVector の例

一様ではないスケール変換 (X、Y、Z すべての値が同じではないスケール変換) を指定すると、3 つの方向をそれぞれ独立して拡大または縮小できます。 たとえば、ScaleX を1に、ScaleY を 2 に、ScaleZ を 1 に設定すると、変換されたモデルの高さは倍になりますが、X 軸方向と Z 軸方向に変化はありません。

既定では、ScaleTransform3D では原点 (0,0,0) を基準にして頂点が拡大または縮小します。 ただし、変換するモデルが原点から描画されていない場合は、原点からモデルを拡大縮小すると、モデルのスケールは "その場" では変化しません。代わりに、モデルの頂点がスケール ベクトルで乗算されて、スケール演算にはモデルの平行移動と拡大縮小の効果があります。

Three cubes scaled with center point specified
スケールの中心点の例

モデルを "特定の場所で" 拡大縮小するには、ScaleTransform3D の CenterXCenterYCenterZ の各プロパティを設定して、モデルの中心を指定します。 このようにすると、グラフィックス システムはモデル空間を拡大縮小した後、指定された Point3D が中心になるようにモデルを平行移動します。 逆に、原点を基準にモデルを構築し、異なる中心点を指定すると、モデルは原点から離れるように平行移動されます。

回転変換

3D のモデルは複数の方法で回転できます。 一般的な回転変換では、軸と、その軸の周りの回転角度を指定します。 RotateTransform3D クラスでは、その Rotation プロパティを使用して Rotation3D を定義できます。 次に、Rotation3D (この例では AxisAngleRotation3D) の Axis プロパティおよび Angle プロパティを指定して、変換を定義します。 次の例では、Y 軸を中心に 60 度だけモデルを回転します。

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <DockPanel>
    <Viewbox>
      <Canvas Width="321" Height="201">

        <!-- The Viewport3D provides a rendering surface for 3-D visual content. -->
        <Viewport3D Name="MyAnimatedObject"
          ClipToBounds="True" Width="150" Height="150"
          Canvas.Left="0" Canvas.Top="10">

          <!-- Defines the camera used to view the 3D object. -->
          <Viewport3D.Camera>
            <PerspectiveCamera x:Name="myPerspectiveCamera" Position="0,0,2" LookDirection="0,0,-1" 
             FieldOfView="60" />
          </Viewport3D.Camera>

          <!-- The ModelVisual3D children contain the 3D models -->
          <Viewport3D.Children>

            <!-- Two ModelVisual3D define the lights cast in the scene. Without light, the
                 3D object cannot be seen. Also, the direction of the lights affect shadowing. -->
            <ModelVisual3D>
              <ModelVisual3D.Content>
                <DirectionalLight Color="#FFFFFF" Direction="-0.612372,-0.5,-0.612372" />
              </ModelVisual3D.Content>
            </ModelVisual3D>
            <ModelVisual3D>
              <ModelVisual3D.Content>
                <DirectionalLight Color="#FFFFFF" Direction="0.612372,-0.5,-0.612372" />
              </ModelVisual3D.Content>
            </ModelVisual3D>
            <ModelVisual3D>
              <ModelVisual3D.Content>
                <GeometryModel3D>

                  <!-- The geometry specifes the shape of the 3D plane. In this case, a flat sheet is created. -->
                  <GeometryModel3D.Geometry>
                    <MeshGeometry3D
                     TriangleIndices="0,1,2 3,4,5 "
                     Normals="0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 "
                     TextureCoordinates="0,0 1,0 1,1 1,1 0,1 0,0 "
                     Positions="-0.5,-0.5,0.5 0.5,-0.5,0.5 0.5,0.5,0.5 0.5,0.5,0.5 -0.5,0.5,0.5 -0.5,-0.5,0.5 " />
                  </GeometryModel3D.Geometry>

                  <!-- The material specifies the material applied to the plane. In this case it is a linear gradient.-->
                  <GeometryModel3D.Material>
                    <MaterialGroup>
                      <DiffuseMaterial>
                        <DiffuseMaterial.Brush>
                          <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
                            <LinearGradientBrush.GradientStops>
                              <GradientStop Color="Yellow" Offset="0" />
                              <GradientStop Color="Red" Offset="0.25" />
                              <GradientStop Color="Blue" Offset="0.75" />
                              <GradientStop Color="LimeGreen" Offset="1" />
                            </LinearGradientBrush.GradientStops>
                          </LinearGradientBrush>
                        </DiffuseMaterial.Brush>
                      </DiffuseMaterial>
                    </MaterialGroup>
                  </GeometryModel3D.Material>

                  <!-- The Transform specifies how to transform the 3D object. The properties of the
                        Rotation object are animated causing the 3D object to rotate and "wobble" (see Storyboard below).-->
                  <GeometryModel3D.Transform>
                    <RotateTransform3D>
                      <RotateTransform3D.Rotation>
                        <AxisAngleRotation3D x:Name="myAngleRotation" Axis="0,3,0" Angle="40" />
                      </RotateTransform3D.Rotation>
                    </RotateTransform3D>
                  </GeometryModel3D.Transform>
                </GeometryModel3D>
              </ModelVisual3D.Content>
            </ModelVisual3D>
          </Viewport3D.Children>

          <!-- Trigger the rotation animation when the 3D object loads. -->
          <Viewport3D.Triggers>
            <EventTrigger RoutedEvent="Viewport3D.Loaded">
              <BeginStoryboard>
                <Storyboard>

                  <!-- This animation animates the Angle property of the AxisAngleRotation3D
                       making the 3D object rotate from -60 degrees to 60 degrees. -->
                  <DoubleAnimation 
                   Storyboard.TargetName="myAngleRotation" 
                   Storyboard.TargetProperty="Angle" 
                   From="-60" To="60" Duration="0:0:4" AutoReverse="True"  RepeatBehavior="Forever"/>

                  <!-- This animation animates the Axis property of the AxisAngleRotation3D
                       making the 3D wobble as it rotates. -->
                  <Vector3DAnimation 
                   Storyboard.TargetName="myAngleRotation" 
                   Storyboard.TargetProperty="Axis" 
                   From="0,3,0" To="1,0,1" Duration="0:0:4" AutoReverse="True"  RepeatBehavior="Forever"/>

                </Storyboard>
              </BeginStoryboard>
            </EventTrigger>
          </Viewport3D.Triggers>
        </Viewport3D>
      </Canvas>

    </Viewbox>
  </DockPanel>
</Page>

注: Windows Presentation Foundation (WPF) 3D は右手座標系であり、正の回転角度を指定するとモデルは軸を中心に反時計方向に回転します。

RotateTransform3D の CenterXCenterYCenterZ の各プロパティの値が指定されていない場合、軸角度の回転は原点を中心とする回転と見なされます。 拡大縮小と同様に、回転でもモデルの座標空間全体が変換されることを憶えておくと役に立ちます。 モデルが原点を中心として作成されていない場合、または前に平行移動されている場合は、その場での回転ではなく、原点を中心とする "ピボット" になる可能性があります。

Rotation with new center point
指定された新しい中心による回転

モデルを "その場で" 回転するには、モデルの実際の中心を回転の中心として指定します。 通常、ジオメトリは原点を中心にモデル化されるので、ほとんどの場合は、最初にモデルのサイズを変更し (スケーリング)、次に向きを設定し (回転)、最後に目的の位置に移動することにより (平行移動)、変換のセットで意図した結果が得られます。

Rotation by 60 degrees in x- and y-axes
回転の例

軸角度の回転は、静的な変換と一部のアニメーションで問題なく行われます。 ただし、立方体モデルを X 軸を中心に 60 度回転してから、Z 軸を中心に 45 度回転する場合を考えてみてください。 この変換は、2 つの個別のアフィン変換として、または行列として記述できます。 しかし、この方法で定義された回転をスムーズにアニメーション化するのは難しい場合があります。 どちらの方法で計算してもモデルの最初と最後の位置は同じですが、モデルの中間の位置は計算上は不確定です。 四元数は、回転の開始と終了の間の補間を計算する別の方法を表します。

四元数では、3D 空間内の軸と、その軸を中心とする回転が表されます。 たとえば、四元数では、(1,1,2) の軸と 50 度の回転といったように表されます。 回転の定義における四元数の威力は、合成と補間という 2 つの操作を実行できることです。 ジオメトリに適用される 2 つの四元数の合成とは、"ジオメトリを、axis2 を中心に rotation2 だけ回転した後、axis1 を中心に rotation1 だけ回転する" ことを意味します。合成を使うと、ジオメトリに対する 2 つの回転を組み合わせて、結果を表す 1 つの四元数を得ることができます。 四元数の補間では、ある軸と向きから別の軸と向きへの滑らかで適切なパスを計算できるので、オリジナルから合成された四元数への補間を行うことで、ある状態から別の状態へのスムーズな移行を実現して、変換をアニメーション化できます。 アニメーション化するモデルでは、Rotation プロパティの QuaternionRotation3D を使用して、回転先の Quaternion を指定できます。

変換のコレクションの使用

シーンを作成する場合、通常はモデルに複数の変換を適用します。 Transform3DGroup クラスの Children コレクションに変換を追加して、シーン内のさまざまなモデルに適用できるように変換を適宜グループ化します。 各インスタンスに異なる変換セットを適用することでモデルを再利用できるように、多くの場合、複数の異なるグループで変換を再利用すると便利です。 変換をコレクションに追加する順序が重要であることに注意してください。コレクション内の変換は、最初から最後まで順番に適用されます。

変換のアニメーション化

Windows Presentation Foundation (WPF) 3D の実装は、2D グラフィックスと同じタイミングおよびアニメーション システムに参加しています。 つまり、3D シーンをアニメーション化するには、そのモデルのプロパティをアニメーション化します。 プリミティブのプロパティを直接アニメーション化することもできますが、通常は、モデルの位置や外観を変更する変換をアニメーション化する方が簡単です。 変換は Model3DGroup オブジェクトおよび個別のモデルに適用できるので、あるアニメーション セットを Model3Dgroup の子に適用し、別のアニメーション セットをオブジェクトのグループに適用するといったことが可能です。 Windows Presentation Foundation (WPF) のタイミングおよびアニメーション システムの背景情報については、「アニメーションの概要」および「ストーリーボードの概要」トピックをご覧ください。

Windows Presentation Foundation (WPF) のオブジェクトをアニメーション化するには、タイムラインを作成し、アニメーションを定義して (時間経過と共に一部のプロパティの値を実際に変更します)、アニメーションを適用するプロパティを指定します。 このプロパティは、FrameworkElement のプロパティである必要があります。 3D シーンのすべてのオブジェクトは Viewport3D の子なので、シーンに適用するアニメーションの対象となるプロパティは、Viewport3D のプロパティのプロパティです。 構文が冗長になる場合があるので、アニメーションのプロパティ パスを慎重に検討することが重要です。

オブジェクトをその場で回転させ、さらに揺れる動作を適用してオブジェクトの多くの部分が見えるようにしたいものとします。 そのためには、RotateTransform3D をモデルに適用し、回転軸をあるベクトルから別のベクトルにアニメーション化します。 次のコード例では、RotateTransform3D が TransformGroup でモデルに適用される複数の変換の 1 つであるものとして、変換の Rotation3D の Axis プロパティに Vector3DAnimation を適用する方法を示します。

//Define a rotation
RotateTransform3D myRotateTransform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 1));
'Define a rotation
Dim myRotateTransform As New RotateTransform3D(New AxisAngleRotation3D(New Vector3D(0, 1, 0), 1))
Vector3DAnimation myVectorAnimation = new Vector3DAnimation(new Vector3D(-1, -1, -1), new Duration(TimeSpan.FromMilliseconds(5000)));
myVectorAnimation.RepeatBehavior = RepeatBehavior.Forever;
Dim myVectorAnimation As New Vector3DAnimation(New Vector3D(-1, -1, -1), New Duration(TimeSpan.FromMilliseconds(5000)))
myVectorAnimation.RepeatBehavior = RepeatBehavior.Forever

オブジェクトを移動または拡大縮小するには、他の変換プロパティを対象に同様の構文を使います。 たとえば、スケール変換の ScaleCenter プロパティに Point3DAnimation を適用して、モデルの形状をスムーズに変形することができます。

上の例では GeometryModel3D のプロパティを変換していますが、シーン内の他のモデルのプロパティを変換することもできます。 Light オブジェクトに適用される変換をアニメーション化することで、モデルの外観を劇的に変えることができる、移動する照明効果とシャドウ効果を作成できます。

カメラもモデルなので、カメラのプロパティを変換することもできます。 カメラの位置または平面距離を変換することにより (実際には、シーン全体の投影を変換して) シーンの外観を変更できますが、この方法で得られる効果の多くは、シーン内のモデルの場所や位置に適用される変換ほど大きな "視覚的意味" を見ている人に与えられない場合があることに注意してください。

関連項目