ロコリンの雑記

アニメ大好きニートのロコリンのブログ。2015年卒(修士)の社会人。学生時代(2010年)から続けてるブログなのでエントリによっては学生ブログと社会人ブログになっています。時系列から察して。
 
 
このブログについて
ブログ内検索
カテゴリ
プロフィール

ロコリン

Author:ロコリン
2017年4月から会社員になる予定のニート。2015年3月まで大学院生でした。
趣味:アニメ/Twitter/ゲーム
今(2015年2月更新):プリキュア/プリパラ/アイカツ/ごちうさ/艦これ

外部リンク
Twitter

スポンサーサイト 

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

[C#][OpenTK] マンデルブロ集合の描画 

マンデルブロ集合

OpenGL で2次元実スカラー場 (1次元複素スカラー場) を可視化するためのデモとしてマンデルブロ集合を描画します。

マンデルブロ集合とは、次の漸化式が発散しないような複素数 \(c\) の集合 \(M\) です。(あまり厳密に説明する気力がない。)

\[\begin{cases} z_{0} = 0 \\ z_{n+1} = {z_{n}}^{2} + c \end{cases}\]
\[M=\left\{c\in\mathbb{C}\,\middle|\,\lim_{n\to\infty}\left|z_{n}\right|<\infty\right\}\]

プログラムではこの漸化式を有限項で打ち切ります。(仮に第 \(m\) 項とします。) \(\left|z_{m}\right|^{2}\) が4未満なら収束、4以上なら発散と判定することにします。発散と判定する時、最初に発散と判定される項の添え字で点の色を変えるとカラフルな図になりますが、どうでもいいなら気にしなくていいです。(うまく説明できない。)

この記事では次の条件でプログラムを作成します。描画には OpenGL を用います。開発環境としては Visual C# 2012 を用います。C# における OpenGL のラッパーとして OpenTK を用います。(説明面倒臭い。)

OpenTK の導入方法については、面倒なので他者のブログに任せます。(どんだけ記事書く気ないんだ。)

OpenTK をインストールしたら、Visual Studio 2012 を起動してください (もしこの記事が古くなったら新しい VS で OK です。)。

Windows フォーム アプリケーションのプロジェクトを作成してください。名前は何でもいいですが、この記事では GlMandelbrotSet とします。

参照設定に OpenTK.dll と OpenTK.GLControl.dll を追加してください。

初回はツールボックスに GLControl アイテムが表示されないので追加したほうがいいです。メニューバーから [ツール(T)] → [ツールボックス アイテムの選択(X)...] を開き、.NET Framework コンポーネントにある GLControl にチェックを入れて [OK] をクリックします。

ツールボックスの [すべての Windows フォーム] から [GLControl] を探し、これをフォームデザイナのフォームにドラッグしてください。黒い長方形として表示されると思います。これをクリックして、プロパティを編集します。[Dock] プロパティを Fill にすると、フォーム全体が GLControl になります。名前はとりあえず glControl1 のままでいいです。

glControl1 のイベントを設定します。プロパティウィンドウにある雷アイコンをクリックするとイベントが表示されます。このうち Load と Resize と Paint イベントをダブルクリックしてください。Form1.cs に glControl1_Load と glControl1_Resize と glControl1_Paint メソッドが追加されると思います。

Form1.cs を開きます。以下のようにしてください。(環境に合わせて適宜変更してください。)

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using OpenTK;	// 追加
using OpenTK.Graphics.OpenGL;	// 追加

namespace GlMandelbrotSet
{
    public partial class Form1 : Form
    {
        // 表示領域の縦幅
        readonly double _Height = 4.0;
        // 注視点
        readonly Vector2 _CentralPoint = Vector2.Zero;
        // マンデルブロ集合描画時の色リスト
        readonly List<Color> _ColorList = new List<Color>();

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Form1()
        {
            InitializeComponent();

            // 色リストの初期化
            InitializeColorList();
        }

        /// <summary>
        /// GL コントロールのロードイベント
        /// </summary>
        /// <param name="sender">イベント発行元</param>
        /// <param name="e">イベント追加情報</param>
        private void glControl1_Load(object sender, EventArgs e)
        {
            glControl1.MakeCurrent();

            // 背景色の設定
            GL.ClearColor(glControl1.BackColor);

            // Projection の設定
            SetProjection();
        }

        /// <summary>
        /// GL コントロールのリサイズイベント
        /// </summary>
        /// <param name="sender">イベント発行元</param>
        /// <param name="e">イベント追加情報</param>
        private void glControl1_Resize(object sender, EventArgs e)
        {
            glControl1.MakeCurrent();

            SetProjection();

            glControl1.Refresh();
        }

        /// <summary>
        /// GL コントロールの描画イベント
        /// </summary>
        /// <param name="sender">イベント発行元</param>
        /// <param name="e">イベント追加情報</param>
        private void glControl1_Paint(object sender, PaintEventArgs e)
        {
            glControl1.MakeCurrent();
            GL.Clear(ClearBufferMask.ColorBufferBit);

            GL.PointSize(1.0f);
            double sideY = _Height * 0.5;
            double sideX = sideY * glControl1.AspectRatio;
            DrawMandelbrot(_CentralPoint.X - sideX, _CentralPoint.X + sideX, _CentralPoint.Y - sideY, _CentralPoint.Y + sideY, glControl1.Width, glControl1.Height);

            glControl1.SwapBuffers();
        }

        /// <summary>
        /// 投影の設定
        /// </summary>
        private void SetProjection()
        {
            // ビューポートの設定
            GL.Viewport(0, 0, glControl1.Width, glControl1.Height);

            // 変換行列の初期化
            GL.LoadIdentity();

            // 描画領域の設定
            double sideY = _Height * 0.5;
            double sideX = sideY * glControl1.AspectRatio;
            GL.Ortho(_CentralPoint.X - sideX, _CentralPoint.X + sideX, _CentralPoint.Y - sideY, _CentralPoint.Y + sideY, -1.0, 1.0);
        }

        /// <summary>
        /// 色リストの初期化
        /// </summary>
        private void InitializeColorList()
        {
            _ColorList.Add(Color.Black);
            for (int i = 1; i <= 16; ++i)
            {
                int x = 31 + 14 * i;
                _ColorList.Add(Color.FromArgb(x, x, x));
            }
        }

        /// <summary>
        /// ある複素数がマンデルブロ集合の元かを調べる。
        /// </summary>
        /// <param name="re">実部</param>
        /// <param name="im">虚部</param>
        /// <param name="nMax">漸化式を調べる回数</param>
        /// <returns>複素数がマンデルブロ集合の元ならば0、そうでなければ0より大きい値</returns>
        private int Mandelbrot(double re, double im, int nMax = 16)
        {
            double x = 0.0, y = 0.0, x1, y1;
            for (int n = 0; n < nMax; ++n)
            {
                x1 = x * x - y * y + re;
                y1 = 2.0 * x * y + im;
                if (x1 * x1 + y1 * y1 > 4.0) { return n + 1; }
                x = x1; y = y1;
            }
            return 0;
        }

        /// <summary>
        /// マンデルブロ集合を描画する。
        /// </summary>
        /// <param name="xMin">描画領域左端</param>
        /// <param name="xMax">描画領域右端</param>
        /// <param name="yMin">描画領域下端</param>
        /// <param name="yMax">描画領域上端</param>
        /// <param name="screenWidth">スクリーン横幅</param>
        /// <param name="screenHeight">スクリーン縦幅</param>
        private void DrawMandelbrot(double xMin, double xMax, double yMin, double yMax, int screenWidth, int screenHeight)
        {
            double xDiff = xMax - xMin, yDiff = yMax - yMin;
            ++screenWidth; ++screenHeight;
            GL.Begin(BeginMode.Points);
            for (int i = 0; i < screenWidth; ++i)
            {
                double x = xMin + xDiff * (double)i / (double)screenWidth;
                for (int j = 0; j < screenHeight; ++j)
                {
                    double y = yMin + yDiff * (double)j / (double)screenHeight;
                    GL.Color3(_ColorList[Mandelbrot(x, y, 16)]);
                    GL.Vertex2(x, y);
                }
            }
            GL.End();
        }
    }
}

すみませんが、順を追って解説するのが面倒というか、面倒というか、面倒なのでパスします。プレゼンテーション能力ェ…。

参考文献

スポンサーサイト
コメント















 管理者にだけ表示を許可する

トラックバック
 
http://rexpit.blog29.fc2.com/tb.php/94-002e7f11
最新記事
最新コメント
FC2カウンタ
やりたいゲーム

艦これはやってる

神姫PROJECT かんぱに☆ガールズ オンラインゲーム ロードオブワルキューレ オンラインゲーム
欲しい
最近買ったもの
Amazon 検索
 
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。