はじめてのレイトレーシング

レイトレーシングを一度はやってみたいと思っていましたので挑戦してます。

参考になるサイトや書籍を探す

ネットでいろいろ調べては見たけれど、具体的なやり方やソースコードまで載せている分かりやすいところは見つかりませんでしたが、おすすめのレイトレーシングの本を挙げていらっしゃる方がいました。

この中から初心者向けかつ Kindle で今すぐ手に入る本として以下を選んでみました。

内容は基礎から説明されており、かつ、C++ で書かれた具体的な実装のソースコードも記載されていて良い感じです。

ソースコードは配布されてもいるのですが、Windows 向けではないので、ビルドを通そうとするとソースコードに修正が必要です。私は面倒くさいので、こちらのビルドを通すのは諦めました。

また、本に記載されるソースコードと微妙に違うようです。

本を読んで実装する

この本のサンプルを写経するにあたり、私は C# が好きなので、C# で実装することにしました。

第 3 章までを読んでサンプルを実装した結果、得られた出力が以下の通りです。

f:id:hon_ya:20150311232504p:plain

Kindle では画像が白黒になっていて、サンプルの正しい出力がシルエットでしか分からないのですが、どうやら正しく実装できていそうです。

SharpDX + BulletSharp

物理演算をやりたい。

Bullet を使う

Bullet の使い方は、以下の書籍から習いました。

Amazon.co.jp: Learning Game Physics with Bullet Physics and OpenGL 電子書籍: Chris Dickinson: Kindleストア

サンプルソースコード付きでページ数の少ない本で、とりあえず初めて見る目的で読む分には都合が良いと思いました。

公式のサンプルはどうもミニマムじゃないので分かりにくい。

3 章まで読んだ内容で、コリジョンを処理するプログラムを作成しました。


BulletSharp for SharpDX を使うとビルドエラーが起きる

Bullet を使うよりも、BulletSharp を正しく使える環境を用意することに時間がかかりました。

nuget から SharpDX を導入している環境で BulletSharp for SharpDX のアセンブリを参照して実装を行うと、ビルド時にエラーが出ました。

Error 1 The type 'SharpDX.Vector3' is defined in an assembly that is not referenced. You must add a reference to assembly 'SharpDX, Version=2.6.3.0, Culture=neutral, PublicKeyToken=null'.

SharpDX がアカンといわれる。参照しているはずなのに。
nuget から SharpDX を導入した場合、以下の SharpDX.dll が参照されていました。

packages\SharpDX.2.6.3\build\..\Bin\DirectX11-Signed-net40\SharpDX.dll

これは、"SharpDX, Version=2.6.3.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1" な DLL です。
PublicKeyToken が null ではない。

どうも、BulletSharp は、PublicKeyToken=null な SharpDX しか受け付けないようなビルドが行われているっぽい?

そこで、nuget から SharpDX を導入した時に一緒に落ちてきている以下の Unsigned な DLL を参照するように変更します。

packages\SharpDX.2.6.3\build\..\Bin\DirectX11-net40\SharpDX.dll

これで正しくビルドが通りました。

MMD PMD + VMD

タイトル修正。PMX やめて PMD でやったのだった。

まず、C++ なんてものでコーディングしてられないなって思ったので C#DirectX を扱える SharpDX に乗り換えました。

SharpDX - Managed DirectX


C# で実装できる素晴らしさ。

そして、3D Game Programming with DirectX11 という本で勉強しました。
英語ですが、ソースコード付きでわかりやすい。

ここまでで、VMD ファイルを使って MMD モデルを躍らせることができるようになりました。

さらに、VMD は IK を使わないと足が棒のようにぶらぶらするだけなので、IK を実装しました。
以下の動画など参考にしました。動いてるものがソースコード付きであると助かります。

【ゆっくり解説】 第8回 MMDモデルを踊らす 【3Dプログラミング】 ‐ ニコニコ動画:GINZA

最初に作った踊らせる仕組みが悪くて苦労させられました。

