テクスチャ
画像をプリミティブの面に貼り付ける
プリミティブの面に、テクスチャを貼り付けることもできます。画像ファイルを読み込んで作られたテクスチャを貼り付けることによって、3D 空間上に 2D の画像ファイルを表示できます。 レンガやコンクリートの壁、布や革の衣装といった複雑な模様を持つモデルのプリミティブには、こうしたテクスチャが貼られます。テクスチャを用いることで、平坦なプリミティブにリアリティのある質感を与えることができます。
テクスチャを描画するには、デバイスに描画するテクスチャを設定しなければなりません。BasicEffect クラスを用いている場合、描画するテクスチャを Texture プロパティに設定します。
public Texture2D Texture { get; set; }
BasicEffect クラスのデフォルトの状態では、テクスチャが無効に設定されています。この設定は TextureEnabled プロパティで行います。
public bool TextureEnabled { get; set; }
このプロパティが true の場合は、テクスチャが有効になります。デフォルトでは false に設定されているので、テクスチャを描画するために true に変更します。
プリミティブにテクスチャを描画するには、頂点がテクスチャのどの部分に対応するかという情報が必要になります。頂点をテクスチャに対応させるには、テクスチャ座標を含む Microsoft.Xna.Framework.Graphics.VertexPositionTexture 構造体を使います。
[SerializableAttribute] public struct VertexPositionTexture : IVertexType
この構造体のコンストラクタには、頂点の座標とテクスチャの座標を指定します。
public VertexPositionTexture ( Vector3 position, Vector2 textureCoordinate )
position パラメータには頂点の空間座標を指定します。textureCoordinate パラメータには、頂点に対応するテクスチャ座標を指定します。
テクスチャ座標は頂点が対応するテクスチャの位置を表すベクトルです。テクスチャの左上隅を原点とし、図1のように水平方向を u 軸、垂直方向を v 軸とした 0 ~ 1 までの範囲の値で表されます。Vector2 型の X フィールドが u 軸、Y フィールドが v 軸に対応します。
プロファイルやグラフィックスデバイスの性能によりますが、テクスチャのサイズは縦横共に 2 の累乗(256 ピクセルや 512 ピクセルなど)でなければならず、テクスチャの最大サイズも定められています。Reach プロファイルの場合は 2048 ピクセル、HiDef プロファイルの場合は 4096 ピクセルが最大テクスチャサイズと定められています。
設定した空間座標は Position フィールドに、テクスチャ座標は TextureCoordinate フィールドに設定されています。
[SuppressMessageAttribute("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] public Vector3 Position
[SuppressMessageAttribute("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] public Vector2 TextureCoordinate
これらのフィールドから、頂点の空間座標とテクスチャ座標を設定または取得できます。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; public class TestGame : Game { public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private VertexBuffer vertexBuffer; private IndexBuffer indexBuffer; private BasicEffect effect; private VertexPositionTexture[] vertices; private short[] indices; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { Vector3 pt1 = new Vector3(-0.5F, 0.8F, 0); Vector3 pt2 = new Vector3(0.5F, 0.8F, 0); Vector3 pt3 = new Vector3(-0.5F, -0.8F, 0); Vector3 pt4 = new Vector3(0.5F, -0.8F, 0); Vector2 uv1 = new Vector2(0, 0); Vector2 uv2 = new Vector2(1, 0); Vector2 uv3 = new Vector2(0, 1); Vector2 uv4 = new Vector2(1, 1); vertices = new VertexPositionTexture[] { new VertexPositionTexture(pt1, uv1), new VertexPositionTexture(pt2, uv2), new VertexPositionTexture(pt3, uv3), new VertexPositionTexture(pt4, uv4), }; indices = new short[] { 0, 1, 2, 1, 3, 2 }; base.Initialize(); } protected override void LoadContent() { vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), vertices.Length, BufferUsage.None); vertexBuffer.SetData(vertices); GraphicsDevice.SetVertexBuffer(vertexBuffer); indexBuffer = new IndexBuffer(GraphicsDevice, IndexElementSize.SixteenBits, indices.Length, BufferUsage.None); indexBuffer.SetData(indices); GraphicsDevice.Indices = indexBuffer; effect = new BasicEffect(GraphicsDevice); effect.TextureEnabled = true; effect.Texture = Content.Load<Texture2D>("TestImage"); base.LoadContent(); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Azure); foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Apply(); GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Length, 0, 2); } base.Draw(gameTime); } }
コード1は、三角形のプリミティブを 2 枚繋ぎ合わせた長方形の表面にテクスチャを貼り付けています。透過 PNG ファイルをテクスチャとして読み込んでいるため、透過部分が黒く塗りつ美されています。プリミティブを透過させる方法は後述します。
この単純な実行結果だけを見ると 1 枚のテクスチャをスプライトとして描画しているように見ますが、実体は分割された 2 つの三角形の表面に、図2のような形でテクスチャが張り付けられています。
複雑なモデルのテクスチャに対しても、基本的な考え方は同じです。グラフィックスデバイスに対して描画命令(Draw~Primitives() 系のメソッド)を行う回数は少ない方がパフォーマンスがよいので、できる限り頂点やインデックス、そしてテクスチャを 1 つにまとめて描画するべきです。