2016年8月18日木曜日

C#でれんだりんぐえんじん



ちょっとFormで3Dを表示したくなったので試してみた。
とりあえずXNAのVectorとかQuaternionを使えば簡単に座標変換ができるので、平行投影ならサクッと作れる(それでも結構なソースになるが)。

ただ、あくまで座標変換して書き重ねてるだけなので、後ろにある物体を書いても、前にある物体の手前に書かれることになる。

ちゃんとした表示を行うなら遠近法の変換だったり、前後を比較して書き込む順番を変えたりといった作業が必要になる。そこまでやると3Dのレンダリングエンジンになってしまうし、かなり面倒なことになると思うけども。

それから、この座標変換は1点のみに適用される。なので線や面に対しては適用されないことに注意する必要がある。
例えばGraphicsに四角形を書く場合、通常であればFillRectangleとかを使うことになると思うが、コレでは思い通りの結果にはならないと思う。サンプルの通り、頂点の位置を変換してからFillPolygonで表示すれば予想通りの結果となる。


他の方法としてはGraphicsのTransformを使う方法もあるが、コレは思ったように動かすのはちょっと難しい。ということで僕は早々に見切りをつけてXNAの型を使っている。
ただしXNAを使ったところで線やポリゴンを書くことが限界だから、テクスチャを貼りたいならGraphics.Transformを使う必要があると思う。


ちゃんと3Dを表示させたい場合、最低でも前後の位置関係はどうにかしたい。コレはデータを一旦バッファにためてから、頂点の平均位置を比較して視点から遠い順に書いていく、という感じの処理になるのかな。
とりあえず当初の目標はすでにクリアしているのだけど、もうちょっとレンダリングエンジンについて調べてみると面白いのかも。



public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        Bitmap bmp = new Bitmap(400, 400);
        using (Graphics g = Graphics.FromImage(bmp))
        {
            GraphicsTransform gt = new GraphicsTransform(g);

            gt.Push();

            Quaternion q = new Quaternion(0, 0, 0, 1);

            q = Quaternion.CreateFromAxisAngle(Vector3.Right, MathHelper.ToRadians(45)) * q;
            q = Quaternion.CreateFromAxisAngle(Vector3.Down, MathHelper.ToRadians(45)) * q;

            Quaternion q2 = q;

#if true
            g.TranslateTransform(bmp.Width * 0.5f, bmp.Height * 0.5f);
#else
            Matrix mtx = Matrix.CreateFromQuaternion(q);
            g.Transform = new System.Drawing.Drawing2D.Matrix(
                mtx.M11, mtx.M12,
                mtx.M21, mtx.M22,
                bmp.Width * 0.5f, bmp.Height * 0.5f);
#endif

            List<PointF[]> posArray = new List<PointF[]>();

            #region

            posArray.Add(new PointF[]
            {
                    ConvertPosition(new Vector3(+100, +100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, +100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, -100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, -100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, +100, +100), q2).ToPointF(),
            });

            posArray.Add(new PointF[]
            {
                    ConvertPosition(new Vector3(+100, +100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, +100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, -100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, -100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, +100, -100), q2).ToPointF(),
            });

            posArray.Add(new PointF[]
            {
                    ConvertPosition(new Vector3(+100, +100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, +100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, -100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, -100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, +100, +100), q2).ToPointF(),
            });
            posArray.Add(new PointF[]
            {
                    ConvertPosition(new Vector3(-100, +100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, +100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, -100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, -100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, +100, +100), q2).ToPointF(),
            });

            posArray.Add(new PointF[]
            {
                    ConvertPosition(new Vector3(+100, +100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, +100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, +100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, +100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, +100, +100), q2).ToPointF(),
            });

            posArray.Add(new PointF[]
            {
                    ConvertPosition(new Vector3(+100, -100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, -100, +100), q2).ToPointF(),
                    ConvertPosition(new Vector3(-100, -100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, -100, -100), q2).ToPointF(),
                    ConvertPosition(new Vector3(+100, -100, +100), q2).ToPointF(),
            });

            #endregion

            foreach (PointF[] arr in posArray)
            {
                g.FillPolygon(Brushes.Red, arr);
            }

            foreach (PointF[] arr in posArray)
            {
                g.DrawLines(Pens.Black, arr);
            }

            gt.AllPop();
        }

        Size += bmp.Size - pictureBox1.Size;
        MinimumSize = MaximumSize = Size;
        pictureBox1.Image = bmp;
    }

    protected Vector2 ConvertPosition(Vector3 Pos, Quaternion q)
    {
        Vector3 vt = Vector3.Transform(Pos, q);
        return (new Vector2(vt.X, -vt.Y));
    }

    class GraphicsTransform
    {
        protected Graphics Graphics_;
        protected Stack<System.Drawing.Drawing2D.Matrix> Stack_;
        readonly bool NotException;

        public GraphicsTransform(Graphics Graphics, bool NotException = true)
        {
            this.NotException = NotException;
            Graphics_ = Graphics;
            Stack_ = new Stack<System.Drawing.Drawing2D.Matrix>();
        }

        public void Push()
        {
            Stack_.Push(Graphics_.Transform);
        }

        public bool Pop()
        {
            if (NotException && Stack_.Count == 0) { return (false); }

            Graphics_.Transform = Stack_.Pop();

            return (true);
        }

        public void AllPop()
        {
            if (Stack_.Count < 1) { return; }

            while (Stack_.Count > 1)
            {
                Stack_.Pop();
            }

            Graphics_.Transform = Stack_.Pop();
        }
    }
}

static class MyExt
{
    static public PointF ToPointF(this Vector2 v)
    {
        return (new PointF(v.X, v.Y));
    }
}

0 件のコメント:

コメントを投稿