出来上がったのがこちら。膝がガクガクなのは、正しくは角度制限しなきゃいけないところをやってないから。

WPF Data Binding

WPF の Binding について復習中。

データ バインディングの概要

方法 : コードでバインディングを作成する

Data Points: Data Binding in WPF

データと WPF: データ バインドと WPF でデータの表示をカスタマイズする

WPF で MVVM

Livet などの MVVM ライブラリを真面目に使いたいので、まずは WPF での MVVM について勉強しました。

メインはここの内容を参考にしました。
WPF/MVVM Quick Start Tutorial - CodeProject

関連してその他いろいろ参考にしました。
連載 WPF/Silverlight UIフレームワーク入門:第2回 データの表示と入力に必要な知識 (1/5) - @IT

CommandManager クラス (System.Windows.Input)

連載:WPF入門:第6回 「コマンド」と「MVVMパターン」を理解する (1/3) - @IT

CommandManager.RequerySuggested を使う意味がよくわからずに困りました。

RoutedCommand.CanExecuteChanged イベント (System.Windows.Input)

RoutedCommand は、CommandManager が発生させる RequerySuggested イベントを待機します。 RequerySuggested イベントは、キーボード フォーカスの変更など、コマンドを実行するかどうかを変更する可能性のある条件が満たされたときに発生します。 コマンドが RequerySuggested イベントを受け取ると、CanExecuteChanged イベントを発生させます。 通常、コマンド ソースはこのイベントを待機し、CanExecute メソッドを使用して RoutedCommand に照会します。 コマンドを実行できない場合、多くのコマンド ソースでは、コマンド バインディングの一部として、自動的に自身を無効にします。 コマンドを実行できない場合に淡色表示になる MenuItem は、その一例です。

CommandManager.InvalidateRequerySuggested メソッド (System.Windows.Input)

CommandManager は、キーボード フォーカスの変更など、一定の条件だけを基に、コマンド ターゲットが変更されたものと判断します。 コマンドを実行不可能にする条件が変更されたことを CommandManager が適切に判断しない場合は、InvalidateRequerySuggested を呼び出すことによって、CommandManager に RequerySuggested イベントを強制的に発生させることができます。

キーボードから入力があったとか、マウスがクリックされたとか、ウィンドウにフォーカスが移ったとか、そのたぐいの動きがあったら RequerySuggested に登録されたイベントハンドラをコールしてくれるのでしょう。

今回の CanExecute の実装は、これにおんぶに抱っこでお世話になることで、いろいろ楽をしているようです。

RequerySuggested が呼び出される正確な条件ってどこかにまとめられてないでしょうか。このへんの挙動を理解した上でないと使いにくいと思います。

ソフトウェアの更新機能の実装

.exe ファイルを置き換える

こちらの記事を参考に実装。update ディレクトリに Update.exe を置くと、それを更新データとして現在の Update.exe と置き換える。

http://mo.kerosoft.com/095

public partial class MainWindow : Window
{
    public MainWindow()
    {
        Clean();
        InitializeComponent();
    }

    private void Clean()
    {
        if (Environment.CommandLine.IndexOf("/up", StringComparison.CurrentCultureIgnoreCase) != -1)
        {
            try
            {
                string[] args = Environment.GetCommandLineArgs();
                int pid = Convert.ToInt32(args[2]);
                Process.GetProcessById(pid).WaitForExit();    // 終了待ち
            }
            catch (Exception)
            {
            }
            File.Delete("Update.old");
        }
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if(File.Exists("update/Update.exe"))
        {
            File.Delete("Update.old");
            File.Move("Update.exe", "Update.old");
            File.Move("update/Update.exe", "Update.exe");
            Process.Start("Update.exe", "/up " + Process.GetCurrentProcess().Id);
            this.Close();
        }
        else
        {
            textBlock.Text = "update not found.";
        }
    }
}

C# でトースト通知

C# でトースト通知する方法を調べた。

AppUserModelID 周りは意味わからんかったのでまったく理解してない。