忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


[Windows® API Code Pack for Microsoft® .NET Framework] C#でDirectX11をつかう その2 背景のクリア

前回作った雛形にコードを追加していきます。 

今回はDirect3Dのデバイスを作成して
ウィンドウの背景をクリアします。


using Microsoft.WindowsAPICodePack.DirectX.Direct3D11;
using Microsoft.WindowsAPICodePack.DirectX.Graphics;

class Program
{
    static void Main()
    {
        using (Game game = new Game())
        {
            game.Run();
        }
    }
}

class Game : System.Windows.Forms.Form
{
    SwapChain swapChain;
    DeviceContext deviceContext;
    RenderTargetView renderTargetView;

    public void Run()
    {
        initDevice();
        Show();

        while (Created)
        {
            Draw();
            System.Windows.Forms.Application.DoEvents();
        }
    }

    private void Draw()
    {
        deviceContext.ClearRenderTargetView(renderTargetView, new ColorRgba(0, 0, 1, 1));
        swapChain.Present(0, PresentOptions.None);
    }

    private void initDevice()
    {
        D3DDevice device = D3DDevice.CreateDeviceAndSwapChain(this.Handle);
        this.swapChain = device.SwapChain;
        this.deviceContext = device.ImmediateContext;

        using (Texture2D texture2D = swapChain.GetBuffer<Texture2D>(0))
        {
            this.renderTargetView = device.CreateRenderTargetView(texture2D);
            this.deviceContext.OM.RenderTargets = new OutputMergerRenderTargets(new[] { renderTargetView });
        }
    }
}
 これは背景を青でクリアしています。

directX11TutorialClearRenderTarget.jpg

最初にDirect3Dに関連したオブジェクトを初期化し
それを使ってwhileループの中で
延々と背景を青にクリアし続けています。

大まかに説明すると
こうなります:
クラス名 説明 XNAで言うと
SwapChain
 
 
 
これはダブルバッファリングを行うための2つのバッファを持っています。
 
(片方が描画対象、もう片方はディスプレイに表示されるバッファ)
 
この2つのバッファは、描画が終わって実際にディスプレイに表示するときに
 
役割が交代(スワップ)します
GraphicsDeviceがこれの機能を持っています
DeviceContext
描画を行うオブジェクトです。
これを使ってポリゴンとかいろいろなものを描画します。
GraphicsDeviceがこれに近いです。
RenderTargetView 描画する対象です。 RenderTarget2Dでしょうか

これら3つのオブジェクトを手に入れるためにD3DDeviceをつくっています。
SwapChainとIntermediateContextプロパティを通じて2つのオブジェクトを手に入れています。

「プロパティがあるんだからわざわざ2つもメンバ変数にしなくていいじゃない?
必要に応じてプロパティにアクセスすればメンバ変数は一つ減らせるし」
、と思われるかもしれません。

が、Windows API Code Packのソースを読めばわかりますが、
実はこのプロパティは内部で新たに生成して返しています。
今後実装が変わる可能性もありますが、ここでは念のためメンバ変数として確保しておきます。


エラーが起きる場合

64bit OSを使っている場合、
Windows API Code Packに含まれるx64のアセンブリを使いたくなるかもしれません。

しかしx86ではなくそっちを使うと、
BadImageFormatExceptionがスローされると思います。

しかしx86のアセンブリに切り替えて実行すると
今度はFileLoadExceptionがスローされるかもしれません。
にっちもさっちもいきません。
この原因は、アセンブリのランタイムバージョンが2.0だからです。

これはプロジェクトに.configファイルを次のように作れば解決します。
要はランタイムのバージョンが2.0であるアセンブリを使うことを示してやれば良いのです。

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0"/>
  </startup>
</configuration>



 

拍手[1回]

PR

[Windows® API Code Pack for Microsoft® .NET Framework] C#でDirectX11をつかう その1 下準備

(対象読者は「XNAが使えるけどDirectXはちょっとめんどくさいなぁ」と感じている人を想定しています)

XNAではDirectX11世代の技術は使えない

簡単に3Dゲームを作れるXNAですが、
限界もあります。

XNAはDirectX9世代ですが、
現在DirectXの最新世代は11なのです。
つまりXNAでは最新技術を使えません

ではXNAがDirectX11以上に将来対応するかというと、
おそらくいつかはするのでしょうが、
Xbox360との兼ね合いですぐには難しいでしょう。

MicrosoftとしてはXNAで作ったゲームはXbox360
で動いて欲しいはずですが、
肝心のXbox360がDirectX9固定です。
XNAが11に対応してしまえば、XNAで作ったゲームが
Xbox360で動かせないケースが出てくるはずです。

ですから、XNAが11移行に対応するとすれば
それはXbox360の次の世代のゲーム機が発売された時だと思われます。
しかし残念なことにそれは、まだまだ先のことのようです。



