3-D グラフィックスの概要
更新 : 2007 年 11 月
ここでは、Windows Presentation Foundation (WPF) グラフィックス システムの 3-D 機能の概要について説明します。WPF 3-D 実装では、マークアップ コードおよび手順コードのいずれにも、2-D グラフィックスのプラットフォームで提供される機能と同じ機能を使用して、開発者が 3-D グラフィックスを描画、変換、アニメーション化できます。開発者は、2-D グラフィックスと 3-D グラフィックスを組み合わせることで、多彩なコントロールを作成したり、複雑なイラストレーションのデータを提供したり、アプリケーション インターフェイスのユーザー エクスペリエンスを向上させることができます。WPF での 3-D サポートは、多機能なゲーム開発プラットフォームを提供するために設計されたものではありません。
このトピックは、次のセクションで構成されています。
2-D コンテナ内の 3-D グラフィックス
3-D 座標空間
カメラと投影
モデルとメッシュ プリミティブ
モデルへのマテリアルの適用
シーンの照明
モデルの変換
モデルへのアニメーションの適用
ウィンドウへの 3-D コンテンツの追加
関連トピック
2-D コンテナ内の 3-D グラフィックス
WPF の 3-D グラフィックス コンテンツは、2 次元要素の構造内に追加できる Viewport3D 要素内にカプセル化されます。グラフィックス システムでは、他の多くの WPF と同様に、Viewport3D を 2 次元のビジュアル要素として扱います。Viewport3D 機能は、3 次元シーンへのウィンドウ (ビューポート) として機能します。正確には、3-D シーンが投影されるサーフェイスです。
従来の 2-D アプリケーションでは、Grid や Canvas などの他のコンテナ要素と同じように Viewport3D を使用します。Viewport3D と他の 2-D 描画オブジェクトを同じシーン グラフで使用することはできますが、Viewport3D 内で 2-D オブジェクトと 3-D オブジェクトを相互に貫入させることはできません。ここでは、Viewport3D 内で 3-D グラフィックスを描画する方法を中心に説明します。
3-D 座標空間
2-D グラフィックスの WPF 座標系では、レンダリング領域 (通常は画面) の左上に原点を置きます。2-D 座標系では、正の x 軸の値で右に移動し、正の y 軸の値で下に移動します。しかし、3-D 座標系では、原点はレンダリング領域の中心に置かれ、正の x 軸の値で右に移動しますが、正の y 軸の値では上に移動し、さらに正の z 軸の値で原点から離れて閲覧者に向かう方向に移動します。
従来の 2-D 座標系および 3-D 座標系の表現
これらの軸によって定義される空間は、WPF の 3-D オブジェクトを参照する静止座標系です。この座標空間にモデルを構築し、照明とカメラを作成して表示するときに、この静止座標系、つまり "ワールド座標" と、変換を適用するときにモデルごとに作成するローカル座標とを区別すると役に立ちます。また、ワールド座標のオブジェクトは、照明とカメラの設定によってまったく違って見えたり何も表示されないことがあります。ただし、カメラの位置によってワールド座標におけるオブジェクトの場所が変わることはありません。
カメラと投影
2-D を操作する開発者は、2 次元の画面上に描画プリミティブを配置することに慣れています。3-D シーンを作成するときには、実際は 3-D オブジェクトの 2-D 表現を作成しているということを忘れないことが重要です。3-D シーンは閲覧者の視点によって異なって見えるため、視点を指定する必要があります。この 3-D シーンの視点は、Camera クラスによって指定できます。
3-D シーンを 2-D サーフェイス上で表現する方法を理解するために、表示するサーフェイス上への投影としてシーンを説明することもできます。ProjectionCamera では、閲覧者からの 3-D モデルの見え方を変化させる多様な投影とそのプロパティを指定できます。PerspectiveCamera は、シーンを遠近短縮する投影を指定します。つまり、PerspectiveCamera は、消失点の視野を提供します。シーンの座標空間内のカメラの位置、カメラの向きと視野、およびシーン内での "上" の方向を定義するベクタを指定できます。PerspectiveCamera の投影を表す図を次に示します。
ProjectionCamera の NearPlaneDistance プロパティと FarPlaneDistance プロパティは、カメラの投影の範囲を制限します。カメラはシーン内の任意の場所に配置できるため、カメラの実際の配置がモデルの内部だったりモデルに近すぎたりして、オブジェクトを適切に識別できなくなる可能性もあります。NearPlaneDistance では、その距離を超えるとオブジェクトが描画されなくなる、カメラからの最短の距離を指定できます。逆に、FarPlaneDistance では、識別できないほど離れたオブジェクトがシーンに含まれないように、その距離を超えるとオブジェクトが描画されなくなる、カメラからの最長の距離を指定できます。
カメラの位置
OrthographicCamera は、2-D ビジュアル サーフェイスに対する 3-D モデルの正投影を指定します。他のカメラと同様に、位置、表示方向、および "上向き" 方向を指定します。ただし、PerspectiveCamera とは異なり、OrthographicCamera は遠近短縮を含まない投影を記述します。つまり、OrthographicCamera は、辺がカメラの 1 点に集まる表示ボックスではなく、辺が平行な表示ボックスを記述します。次の図は、PerspectiveCamera および OrthographicCamera を使用して表示した同じモデルを示しています。
透視投影と正投影
次のコードは、一般的なカメラの設定を示しています。
<!-- Add a camera. -->
<Viewport3D.Camera>
<PerspectiveCamera FarPlaneDistance="20" LookDirection="5,-2,-3" UpDirection="0,1,0" NearPlaneDistance="1" Position="-5,2,3" FieldOfView="45" />
</Viewport3D.Camera>
// Defines the camera used to view the 3D object. In order to view the 3D object,
// the camera must be positioned and pointed such that the object is within view
// of the camera.
PerspectiveCamera myPCamera = new PerspectiveCamera();
// Specify where in the 3D scene the camera is.
myPCamera.Position = new Point3D(0, 0, 2);
// Specify the direction that the camera is pointing.
myPCamera.LookDirection = new Vector3D(0, 0, -1);
// Define camera's horizontal field of view in degrees.
myPCamera.FieldOfView = 60;
// Asign the camera to the viewport
myViewport3D.Camera = myPCamera;
// Defines the camera used to view the 3D object. In order to view the 3D object,
// the camera must be positioned and pointed such that the object is within view
// of the camera.
PerspectiveCamera myPCamera = new PerspectiveCamera();
// Specify where in the 3D scene the camera is.
myPCamera.Position = new Point3D(0, 0, 2);
// Specify the direction that the camera is pointing.
myPCamera.LookDirection = new Vector3D(0, 0, -1);
// Define camera's horizontal field of view in degrees.
myPCamera.FieldOfView = 60;
// Asign the camera to the viewport
myViewport3D.Camera = myPCamera;
モデルとメッシュ プリミティブ
Model3D は、汎用 3-D オブジェクトを表す抽象基本クラスです。3-D シーンを構築するには、表示するためのオブジェクトが必要です。シーン グラフを構成するオブジェクトは、Model3D から派生します。現在、WPF は、GeometryModel3D でモデリングするジオメトリをサポートしています。このモデルの Geometry プロパティは、メッシュ プリミティブを受け取ります。
モデルを構築するには、プリミティブまたはメッシュを構築することから開始します。3-D プリミティブは、単一の 3-D エンティティを形成する頂点のコレクションです。大多数の 3-D システムは、最も単純な閉じた図形、つまり 3 つの頂点で定義された三角形に基づいてモデル化されるプリミティブを提供します。三角形の 3 つの点は同一平面上にあるため、さらに複雑な図形 (メッシュ) をモデル化するために引き続きいくつかの三角形を追加できます。
現在の WPF の 3-D システムには、ジオメトリを指定できる MeshGeometry3D クラスが用意されています。現時点では、球体や立方体のような定義済みの 3-D プリミティブはサポートしていません。三角形の頂点のリストを Positions プロパティとして指定して、MeshGeometry3D の作成を開始します。各頂点は、Point3D として指定します (Extensible Application Markup Language (XAML) では、各頂点の座標を表して 3 つにグループ化した数値のリストとしてこのプロパティを指定します)。ジオメトリに応じて、メッシュは、一部が同じ角 (頂点) を共有する数多くの三角形で構成される場合があります。メッシュを正しく描画するために、WPF は複数の三角形が共有する頂点に関する情報を必要とします。この情報を与えるには、三角形のインデックスのリストとその TriangleIndices プロパティを指定します。このリストは、Positions リストに指定された点が三角形を決定する順序を示します。
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D
Positions="-1 -1 0 1 -1 0 -1 1 0 1 1 0"
Normals="0 0 1 0 0 1 0 0 1 0 0 1"
TextureCoordinates="0 1 1 1 0 0 1 0 "
TriangleIndices="0 1 2 1 3 2" />
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<SolidColorBrush Color="Cyan" Opacity="0.3"/>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
<!-- Translate the plane. -->
<GeometryModel3D.Transform>
<TranslateTransform3D
OffsetX="2" OffsetY="0" OffsetZ="-1" >
</TranslateTransform3D>
</GeometryModel3D.Transform>
</GeometryModel3D>
前の例で、Positions リストには、立方体の形状のメッシュを定義する 8 つの頂点を指定しています。TriangleIndices プロパティでは、3 つのインデックスの 12 のグループのリストを指定しています。リスト内の各数値は、Positions リストへのオフセットを参照します。たとえば、Positions リストで指定される最初の 3 つの頂点は、(1,1,0)、(0,1,0)、および (0,0,0) です。TriangleIndices リストに指定される最初の 3 つのインデックスは、0、2、および 1 です。これは、Positions リスト内の 1 番目の点、3 番目の点、2 番目の点に対応します。結果として、立方体モデルを形成する最初の三角形は (1,1,0) から (0,1,0)、(0,0,0) で構成され、残りの 11 の三角形も同様に決定されます。
Normals プロパティおよび TextureCoordinates プロパティの値を指定して、モデルの定義を続行できます。モデルのサーフェイスをレンダリングするために、グラフィックス システムは、指定された任意の三角形でサーフェイスが向いている方向に関する情報を必要とします。この情報はモデルの光源計算に使用されます。直接光源に向いているサーフェイスは、光源から離れた角度のサーフェイスよりも明るく見えます。WPF では、位置座標を使用して既定の法線ベクタを決定できます。曲面のサーフェイスの外観に接する別の法線ベクタを指定することもできます。
TextureCoordinates プロパティは、メッシュの頂点にテクスチャを描画する方法を決定する座標のマッピング方法をグラフィックス システムに通知する Point のコレクションを指定します。TextureCoordinates は、0 ~ 1 の値として指定されます。Normals プロパティの場合と同様に、グラフィックス システムは既定のテクスチャ座標を計算できますが、たとえば、繰り返すパターンの一部を含むテクスチャのマッピングを制御する別のテクスチャ座標の設定を選択することもできます。テクスチャ座標に関する追加の情報は、後続のトピックまたは Managed Direct3D SDK を参照してください。
手順コードで立方体モデルの 1 つの面を作成する方法を次の例に示します。立方体の全体を単一の GeometryModel3D として描画できます。この例では、後で各面に別々のテクスチャを適用するために、立方体の表面を別個のモデルとして描画しています。
MeshGeometry3D side1Plane = new MeshGeometry3D();
side1Plane.Positions.Add(new Point3D(-0.5, -0.5, -0.5));
side1Plane.Positions.Add(new Point3D(-0.5, 0.5, -0.5));
side1Plane.Positions.Add(new Point3D(0.5, 0.5, -0.5));
side1Plane.Positions.Add(new Point3D(0.5, 0.5, -0.5));
side1Plane.Positions.Add(new Point3D(0.5, -0.5, -0.5));
side1Plane.Positions.Add(new Point3D(-0.5, -0.5, -0.5));
side1Plane.TriangleIndices.Add(0);
side1Plane.TriangleIndices.Add(1);
side1Plane.TriangleIndices.Add(2);
side1Plane.TriangleIndices.Add(3);
side1Plane.TriangleIndices.Add(4);
side1Plane.TriangleIndices.Add(5);
side1Plane.Normals.Add(new Vector3D(0, 0, -1));
side1Plane.Normals.Add(new Vector3D(0, 0, -1));
side1Plane.Normals.Add(new Vector3D(0, 0, -1));
side1Plane.Normals.Add(new Vector3D(0, 0, -1));
side1Plane.Normals.Add(new Vector3D(0, 0, -1));
side1Plane.Normals.Add(new Vector3D(0, 0, -1));
side1Plane.TextureCoordinates.Add(new Point(1, 0));
side1Plane.TextureCoordinates.Add(new Point(1, 1));
side1Plane.TextureCoordinates.Add(new Point(0, 1));
side1Plane.TextureCoordinates.Add(new Point(0, 1));
side1Plane.TextureCoordinates.Add(new Point(0, 0));
side1Plane.TextureCoordinates.Add(new Point(1, 0));
モデルへのマテリアルの適用
メッシュは 3 次元オブジェクトのように見えますが、光を発してカメラに映るには、頂点と三角形によって定義されるサーフェイスを覆うテクスチャの適用が必要です。2-D では、Brush クラスを使用して、色、パターン、グラデーション、または他のビジュアル コンテンツを画面の領域に適用します。しかし、3-D オブジェクトの外観は、適用された単なる色またはパターンではなく、照明モデルの機能です。実世界のオブジェクトは、サーフェイスの品質によって光の反射の仕方が異なります。光沢があって輝いているサーフェイスは、粗くマットなサーフェイスの外観と同じではありません。光を吸収するように見えるオブジェクトもあれば、光を放つように見えるオブジェクトもあります。2-D オブジェクトに適用できるブラシはすべて 3-D オブジェクトにも適用できますが、直接適用することはできません。
モデルのサーフェイスの特性を定義するために、WPF は Material 抽象クラスを使用します。Material の具象サブクラスは、モデル サーフェイスの外観の特性の一部を決定します。それぞれが、SolidColorBrush、TileBrush、または VisualBrush を渡す Brush プロパティを提供します。
DiffuseMaterial は、そのモデルが光を拡散させるかのように、ブラシをモデルに適用することを指定します。DiffuseMaterial の使用は、2-D モデルに直接ブラシを使用することに似ています。モデルのサーフェイスは光沢のようには光を反射しません。
SpecularMaterial は、モデルのサーフェイスが硬質であるかのように、または光沢があってハイライトを反射できるかのように、ブラシをモデルに適用することを指定します。SpecularPower プロパティの値を指定することによって、この反射の品質 ("輝き") を示すテクスチャの度合いを設定できます。
EmissiveMaterial では、モデルがブラシの色と同じ光を放射しているかのように、テクスチャを適用することを指定できます。これは、モデルを光源にするものではありませんが、DiffuseMaterial または SpecularMaterial でテクスチャを適用した場合とは異なる影を加えます。
パフォーマンスの向上のために、GeometryModel3D の背面 (これらの面は、カメラの反対側に位置するモデルの面なので、表示されません) はシーンから除かれます。Material を指定して平面のようなモデルの背面に適用するには、モデルの BackMaterial プロパティを設定します。
光り輝く効果や反射の効果のようなサーフェイスの品質を実現するために、モデルに数種類のブラシを続けて適用することができます。MaterialGroup クラスを使用すると、複数のマテリアルを適用し、再利用できます。MaterialGroup の子は、複数の描画パスの始めから終わりまで適用されます。
3-D モデルにブラシとして純色と描画を適用する方法を次のコード例に示します。
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<SolidColorBrush Color="Cyan" Opacity="0.3"/>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
<DrawingBrush x:Key="patternBrush" Viewport="0,0,0.1,0.1" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z"
Brush="Gray" />
<GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z"
Brush="Gray" />
<GeometryDrawing Geometry="M0.25,0.25 L0.5,0.125 0.75,0.25 0.5,0.5z"
Brush="#FFFF00" />
<GeometryDrawing Geometry="M0.25,0.75 L0.5,0.875 0.75,0.75 0.5,0.5z"
Brush="Black" />
<GeometryDrawing Geometry="M0.25,0.75 L0.125,0.5 0.25,0.25 0.5,0.5z"
Brush="#FF0000" />
<GeometryDrawing Geometry="M0.75,0.25 L0.875,0.5 0.75,0.75 0.5,0.5z"
Brush="MediumBlue" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
<DrawingBrush x:Key="patternBrush" Viewport="0,0,0.1,0.1" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z"
Brush="Gray" />
<GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z"
Brush="Gray" />
<GeometryDrawing Geometry="M0.25,0.25 L0.5,0.125 0.75,0.25 0.5,0.5z"
Brush="#FFFF00" />
<GeometryDrawing Geometry="M0.25,0.75 L0.5,0.875 0.75,0.75 0.5,0.5z"
Brush="Black" />
<GeometryDrawing Geometry="M0.25,0.75 L0.125,0.5 0.25,0.25 0.5,0.5z"
Brush="#FF0000" />
<GeometryDrawing Geometry="M0.75,0.25 L0.875,0.5 0.75,0.75 0.5,0.5z"
Brush="MediumBlue" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
DiffuseMaterial side5Material = new DiffuseMaterial((Brush)Application.Current.Resources["patternBrush"]);
シーンの照明
3-D グラフィックス内の光源は、現実の世界で光が果たす役割を果たします。つまり、サーフェイスを見えるようにします。さらに、光源は、投影に含まれるシーンの部分を決定します。WPF の光源オブジェクトは、多彩な光と影の効果を作り出し、現実の世界のさまざまな光源の振る舞いに従ってモデル化されます。シーンには少なくとも 1 つの光源を入れる必要があります。そうしないと、モデルが表示されません。
次の光源は、Light 基本クラスから派生します。
AmbientLight: 位置や向きに関係なく、すべてのオブジェクトを一様に照らすアンビエント光を提供します。
DirectionalLight: 遠くにある光源のように照らします。指向性のある光源には、Vector3D として指定される Direction はありますが、特定の位置はありません。
PointLight: 隣接する光源のように照らします。PointLight には位置があり、その位置から光を投げかけます。シーン内のオブジェクトは、その位置と、光源に対する距離に応じて照らされます。PointLightBase は Range プロパティを公開します。これは、モデルが光源によって照らされなくなる距離を決定します。PointLight は、光の強さが距離に応じて減少する割合を決定する減衰のプロパティを公開します。光の減衰には定数補間、線形補間、または 2 次補間を指定できます。
SpotLight: PointLight から継承します。スポットライトは PointLight のように照らし、位置と方向の両方があります。InnerConeAngle プロパティおよび OuterConeAngle プロパティによって設定される円すい形の領域に、指定された程度の光を投射します。
光源は Model3D オブジェクトなので、位置、色、方向、および範囲などの光源のプロパティを変換したり、アニメーション化したりできます。
<ModelVisual3D.Content>
<AmbientLight Color="#333333" />
</ModelVisual3D.Content>
DirectionalLight myDirLight = new DirectionalLight();
myDirLight.Color = Colors.White;
myDirLight.Direction = new Vector3D(-3, -4, -5);
modelGroup.Children.Add(myDirLight);
モデルの変換
モデルを作成すると、モデルはシーン内の特定の場所に配置されます。モデルをシーン内で移動したり、回転したり、サイズを変更したりするために、モデル自身を定義する頂点を変更するのは実用的ではありません。代わりに、2-D と同様、モデルに変換を適用します。
各モデル オブジェクトには Transform プロパティがあり、モデルを移動したり、向きやサイズを変更したりできます。変換を適用すると、変換で指定されたベクタまたは値だけ効果的にモデルのすべての点をオフセットすることができます。つまり、モデルが定義されている座標空間 (モデル空間) を変換しても、シーン全体の座標系におけるモデルの図形座標 (ワールド座標) を構成する値は変更されません。
モデルの変換の詳細については、「3-D 変換の概要」を参照してください。
モデルへのアニメーションの適用
WPF 3-D 実装は、2-D グラフィックスと同じタイミングとアニメーション システムに追加されます。つまり、3-D シーンにアニメーションを適用するには、そのモデルのプロパティにアニメーションを適用します。プリミティブのプロパティに直接アニメーションを適用できますが、通常はモデルの位置や外観を変更する変換にアニメーションを適用する方が簡単です。変換は、個々のモデル同様、Model3DGroup オブジェクトに適用できるため、Model3DGroup の子にアニメーションのセットを適用し、子オブジェクトのグループに別のアニメーションのセットを適用することが可能です。シーンの光源のプロパティをアニメーション化することによって、さまざまな視覚効果を実現できます。最後に、カメラの位置または視野をアニメーション化することによって、投影自体をアニメーション化することもできます。WPF のタイミングとアニメーション システムに関する背景情報については、「アニメーションの概要」、「ストーリーボードの概要」、および「Freezable オブジェクトの概要」を参照してください。
WPF のオブジェクトをアニメーション化するには、タイムラインを作成し、アニメーションを定義し (これは、実際は一定期間にわたって一部のプロパティ値を変更します)、アニメーションを適用するプロパティを指定します。3-D シーン内のすべてのオブジェクトは Viewport3D の子であるため、シーンに適用するアニメーションの対象となるプロパティは、Viewport3D のプロパティになります。
モデルが同じ場所で揺れて見えるようにするとします。モデルに RotateTransform3D を適用して、あるベクタから別のベクタへの回転の軸にアニメーションを適用できます。RotateTransform3D は TransformGroup でモデルに適用されている複数の変換の 1 つであるという前提で、Vector3DAnimation を変換の Rotation3D の Axis プロパティに適用する方法を次のコード例に示します。
//Define a rotation
RotateTransform3D myRotateTransform = 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;
myRotateTransform.Rotation.BeginAnimation(AxisAngleRotation3D.AxisProperty, myVectorAnimation);
//Add transformation to the model
cube1TransformGroup.Children.Add(myRotateTransform);
ウィンドウへの 3-D コンテンツの追加
シーンを描画するには、モデルと光源を Model3DGroup に追加し、その Model3DGroup を ModelVisual3D の Content として設定します。ModelVisual3D を Viewport3D の Children コレクションに追加します。Camera プロパティを設定することによって、カメラを Viewport3D に追加します。
最後に、ウィンドウに Viewport3D を追加します。Viewport3D が Canvas などのレイアウト要素のコンテンツとして含まれる場合、その Height プロパティおよび Width プロパティ (FrameworkElement から継承) を設定することによって、Viewport3D のサイズを設定します。