ゲームエフェクトデザイナーのブログ | A Real-Time VFX Artist's Blog

About Making Materials on UE, Making Tools with C#, etc

C# 穴の空いた多角形の3Dモデルにテクスチャを貼ってみた

前回に続いてテクスチャマッピングを行ってみました。
f:id:moko_03_25:20190114214652j:plain 頂点を共有していてもUVにシームを入れたい場合、1つの頂点にはUV座標は1つしか設定できないため、頂点を増やしてUVを別々に設定してあげないといけないようです。
こちらもんしょ(@monsho1977)さんにアドバイスいただいてうまくいきました!ありがとうございます!
 
コードは命名なども適当で公開するのは恥ずかしいですが、どなたかのお役に立つかもなので一応載せておきたいと思います。

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace MonoGame3DTest2
{
    public class MyGame : Game
    {
        ////////////////////////////////////////////////////
        //                                               //
        // 穴の空いた円ポリゴンにテクスチャを貼るサンプル //
        //                                               //
        ////////////////////////////////////////////////////

        GraphicsDeviceManager graphics;
        BasicEffect effect;

        static int polyCircleCount = 48;
        float r = 0.3f;
        // UVのシーム用に2箇所で2重に頂点を生成するため+2している
        VertexPositionTexture[] vertices = new VertexPositionTexture[polyCircleCount * 2 + 2];
        int[] indices  = new int[polyCircleCount * 2 * 3];
        
        // テクスチャ
        Texture2D texture;

        // カメラの制御用
        float angle = 0;
        float height = 0;

        public MyGame()
        {
            graphics = new GraphicsDeviceManager(this);
        }

        protected override void LoadContent()
        {
            // 多角形の頂点情報
            for (int i = 0; i < polyCircleCount + 1; i++)
            {
                // 円を分割した1つ分の角度
                double cut = (Math.PI * 2) / polyCircleCount;
                // 角度
                double theta = cut * i;

                // X, Y の算出
                float x2 = (float)(Math.Cos(theta));
                float y2 = (float)(Math.Sin(theta));
                float x1 = r * x2;
                float y1 = r * y2;

                // UVのU座標の算出
                float u = 1.0f / polyCircleCount * i;

                // 円の内側の頂点
                vertices[i] = new VertexPositionTexture
                    (
                        new Vector3(x1, y1, 0),
                        new Vector2(u, 1)
                    );

                // 円の外側の頂点
                vertices[i + polyCircleCount + 1] = new VertexPositionTexture
                    (
                        new Vector3(x2, y2, 0),
                        new Vector2(u, 0)
                    );

                // デバッグ用
                //Console.WriteLine("U: " + u);
                theta = (theta / Math.PI) *180;
                float dX1 = (float)(((double)x1 / Math.PI) * 180);
                float dY1 = (float)(((double)y1 / Math.PI) * 180);
                float dX2 = (float)(((double)x2 / Math.PI) * 180);
                float dY2 = (float)(((double)y2 / Math.PI) * 180);
                Console.WriteLine("i: " + i + " theta: " + theta +
                " x1: " + dX1 + " y1: " + dY1 +
                " x2: " + dX2 + " y2: " + dY2);
            }

            // 穴の空いた円ポリゴンのインデックス生成
            int vertexId = 0;
            for (int i = 0; i < polyCircleCount; i++)
            {
                // 一度に三角形2つ分のIndexを指定するので6ずつ増やす
                int i2 = i * 6;

                // 三角形1つ目
                indices[i2]     = vertexId;
                indices[i2 + 1]    = vertexId + polyCircleCount + 1;
                indices[i2 + 2]    = vertexId + polyCircleCount + 2;
                // 三角形2つ目
                indices[i2 + 3]    = vertexId;
                indices[i2 + 4]    = vertexId + polyCircleCount + 2;
                indices[i2 + 5]    = vertexId + 1;

                vertexId++;
            }

            effect = new BasicEffect(GraphicsDevice)
            {
                TextureEnabled = true,

                Projection = Matrix.CreatePerspectiveFieldOfView
                (
                    MathHelper.ToRadians(45),              // 視野角
                    GraphicsDevice.Viewport.AspectRatio,    // アスペクト比(横/縦)
                    1,     // ニアクリップ
                    100        // ファークリップ
                )
            };

            // 画像を読み込んでテクスチャに設定
            texture = Content.Load<Texture2D>("Content/sample");
        }

        protected override void UnloadContent()
        {
            effect.Dispose();
        }

        protected override void Update(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            if (keyboardState.IsKeyDown(Keys.Left))
                angle -= MathHelper.ToRadians(2.0f); // 1/60秒に0.5°回転
            if (keyboardState.IsKeyDown(Keys.Right))
                angle += MathHelper.ToRadians(2.0f); // 1/60秒に0.5°回転
            if (keyboardState.IsKeyDown(Keys.Up))
                height += 0.1f; // 1/60秒に0.1移動
            if (keyboardState.IsKeyDown(Keys.Down))
                height -= 0.1f; // 1/60秒に0.1移動
        }

        protected override void Draw(GameTime gameTime)
        {
            // 背景のクリア
            Color c = new Color(100, 110, 60);
            GraphicsDevice.Clear(c);

            // テクスチャの設定
            effect.Texture = texture;

            // 両面描画
            GraphicsDevice.RasterizerState = RasterizerState.CullNone;

            // カメラの位置
            effect.View = Matrix.CreateLookAt
            (
                new Vector3
                (
                    3 * (float)Math.Sin(angle),
                    height,
                    3 * (float)Math.Cos(angle)
                ),
                new Vector3(0, 0, 0),   //カメラの注視点
                new Vector3(0, 1, 0)    //カメラのUPベクトル
            );

            // 三角形の描画
            foreach (var pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                // 3角形を実際に描画する
                GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionTexture>(
                    PrimitiveType.TriangleList,
                    vertices,
                    0,
                    vertices.Length,
                    indices ,
                    0,
                    indices .Length / 3
                    );
            }
        }
    }
}

 
例によってこの記述ではポリゴンの面が裏を向いて生成されてしまっていると思います。
(両面描画にしているので表示されていますが)