Windows API Code Pack for Microsoft .NET Framework

つまりC#で最新技術に追いつくのはやや面倒なことになっているのですが、
一方で救いもあります。
 Windows® API Code Pack for Microsoft® .NET Frameworkというものがあるのです。

ここでは最新APIの数々がC#で使えるようにラッパーが書かれています。
DirectX11もその中に含まれています。

C#でDirectX11が使えるわけです

しかし見たところすべての機能がラップされているわけではありません。
たとえばMatrix4x4Fにファクトリメソッドは一つもありません
XNAの豊富なメソッド群と比べるとまだまだ貧相です。
今すぐ使うのはまだ難しいけれど、将来に期待といったところでしょうか。
ただ動作を確認するだけなら今でもできるはずです。

使ってみましょう。


Windows API Code Pack for Microsoft .NET Frameworkのインストール

インストールはここ

Windows API Code Pack v1.1 (Binaries, Source Docs)

というリンクをクリックします。
現在バージョンは1.1で、2010年9月のもののようです。

クリックすると

Windows API Code Pack Self Extractor.exe

というファイルがダウンロードされますので、実行します。
すると同じディレクトリに

Windows API Code Pack 1.1.zip

というファイルが展開されます。
この中に.netのアセンブリが含まれています。
Visual StudioのProjectフォルダかどこかに移動して展開しましょう。


出来たフォルダの中を見ると、

binaries > DirectX > x86

にDirectXのラッパーアセンブリがあります。

Visual C#のプロジェクトを作ってこれを参照追加してみましょう。


コードの雛形

いきなりDirectXを使ってみるというのは
その複雑さから言って無謀です!

順序良く行きましょう。
シンプルなものから少しずつ発展させていくのです。

まずはWindows Formsを使って
ウィンドウを表示します。

Program.cs

class Program
{
    static void Main()
    {
        using (Game game = new Game())
        {
            game.Run();
        }
    }
}

class Game : System.Windows.Forms.Form
{
    public void Run()
    {
        Show();

        while (Created)
        {
            System.Windows.Forms.Application.DoEvents();
        }
    }
}



このコードはウィンドウを一つ表示します。

directX11TutorialCreateWindow.jpg

Application.Run()ではなくwhileループを使っているのは
そのほうが後に書くことになるプログラムの構造に近いからです。

ちなみにどことなくクラス名メソッド名などが
XNAに似ているのはわざとです。

今後もこの調子で
続けていきます。




拍手[1回]


C#でRubyっぽいループを書く

(この記事を書き終わってもしやと思いググッてみると
どうやら同じことを考えている人がすでに居たようです。残念)


4,5年前Rubyを使ったことがあったのですが、
ループがけっこう簡単です。
5.times { |i|
	puts i
}
Rubyを使ったことのない方のために言うと、
上の実行結果は
0
1
2
3
4

です。

これはたぶんC#よりも初学者にとって分かりやすいと思います。
なにせ「5回繰り返せ!」というのが
5.timesからぐっと伝わってくるからです。
(そう思いませんか!?)

私が初めて覚えたプログラミング言語はCでしたが、
当時中学生の身としてforはいささか複雑すぎました。
結果forに引っかかったことを覚えています。

C#でクロージャを使ってループ

しかしここで言いたいのはC#はダメだということではありません!
C#でもRubyのような書き方ができるからです。

まずC#で普通にループを書こうと思えば
以下のようにforを使います。
class Program
{
    static void Main(string[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            System.Console.WriteLine(i);
        }
    }
}


結果は、上に書いたrubyのコードと同じです。
数字を0から4まで出力します。

C#でRubyのように書くにはどうすればいいでしょう?
ここで拡張メソッドの登場です。

static class Int32Extension
{
    public static void Times(this int loopCount, System.Action<int> loop)
    {
        for (int i = 0; i < loopCount; i++)
        {
            loop(i);
        }
    }
}


皆さんご存知のとおり上のように書くと、
System.Int32構造体に拡張メソッドを追加できます。
つまり以下のようにかけるのです。
class Program
{
    static void Main(string[] args)
    {
        5.Times(i => System.Console.WriteLine(i));
    }
}

いかがでしょうか。
forを使うよりシンプルになっているのは確かです。

ただ・・・これは良いものなのでしょうか悪いものなのでしょうか?
なるほどシンプルで書きやすいというのはそのとおりでしょうが、
forより明らかにパフォーマンスが落ちそうです。
もっともパフォーマンスはネックになるところ以外では気にするべきではありません。

では読みやすさはどうでしょうか?
個人的にこれを使ってみて、
「書きやすいけれどももしかするとほんの少し読みにくいかもしれない」
といった感想です。
読みにくいかもしれないというのはデリゲートに{}を使ったとき);と重なって
ちょっと汚く見えるかもしれないということです。

ただ、Timesの他にもUpToメソッドなどを書いて使ってみたのですが
そちらは});がごちゃごちゃしているのを差し引いても
かなり読みやすくなったと思います。

個人的見解としては、今後もループをたくさん使わなければいけないときにはこれを使うと思います、
といったところでしょうか。

拍手[0回]


WPFで列挙型の表示(DataTemplateSelector)

 WPFの話です。

さて以下のような列挙型があり、
それをWPFで表示したくなったとします。

public enum FigureType
{
    Ellipse,
    Rectangle,
    Triangle
}
3つの値はそれぞれ図形のタイプを表しています。
丸、四角、三角です。

例えば「この中から一つ図形をユーザーに選ばせたい」という状況を考えてください。

すると、WPFでは全体として次のようになるでしょう。
(全体として、と言いましたがMainWindow.csとMainWindow.xamlだけです。
App.xaml、App.csはデフォルトのままなので割愛します。)


C#
using System.Windows;

namespace DataTemplateSelectorDemo
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            figuresView.ItemsSource = System.Enum.GetValues(typeof(FigureType));
        }
    }

    public enum FigureType
    {
        Ellipse,
        Rectangle,
        Triangle
    }
}

XAML
<Window x:Class="DataTemplateSelectorDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

    <ListBox x:Name="figuresView"/>
    
</Window>


この結果がどうなるかというと、以下のようなぐあいです。
dataTemplateSelectorWithoutSelector.png

リストボックス中に3つの選択肢が出てきます!
リストボックスなのでマウスでクリックするとそれ相応のイベントが発生し
選択した図形をもとにプログラムを書くことができます。

しかしここで表示されるのはあくまでも文字なので、
ちょっと使いにくいと考える人もいるでしょう。

Ellipse, Rectangle, Triangleよりも
○、□、△のほうが直感的です。

DataTemplateSelectorを使う

では列挙型をもとに図形を表示するにはどうすればいいでしょうか?
WPFではSystem.Windows.Controls.DataTemplateSelectorを利用するとできます。

(別の方法もあるのですが)
これを使うと以下のようになります。

C#
using System.Windows;
using System.Windows.Controls;

namespace DataTemplateSelectorDemo
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            figuresView.ItemsSource = System.Enum.GetValues(typeof(FigureType));
        }
    }

    public enum FigureType
    {
        Ellipse,
        Rectangle,
        Triangle
    }


    public class FigureTypeDataTemplateSelector : DataTemplateSelector     {
public DataTemplate EllipseDataTemplate { get; set; }
public DataTemplate RectangleDataTemplate { get; set; }
public DataTemplate TriangleDataTemplate { get; set; }

public override DataTemplate SelectTemplate(
object item,
DependencyObject container
)
{
if (!(item is FigureType)) return null;

switch ((FigureType)item)
{
case FigureType.Ellipse:
return EllipseDataTemplate;
case FigureType.Rectangle:
return RectangleDataTemplate;
case FigureType.Triangle:
return TriangleDataTemplate;
default: return null;
}
}
}
}

XAML
<Window x:Class="DataTemplateSelectorDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:model="clr-namespace:DataTemplateSelectorDemo"
        Title="MainWindow" Height="350" Width="525">

    <ListBox x:Name="figuresView" ItemTemplateSelector="{DynamicResource templateSelector}"/>

    <Window.Resources>
<DataTemplate x:Key="MyEllipseDataTemplate">
<Ellipse Stroke="Black" Width="100" Height="100"/>
</DataTemplate>
<DataTemplate x:Key="MyRectangleDataTemplate">
<Rectangle Stroke="Black" Width="150" Height="100"/>
</DataTemplate>
<DataTemplate x:Key="MyTriangleDataTemplate">
<Polygon Stroke="Black" Points="100,0 0,100 200,100"/>
</DataTemplate>

<model:FigureTypeDataTemplateSelector
x:Key="templateSelector"
EllipseDataTemplate="{StaticResource MyEllipseDataTemplate}"
RectangleDataTemplate="{StaticResource MyRectangleDataTemplate}"
TriangleDataTemplate="{StaticResource MyTriangleDataTemplate}"
/> </Window.Resources>
</Window>


DataTemplateSelectorは、「どの値の時にどのように表示するか」を決定することができます。
丸の時には○を表示するように、四角の時には□を表示するように、三角の時には△を表示するようにしたいものです。
そういったことを実際に決定するのがDataTemplateSelector.SelectTemplate()メソッドで、
上のコードではswitchで場合分けしてデータの表示方法(DataTemplate)をreturnしています。

結果はこうなります。

dataTemplateSelectorDemoWithFigure.jpg


リストボックス中に図形が表示されました!

ここでは列挙型を表示しましたが、
もちろん、DataTemplateSelectorは列挙体だけでなくintなど他の値であっても使えるクラスです。























拍手[2回]