忍者ブログ

Memeplexes

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

[PR]

×

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


C#でDirectX11 SlimDXチュートリアルその11 テクスチャ

 今までやってきたサンプルはこんな具合でした:

Tutorial03WhiteTriangle.jpg
Tutorial04ColoredTriangle.jpg

今回はちょっと見栄えが変わります。
三角形にテクスチャを貼ります。
Windows7についてくるPenguins.jpgです(Pictures\Sample Pictures)。

Tutorial11TextureMappedTriangle.jpg

三角形にテクスチャを貼るのは物体をリアルに見せる一般的な技法です。
たとえばDirectXに次のような人を表示するサンプルがあるのですが:

BasicHLSL10.jpg

実は次のようなテクスチャを表面に貼っているのです。
(ちょっと怖いですが)

Tiny_skin.JPG

単なる三角形ではいかにも無機質なCGという感じですが、テクスチャを貼ると少しましになります。

テクスチャ

テクスチャを三角形に貼ると一言で言っても、具体的に何をするのでしょうか?
テクスチャを貼る作業は、ピクセルシェーダーの中で行われます。
ピクセルシェーダーはお絵かきで例えると色塗り。
三角形を構成するピクセルそれぞれが具体的にどんな色になるか決定する関数です。
ピクセルシェーダーでテクスチャにアクセスし、そのピクセルの色を決めるのです。
全体としてテクスチャがマップされているように見えるように。

このように、シェーダーの中で使うリソースのことを、シェーダーリソースといいます。
今回テクスチャはシェーダーリソースなのです。
シェーダーリソースとしてリソースを使う場合、SlimDX.Direct3D11.ShaderResourceViewクラスを利用します。

public class ShaderResourceView : ResourceView

このクラスが今回三角形に貼り付けるテクスチャをつかさどります。
テクスチャのロードにはShaderResourceView.FromFile()メソッドを使います。

public static ShaderResourceView FromFile(Device device, string fileName);

deviceはこのシェーダーリソースを作るデバイス。
fileNameはロードするテクスチャの画像ファイル名です。

このメソッドの便利なところは、Texture2Dクラスなんかを経由せずに直接ShaderResourceViewを生成できるところです。
ふつう~~Viewクラスは、リソースを作ってからそれと関連付けるように生成するものです。
ですがそのリソースを~~View以外の用途で使わないのならそれは二度手間です。
シェーダーリソース以外にも使いたい用途があるのならこのメソッドはまずいのですが、今回の用途ではこれで十分です。

こうして生成したテクスチャのビューは、EffectResourceVariable.SetResource()メソッドでエフェクトにセットし、シェーダーから使えるようにします。

public Result SetResource(ShaderResourceView view);

viewはセットするビューです。

HLSL側でのテクスチャの扱い

貼り付けるテクスチャは、C#ではシェーダーリソースとしてセットしますが、HLSL側ではTexture2D変数です。
HLSLでテクスチャのある点の色を取得するには、Texture2D.Sampleメソッドを使います。

float4 Texture2D.Sample(
    SamplerState samplerState,
    float2 location
);
samplerStateはテクスチャから色を取ってくるときにのやり方です。
locationはテクスチャ内の座標です。

戻り値はテクスチャのlocation地点の色です。

SamplerStateはテクスチャのピクセルとピクセルの間の色を取ってくるときに、どのような色の取り方をするかを表します。
が、今回は難しい事はなしで、次のように空の定義でいいでしょう。

SamplerState mySampler {};

locationに渡すテクスチャ座標は、頂点の中に含めておきます。
テクスチャ座標とはテクスチャ中の位置を表す縦1,横1の座標空間です。

Tutorial11TextureCoordinate.jpg

頂点中のテクスチャ座標データには、何かセマンティクスを付ける必要があります。
それにはTEXCOORDを使うといいでしょう。
TEXtureCOORDinate(テクスチャ座標)の略です。


コード

Program.cs
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using SlimDX.D3DCompiler;

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

class MyGame : Game
{
    Effect effect;
    InputLayout vertexLayout;
    Buffer vertexBuffer;
    ShaderResourceView texture;

    protected override void Draw()
    {
        GraphicsDevice.ImmediateContext.ClearRenderTargetView(
            RenderTarget,
            new SlimDX.Color4(1, 0.39f, 0.58f, 0.93f)
            );

        effect.GetVariableByName("diffuseTexture")
            .AsResource().SetResource(texture);
        initTriangleInputAssembler();
        drawTriangle();

        SwapChain.Present(0, PresentFlags.None);
    }

    private void drawTriangle()
    {
        effect.GetTechniqueByIndex(0).GetPassByIndex(0).Apply(GraphicsDevice.ImmediateContext);
        GraphicsDevice.ImmediateContext.Draw(3, 0);
    }

    private void initTriangleInputAssembler()
    {
        GraphicsDevice.ImmediateContext.InputAssembler.InputLayout = vertexLayout;
        GraphicsDevice.ImmediateContext.InputAssembler.SetVertexBuffers(
            0,
            new VertexBufferBinding(vertexBuffer, VertexPositionTexture.SizeInBytes, 0)
            );
        GraphicsDevice.ImmediateContext.InputAssembler.PrimitiveTopology
            = PrimitiveTopology.TriangleList;
    }

    protected override void LoadContent()
    {
        initEffect();
        initVertexLayout();
        initVertexBuffer();
        initTexture();
    }

    private void initEffect()
    {
        using (ShaderBytecode shaderBytecode = ShaderBytecode.CompileFromFile(
            "myEffect.fx", "fx_5_0",
            ShaderFlags.None,
            EffectFlags.None
            ))
        {
            effect = new Effect(GraphicsDevice, shaderBytecode);
        }
    }

    private void initVertexLayout()
    {
        vertexLayout = new InputLayout(
            GraphicsDevice,
            effect.GetTechniqueByIndex(0).GetPassByIndex(0).Description.Signature,
            VertexPositionTexture.VertexElements
            );
    }

    private void initVertexBuffer()
    {
        vertexBuffer = MyDirectXHelper.CreateVertexBuffer(
            GraphicsDevice,
            new[] {
                new VertexPositionTexture
                {
                    Position = new Vector3(0, 0.5f, 0),
                    TextureCoordinate = new Vector2(0.5f, 0) 
                },
                new VertexPositionTexture
                {
                    Position = new Vector3(0.5f, 0, 0),
                    TextureCoordinate = new Vector2(1, 1)
                },
                new VertexPositionTexture
                {
                    Position = new Vector3(-0.5f, 0, 0),
                    TextureCoordinate = new Vector2(0, 1)
                },
            });
    }

    private void initTexture()
    {
        texture = ShaderResourceView.FromFile(GraphicsDevice, "Penguins.jpg");
    }

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

struct VertexPositionTexture
{
    public Vector3 Position;
    public Vector2 TextureCoordinate;

    public static readonly InputElement[] VertexElements = new[]
    {
        new InputElement
        {
            SemanticName = "SV_Position",
            Format = Format.R32G32B32_Float
        },
        new InputElement
        {
            SemanticName = "TEXCOORD",
            Format = Format.R32G32_Float,
            AlignedByteOffset = InputElement.AppendAligned//自動的にオフセット決定
        }
    };

    public static int SizeInBytes
    {
        get
        {
            return System.Runtime.InteropServices.
                Marshal.SizeOf(typeof(VertexPositionTexture));
        }
    }
}

class Game : System.Windows.Forms.Form
{
    public SlimDX.Direct3D11.Device GraphicsDevice;
    public SwapChain SwapChain;
    public RenderTargetView RenderTarget;


    public void Run()
    {
        initDevice();
        SlimDX.Windows.MessagePump.Run(this, Draw);
        disposeDevice();
    }

    private void initDevice()
    {
        MyDirectXHelper.CreateDeviceAndSwapChain(
            this, out GraphicsDevice, out SwapChain
            );

        initRenderTarget();
        initViewport();

        LoadContent();
    }

    private void initRenderTarget()
    {
        using (Texture2D backBuffer
            = SlimDX.Direct3D11.Resource.FromSwapChain<Texture2D>(SwapChain, 0)
            )
        {
            RenderTarget = new RenderTargetView(GraphicsDevice, backBuffer);
            GraphicsDevice.ImmediateContext.OutputMerger.SetTargets(RenderTarget);
        }
    }

    private void initViewport()
    {
        GraphicsDevice.ImmediateContext.Rasterizer.SetViewports(
            new Viewport
            {
                Width = ClientSize.Width,
                Height = ClientSize.Height,
            }
            );
    }

    private void disposeDevice()
    {
        UnloadContent();
        RenderTarget.Dispose();
        GraphicsDevice.Dispose();
        SwapChain.Dispose();
    }

    protected virtual void Draw() { }
    protected virtual void LoadContent() { }
    protected virtual void UnloadContent() { }
}

class MyDirectXHelper
{
    public static void CreateDeviceAndSwapChain(
        System.Windows.Forms.Form form,
        out SlimDX.Direct3D11.Device device,
        out SlimDX.DXGI.SwapChain swapChain
        )
    {
        SlimDX.Direct3D11.Device.CreateWithSwapChain(
            DriverType.Hardware,
            DeviceCreationFlags.None,
            new SwapChainDescription
            {
                BufferCount = 1,
                OutputHandle = form.Handle,
                IsWindowed = true,
                SampleDescription = new SampleDescription
                {
                    Count = 1,
                    Quality = 0
                },
                ModeDescription = new ModeDescription
                {
                    Width = form.ClientSize.Width,
                    Height = form.ClientSize.Height,
                    RefreshRate = new SlimDX.Rational(60, 1),
                    Format = Format.R8G8B8A8_UNorm
                },
                Usage = Usage.RenderTargetOutput
            },
            out device,
            out swapChain
            );
    }

    public static Buffer CreateVertexBuffer(
        SlimDX.Direct3D11.Device graphicsDevice,
        System.Array vertices
        )
    {
        using (SlimDX.DataStream vertexStream 
            = new SlimDX.DataStream(vertices, true, true))
        {
            return new Buffer(
                graphicsDevice,
                vertexStream,
                new BufferDescription
                {
                    SizeInBytes= (int)vertexStream.Length,
                    BindFlags = BindFlags.VertexBuffer,
                }
                );
        }
    }
}

myEffect.fx
Texture2D diffuseTexture;

SamplerState mySampler
{
};

struct VertexPositionTexture
{
float4 Position : SV_Position;
float2 TextureCoordinate : TEXCOORD;
};

VertexPositionTexture MyVertexShader(VertexPositionTexture input)
{
return input;
}

float4 MyPixelShader(VertexPositionTexture input) : SV_Target
{
    return diffuseTexture.Sample(mySampler, input.TextureCoordinate);
}


technique10 MyTechnique
{
pass MyPass
{
SetVertexShader( CompileShader( vs_5_0, MyVertexShader() ) );
SetPixelShader( CompileShader( ps_5_0, MyPixelShader() ) );
}
}

実行結果はこうなります。
Tutorial11TextureMappedTriangle.jpg

このプログラムは、まず三角形にテクスチャ座標を割り当てます。

Tutorial11HowTextureCoordinatesAreSet.jpg

左上が(0, 0)、右下が(1, 1)な座標です。
これにピクセルシェーダーの中で、テクスチャを貼り付けるのです。

Tutorial11TextureCoordinate.jpg

この三角形のテクスチャ座標の指定のしかたは横に眺めになっているので、少し縦に潰れてテクスチャが貼られるのです。

拍手[0回]

PR

C#でDirectX11 SlimDXチュートリアルその10 ライト

 今回は、三角形を光で照らします。

Tutorial10Lighting01.jpg
Tutorial10Lighting02.jpg
Tutorial10Lighting03.jpg
Tutorial10Lighting04.jpg

以前とは違い、今度は本当に三角形を回転させています。
以前のようにカメラのほうを回転させ、三角形は静止状態なのでは、光の移り変わりがわかりませんからね。

Tutorial10HowTheLightIsThrown.jpg


回転マトリクス

3DCGで物をY軸方向に回転させるにはMatrix.RotationY()メソッドによって作られたマトリクスを利用します。

public static Matrix RotationY(float angle);

angleは回転角度です。単位はラジアンです。
戻り値はY軸回転を司るマトリクスです。



光の強さを計算

三角形に光を当てて、どのくらいの明るさになるかはHLSLで自分でプログラムしなければいけません。
と言っても実際には大して難しくなくて、明るさはdot()関数で計算できます。
通称、内積です。

float dot(float3 x, float3 y);

この関数は2つのベクトルが同じ向きであるほど大きく、向きが食い違うほど小さくなります。

howTheDotProductWorks.jpg

そしてまさにこの関数の戻り値こそ、光のあたたったものの明るさを計算するのに必要なものなのです。
物の明るさは、その法線ベクトル(面と垂直なベクトル)の方向と、光の方向ベクトルのなす角度で決まるからです。

relationshipBetweenLightAndAngle.jpg

-dot(法線ベクトル, 光の方向)で面の明るさが計算できます。
上の図では左が暗く、右が明るくなります。

なお、法線ベクトルは頂点座標の中に含めることになります。
そのフィールドに付けるセマンティクスはNORMALです。


コード


Program.cs
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using SlimDX.D3DCompiler;

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

class MyGame : Game
{
    Effect effect;
    InputLayout vertexLayout;
    Buffer vertexBuffer;

    protected override void Draw()
    {
        GraphicsDevice.ImmediateContext.ClearRenderTargetView(
            RenderTarget,
            new SlimDX.Color4(1, 0.39f, 0.58f, 0.93f)
            );

        updateCamera();
        initTriangleInputAssembler();
        initLight();
        drawRotatingTriangle();

        SwapChain.Present(0, PresentFlags.None);
    }


    private void updateCamera()
    {
        Matrix view = Matrix.LookAtRH(
            new Vector3(0, 0, 1),
            new Vector3(),
            new Vector3(0, 1, 0)
            );
        Matrix projection = Matrix.PerspectiveFovRH(
            (float)System.Math.PI / 2,
            ClientSize.Width / ClientSize.Height,
            0.1f, 1000
            );
        effect.GetVariableByName("ViewProjection")
            .AsMatrix().SetMatrix(view * projection);

    }

    private void initTriangleInputAssembler()
    {
        GraphicsDevice.ImmediateContext.InputAssembler.InputLayout = vertexLayout;
        GraphicsDevice.ImmediateContext.InputAssembler.SetVertexBuffers(
            0,
            new VertexBufferBinding(vertexBuffer, VertexPositionNormal.SizeInBytes, 0)
            );
        GraphicsDevice.ImmediateContext.InputAssembler.PrimitiveTopology
            = PrimitiveTopology.TriangleList;
    }

    private void initLight()
    {
        effect.GetVariableByName("LightDirection")
            .AsVector().Set(Vector3.Normalize(new Vector3(1, 0, -1)));
    }

    private void drawRotatingTriangle()
    {
        double time = System.Environment.TickCount / 500d;
        effect.GetVariableByName("World")
            .AsMatrix().SetMatrix(Matrix.RotationY((float)time));
        drawTriangle();
    }

    private void drawTriangle()
    {
        effect.GetTechniqueByIndex(0).GetPassByIndex(0).Apply(GraphicsDevice.ImmediateContext);
        GraphicsDevice.ImmediateContext.Draw(3, 0);
    }


    protected override void LoadContent()
    {
        initEffect();
        initVertexLayout();
        initVertexBuffer();
    }


    private void initEffect()
    {
        using (ShaderBytecode shaderBytecode = ShaderBytecode.CompileFromFile(
            "myEffect.fx", "fx_5_0",
            ShaderFlags.None,
            EffectFlags.None
            ))
        {
            effect = new Effect(GraphicsDevice, shaderBytecode);
        }
    }

    private void initVertexLayout()
    {
        vertexLayout = new InputLayout(
            GraphicsDevice,
            effect.GetTechniqueByIndex(0).GetPassByIndex(0).Description.Signature,
            VertexPositionNormal.VertexElements
            );
    }

    private void initVertexBuffer()
    {
        vertexBuffer = MyDirectXHelper.CreateVertexBuffer(
            GraphicsDevice,
            new[] {
                new VertexPositionNormal
                {
                    Position = new Vector3(0, 0.5f, 0),
                    Normal = new Vector3(0, 0, 1) 
                },
                new VertexPositionNormal
                {
                    Position = new Vector3(0.5f, 0, 0),
                    Normal = new Vector3(0, 0, 1)
                },
                new VertexPositionNormal
                {
                    Position = new Vector3(-0.5f, 0, 0),
                    Normal = new Vector3(0, 0, 1) 
                },
            });
    }

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

struct VertexPositionNormal
{
    public Vector3 Position;
    public Vector3 Normal;

    public static readonly InputElement[] VertexElements = new[]
    {
        new InputElement
        {
            SemanticName = "SV_Position",
            Format = Format.R32G32B32_Float
        },
        new InputElement
        {
            SemanticName = "NORMAL",
            Format = Format.R32G32B32_Float,
            AlignedByteOffset = InputElement.AppendAligned//自動的にオフセット決定
        }
    };

    public static int SizeInBytes
    {
        get
        {
            return System.Runtime.InteropServices.
                Marshal.SizeOf(typeof(VertexPositionNormal));
        }
    }
}

class Game : System.Windows.Forms.Form
{
    public SlimDX.Direct3D11.Device GraphicsDevice;
    public SwapChain SwapChain;
    public RenderTargetView RenderTarget;


    public void Run()
    {
        initDevice();
        SlimDX.Windows.MessagePump.Run(this, Draw);
        disposeDevice();
    }

    private void initDevice()
    {
        MyDirectXHelper.CreateDeviceAndSwapChain(
            this, out GraphicsDevice, out SwapChain
            );

        initRenderTarget();
        initViewport();

        LoadContent();
    }

    private void initRenderTarget()
    {
        using (Texture2D backBuffer
            = SlimDX.Direct3D11.Resource.FromSwapChain<Texture2D>(SwapChain, 0)
            )
        {
            RenderTarget = new RenderTargetView(GraphicsDevice, backBuffer);
            GraphicsDevice.ImmediateContext.OutputMerger.SetTargets(RenderTarget);
        }
    }

    private void initViewport()
    {
        GraphicsDevice.ImmediateContext.Rasterizer.SetViewports(
            new Viewport
            {
                Width = ClientSize.Width,
                Height = ClientSize.Height,
            }
            );
    }

    private void disposeDevice()
    {
        UnloadContent();
        RenderTarget.Dispose();
        GraphicsDevice.Dispose();
        SwapChain.Dispose();
    }

    protected virtual void Draw() { }
    protected virtual void LoadContent() { }
    protected virtual void UnloadContent() { }
}

class MyDirectXHelper
{
    public static void CreateDeviceAndSwapChain(
        System.Windows.Forms.Form form,
        out SlimDX.Direct3D11.Device device,
        out SlimDX.DXGI.SwapChain swapChain
        )
    {
        SlimDX.Direct3D11.Device.CreateWithSwapChain(
            DriverType.Hardware,
            DeviceCreationFlags.None,
            new SwapChainDescription
            {
                BufferCount = 1,
                OutputHandle = form.Handle,
                IsWindowed = true,
                SampleDescription = new SampleDescription
                {
                    Count = 1,
                    Quality = 0
                },
                ModeDescription = new ModeDescription
                {
                    Width = form.ClientSize.Width,
                    Height = form.ClientSize.Height,
                    RefreshRate = new SlimDX.Rational(60, 1),
                    Format = Format.R8G8B8A8_UNorm
                },
                Usage = Usage.RenderTargetOutput
            },
            out device,
            out swapChain
            );
    }

    public static Buffer CreateVertexBuffer(
        SlimDX.Direct3D11.Device graphicsDevice,
        System.Array vertices
        )
    {
        using (SlimDX.DataStream vertexStream 
            = new SlimDX.DataStream(vertices, true, true))
        {
            return new Buffer(
                graphicsDevice,
                vertexStream,
                new BufferDescription
                {
                    SizeInBytes= (int)vertexStream.Length,
                    BindFlags = BindFlags.VertexBuffer,
                }
                );
        }
    }
}

myEffect.fx
matrix World;
matrix ViewProjection;

float3 LightDirection;

struct VertexPositionNormal
{
float4 Position : SV_Position;
float4 Normal : NORMAL;
};

VertexPositionNormal MyVertexShader(VertexPositionNormal input)
{
    VertexPositionNormal output = input;
output.Position = mul(output.Position, World);
    output.Position = mul(output.Position, ViewProjection);
output.Normal = mul(output.Normal, World);
    return output;
}

float4 MyPixelShader(VertexPositionNormal input) : SV_Target
{
return -dot(LightDirection, input.Normal);
}


technique10 MyTechnique
{
pass MyPass
{
SetVertexShader( CompileShader( vs_5_0, MyVertexShader() ) );
SetPixelShader( CompileShader( ps_5_0, MyPixelShader() ) );
}
}




このプログラムは斜め手前から光のあたった回転する三角形を表示します。
光が垂直にあたったときは一番明るく、光が当たらなくなると暗くなります。
 
三角形の表面と光の方向を元に、三角形の明るさを計算しているのです。



















拍手[0回]


C#でDirectX11 SlimDXチュートリアルその09 深度バッファ

 前回のプログラムはとんだ欠陥プログラムでした。
近くのものが遠くのものの影に隠れて見えないのです。

withoutDepthBuffer.jpg

ここに表示されている2つの三角形は、元は同じ大きさの三角形でした。
右の三角形は遠くにあり、左の三角形は近くにあるので、大きさが違ってみえるのです。
普通このような状況では、右の三角形が左の三角形に隠れます。
近くにあるものが遠くのものを隠す。
当然です。

しかしここでは逆になっています。
遠くのもののせいで近くのものが見れなくなっているのです。
これはおかしいですね。

なぜこうなったのかというと、深度バッファというものを使っていないからです。
深度バッファを使わなければ、物体は単に描かれた順番によって前後関係が決まります。
後から描いたものが、すでに描かれていたものを隠すのです。
ふつうの2Dの描画と同じです。
だから上の図のようなおかしなことになっていたのですね。

今回は、深度バッファを使ってこの問題を解決します。


深度バッファ

深度バッファは、画面に存在するピクセルごとにfloat値がひとつ存在していて、このピクセルに描くべきかそうでないかを判定してくれます。
もしそのピクセルに、すでに手前の物が描かれている場合、そこには何も描きません。
逆にそのピクセルにまだ、これから描こうとしているピクセルよりも奥にしか物が描かれていないのなら、ピクセルを描き、深度バッファのfloatを更新します。
この仕組によって物の前後関係を正しく描くのです。

深度バッファを扱うには、SlimDX.Direct3D11.DepthStencilViewクラスを使います。

public class DepthStencilView : ResourceView

以前説明したとおり、~~Viewというのはリソースの使われ方を表すクラスです。
リソース自体は別に作る必要があります。
そしてそのリソースをコンストラクタ引数に取るのです。

public DepthStencilView(Device device, Resource resource);

deviceはViewをつくるデバイス。
resourceは作成するViewがその役割を指定するリソースです。このリソースは深度ステンシルバッファとして扱われます。(ここではステンシルという言葉は無視してください。ステンシルもピクセルごとに存在し、そのピクセルに描画するか否かを判定するのですが、今回は使いません)

resourceにはTexture2Dのインスタンスを使いましょう。
Texture2Dのインスタンスはここではコンストラクタから得ます。

public Texture2D(Device device, Texture2DDescription description);

deviceはテクスチャを作るデバイス。
descriptionはTexture2Dの設定を表すオブジェクトで、このメンバが真のコンストラクタ引数です。

SlimDX.Direct3D11.Texture2DDescription構造体には10個の設定可能なプロパティが存在します
このテクスチャのサイズはウィンドウのクライアント領域と同じで、BindFlagsにはBindFlags.DepthStencilを設定します。

DepthStencilViewをデバイスにセットするには、OutputMergerWrapper.SetTargetsメソッドを使います。
これはレンダーターゲットと同時にセットします。

public void SetTargets(DepthStencilView depthStencilView, RenderTargetView renderTargetView);

depthStencilViewはセットする深度ステンシルのビューです。
renderTargetViewはセットするレンダーターゲットのビューです。

今回は深度バッファを使うので、Viewport.MaxZプロパティを設定する必要があります。
これは普通1です。


コード

Program.cs
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using SlimDX.D3DCompiler;

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

class MyGame : Game
{
    Effect effect;
    InputLayout vertexLayout;
    Buffer vertexBuffer;

    protected override void Draw()
    {
        GraphicsDevice.ImmediateContext.ClearRenderTargetView(
            RenderTarget,
            new SlimDX.Color4(1, 0.39f, 0.58f, 0.93f)
            );
        GraphicsDevice.ImmediateContext.ClearDepthStencilView(
            DepthStencil,
            DepthStencilClearFlags.Depth,
            1,
            0
            );

        updateCamera();
        initTriangleInputAssembler();
        drawMovingTriangles();

        SwapChain.Present(0, PresentFlags.None);
    }

    private void updateCamera()
    {
        Matrix view = Matrix.LookAtRH(
            new Vector3(0, 0, 1),
            new Vector3(),
            new Vector3(0, 1, 0)
            );
        Matrix projection = Matrix.PerspectiveFovRH(
            (float)System.Math.PI / 2,
            ClientSize.Width / ClientSize.Height,
            0.1f, 1000
            );
        effect.GetVariableByName("ViewProjection")
            .AsMatrix().SetMatrix(view * projection);

    }

    private void initTriangleInputAssembler()
    {
        GraphicsDevice.ImmediateContext.InputAssembler.InputLayout = vertexLayout;
        GraphicsDevice.ImmediateContext.InputAssembler.SetVertexBuffers(
            0,
            new VertexBufferBinding(vertexBuffer, VertexPositionColor.SizeInBytes, 0)
            );
        GraphicsDevice.ImmediateContext.InputAssembler.PrimitiveTopology
            = PrimitiveTopology.TriangleList;
    }

    private void drawMovingTriangles()
    {
        double time = System.Environment.TickCount / 500d;

        effect.GetVariableByName("World").AsMatrix().SetMatrix(
            Matrix.Translation(
            (float)System.Math.Cos(time), 0, -1 + (float)System.Math.Sin(time)
            ));
        drawTriangle();

        effect.GetVariableByName("World").AsMatrix().SetMatrix(
            Matrix.Translation(
            -(float)System.Math.Cos(time), 0, -1 + -(float)System.Math.Sin(time)
            ));
        drawTriangle();
    }

    private void drawTriangle()
    {
        effect.GetTechniqueByIndex(0).GetPassByIndex(0).Apply(GraphicsDevice.ImmediateContext);
        GraphicsDevice.ImmediateContext.Draw(3, 0);
    }

    protected override void LoadContent()
    {
        initEffect();
        initVertexLayout();
        initVertexBuffer();
    }

    private void initEffect()
    {
        using (ShaderBytecode shaderBytecode = ShaderBytecode.CompileFromFile(
            "myEffect.fx", "fx_5_0",
            ShaderFlags.None,
            EffectFlags.None
            ))
        {
            effect = new Effect(GraphicsDevice, shaderBytecode);
        }
    }

    private void initVertexLayout()
    {
        vertexLayout = new InputLayout(
            GraphicsDevice,
            effect.GetTechniqueByIndex(0).GetPassByIndex(0).Description.Signature,
            VertexPositionColor.VertexElements
            );
    }

    private void initVertexBuffer()
    {
        vertexBuffer = MyDirectXHelper.CreateVertexBuffer(
            GraphicsDevice,
            new[] {
                new VertexPositionColor
                {
                    Position = new Vector3(0, 0.5f, 0),
                    Color = new Vector3(1, 1, 1) 
                },
                new VertexPositionColor
                {
                    Position = new Vector3(0.5f, 0, 0),
                    Color = new Vector3(0, 0, 1)
                },
                new VertexPositionColor
                {
                    Position = new Vector3(-0.5f, 0, 0),
                    Color = new Vector3(1, 0, 0)
                },
            });
    }

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

struct VertexPositionColor
{
    public Vector3 Position;
    public Vector3 Color;

    public static readonly InputElement[] VertexElements = new[]
    {
        new InputElement
        {
            SemanticName = "SV_Position",
            Format = Format.R32G32B32_Float
        },
        new InputElement
        {
            SemanticName = "COLOR",
            Format = Format.R32G32B32_Float,
            AlignedByteOffset = InputElement.AppendAligned//自動的にオフセット決定
        }
    };

    public static int SizeInBytes
    {
        get
        {
            return System.Runtime.InteropServices.
                Marshal.SizeOf(typeof(VertexPositionColor));
        }
    }
}

class Game : System.Windows.Forms.Form
{
    public SlimDX.Direct3D11.Device GraphicsDevice;
    public SwapChain SwapChain;
    public RenderTargetView RenderTarget;
    public DepthStencilView DepthStencil;

    public void Run()
    {
        initDevice();
        SlimDX.Windows.MessagePump.Run(this, Draw);
        disposeDevice();
    }

    private void initDevice()
    {
        MyDirectXHelper.CreateDeviceAndSwapChain(
            this, out GraphicsDevice, out SwapChain
            );

        initRenderTarget();
        initDepthStencil();
        GraphicsDevice.ImmediateContext.
            OutputMerger.SetTargets(DepthStencil, RenderTarget);
        initViewport();

        LoadContent();
    }

    private void initRenderTarget()
    {
        using (Texture2D backBuffer
            = SlimDX.Direct3D11.Resource.FromSwapChain<Texture2D>(SwapChain, 0)
            )
        {
            RenderTarget = new RenderTargetView(GraphicsDevice, backBuffer);
        }
    }

    private void initDepthStencil()
    {
        Texture2DDescription depthBufferDesc = new Texture2DDescription
        {
            ArraySize = 1,
            BindFlags = BindFlags.DepthStencil,
            Format = Format.D32_Float,
            Width = ClientSize.Width,
            Height = ClientSize.Height,
            MipLevels = 1,
            SampleDescription = new SampleDescription(1, 0)
        };

        using (Texture2D depthBuffer = new Texture2D(GraphicsDevice, depthBufferDesc))
        {
            DepthStencil = new DepthStencilView(GraphicsDevice, depthBuffer);
        }
    }

    private void initViewport()
    {
        GraphicsDevice.ImmediateContext.Rasterizer.SetViewports(
            new Viewport
            {
                Width = ClientSize.Width,
                Height = ClientSize.Height,
                MaxZ = 1
            }
            );
    }

    private void disposeDevice()
    {
        UnloadContent();
        DepthStencil.Dispose();
        RenderTarget.Dispose();
        GraphicsDevice.Dispose();
        SwapChain.Dispose();
    }

    protected virtual void Draw() { }
    protected virtual void LoadContent() { }
    protected virtual void UnloadContent() { }
}

class MyDirectXHelper
{
    public static void CreateDeviceAndSwapChain(
        System.Windows.Forms.Form form,
        out SlimDX.Direct3D11.Device device,
        out SlimDX.DXGI.SwapChain swapChain
        )
    {
        SlimDX.Direct3D11.Device.CreateWithSwapChain(
            DriverType.Hardware,
            DeviceCreationFlags.None,
            new SwapChainDescription
            {
                BufferCount = 1,
                OutputHandle = form.Handle,
                IsWindowed = true,
                SampleDescription = new SampleDescription
                {
                    Count = 1,
                    Quality = 0
                },
                ModeDescription = new ModeDescription
                {
                    Width = form.ClientSize.Width,
                    Height = form.ClientSize.Height,
                    RefreshRate = new SlimDX.Rational(60, 1),
                    Format = Format.R8G8B8A8_UNorm
                },
                Usage = Usage.RenderTargetOutput
            },
            out device,
            out swapChain
            );
    }

    public static Buffer CreateVertexBuffer(
        SlimDX.Direct3D11.Device graphicsDevice,
        System.Array vertices
        )
    {
        using (SlimDX.DataStream vertexStream 
            = new SlimDX.DataStream(vertices, true, true))
        {
            return new Buffer(
                graphicsDevice,
                vertexStream,
                new BufferDescription
                {
                    SizeInBytes= (int)vertexStream.Length,
                    BindFlags = BindFlags.VertexBuffer,
                }
                );
        }
    }
}

myEffect.fx
matrix World;
matrix ViewProjection;
 
struct VertexPositionColor
{
float4 Position : SV_Position;
float4 Color : COLOR;
};
 
VertexPositionColor MyVertexShader(VertexPositionColor input)
{
    VertexPositionColor output = input;
output.Position = mul(output.Position, World);
    output.Position = mul(output.Position, ViewProjection);
    return output;
}
 
float4 MyPixelShader(VertexPositionColor input) : SV_Target
{
    return input.Color;
}
 
 
technique10 MyTechnique
{
pass MyPass
{
SetVertexShader( CompileShader( vs_5_0, MyVertexShader() ) );
SetPixelShader( CompileShader( ps_5_0, MyPixelShader() ) );
}
}

このプログラムは前回のプログラムに加え、深度ステンシルバッファをデバイスにセットしています。
前回は2つの連星のような三角形を描いていたのですが、前後関係正しく描画されない時がありました。
今回は深度バッファのおかげでそのようなことはありません。
描かれる三角形の前後関係が常に正しくなります。
冒頭のようなおかしな事にはならなくなるのです。






拍手[0回]


C#でDirectX11 SlimDXチュートリアルその08 平行移動

 今回はお互いの周りをくるくる回る2つの三角形です。

Tutorial08HowTheTrianglesRotate.jpg

Tutorial08RotatingTriangles01.jpg


平行移動

今回は、三角形を中心から平行移動します。

Tutorial08RotatingTrianglesNeedTranslation.jpg

「回転させたいのか平行移動させたいのかどっちなんだ?」
いえいえこの2つは相反するものではありません。
平行移動の方向を少しずつ変えていくことによって、回転させるのです。
平行移動する変形は、Matrix.Translationメソッドによって作ります。

public static Matrix Translation(float x, float y, float z);

x, y, zはそれぞれの軸の並行移動距離です。


コード

Program.cs
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using SlimDX.D3DCompiler;

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

class MyGame : Game
{
    Effect effect;
    InputLayout vertexLayout;
    Buffer vertexBuffer;

    protected override void Draw()
    {
        GraphicsDevice.ImmediateContext.ClearRenderTargetView(
            RenderTarget,
            new SlimDX.Color4(1, 0.39f, 0.58f, 0.93f)
            );

        updateCamera();
        initTriangleInputAssembler();
        drawMovingTriangles();

        SwapChain.Present(0, PresentFlags.None);
    }

    private void updateCamera()
    {
        Matrix view = Matrix.LookAtRH(
            new Vector3(0, 0, 1),
            new Vector3(),
            new Vector3(0, 1, 0)
            );
        Matrix projection = Matrix.PerspectiveFovRH(
            (float)System.Math.PI / 2,
            ClientSize.Width / ClientSize.Height,
            0.1f, 1000
            );
        effect.GetVariableByName("ViewProjection")
            .AsMatrix().SetMatrix(view * projection);

    }

    private void initTriangleInputAssembler()
    {
        GraphicsDevice.ImmediateContext.InputAssembler.InputLayout = vertexLayout;
        GraphicsDevice.ImmediateContext.InputAssembler.SetVertexBuffers(
            0,
            new VertexBufferBinding(vertexBuffer, VertexPositionColor.SizeInBytes, 0)
            );
        GraphicsDevice.ImmediateContext.InputAssembler.PrimitiveTopology
            = PrimitiveTopology.TriangleList;
    }

    private void drawMovingTriangles()
    {
        double time = System.Environment.TickCount / 500d;

        effect.GetVariableByName("World").AsMatrix().SetMatrix(
            Matrix.Translation(
            (float)System.Math.Cos(time), 0, -1 + (float)System.Math.Sin(time)
            ));
        drawTriangle();

        effect.GetVariableByName("World").AsMatrix().SetMatrix(
            Matrix.Translation(
            -(float)System.Math.Cos(time), 0, -1 + -(float)System.Math.Sin(time)
            ));
        drawTriangle();
    }

    private void drawTriangle()
    {
        effect.GetTechniqueByIndex(0).GetPassByIndex(0).Apply(GraphicsDevice.ImmediateContext);
        GraphicsDevice.ImmediateContext.Draw(3, 0);
    }


    protected override void LoadContent()
    {
        initEffect();
        initVertexLayout();
        initVertexBuffer();
    }

    private void initEffect()
    {
        using (ShaderBytecode shaderBytecode = ShaderBytecode.CompileFromFile(
            "myEffect.fx", "fx_5_0",
            ShaderFlags.None,
            EffectFlags.None
            ))
        {
            effect = new Effect(GraphicsDevice, shaderBytecode);
        }
    }

    private void initVertexLayout()
    {
        vertexLayout = new InputLayout(
            GraphicsDevice,
            effect.GetTechniqueByIndex(0).GetPassByIndex(0).Description.Signature,
            VertexPositionColor.VertexElements
            );
    }

    private void initVertexBuffer()
    {
        vertexBuffer = MyDirectXHelper.CreateVertexBuffer(
            GraphicsDevice,
            new[] {
                new VertexPositionColor
                {
                    Position = new Vector3(0, 0.5f, 0),
                    Color = new Vector3(1, 1, 1) 
                },
                new VertexPositionColor
                {
                    Position = new Vector3(0.5f, 0, 0),
                    Color = new Vector3(0, 0, 1)
                },
                new VertexPositionColor
                {
                    Position = new Vector3(-0.5f, 0, 0),
                    Color = new Vector3(1, 0, 0)
                },
            });
    }

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

struct VertexPositionColor
{
    public Vector3 Position;
    public Vector3 Color;

    public static readonly InputElement[] VertexElements = new[]
    {
        new InputElement
        {
            SemanticName = "SV_Position",
            Format = Format.R32G32B32_Float
        },
        new InputElement
        {
            SemanticName = "COLOR",
            Format = Format.R32G32B32_Float,
            AlignedByteOffset = InputElement.AppendAligned//自動的にオフセット決定
        }
    };

    public static int SizeInBytes
    {
        get
        {
            return System.Runtime.InteropServices.
                Marshal.SizeOf(typeof(VertexPositionColor));
        }
    }
}

class Game : System.Windows.Forms.Form
{
    public SlimDX.Direct3D11.Device GraphicsDevice;
    public SwapChain SwapChain;
    public RenderTargetView RenderTarget;


    public void Run()
    {
        initDevice();
        SlimDX.Windows.MessagePump.Run(this, Draw);
        disposeDevice();
    }

    private void initDevice()
    {
        MyDirectXHelper.CreateDeviceAndSwapChain(
            this, out GraphicsDevice, out SwapChain
            );

        initRenderTarget();
        initViewport();

        LoadContent();
    }

    private void initRenderTarget()
    {
        using (Texture2D backBuffer
            = SlimDX.Direct3D11.Resource.FromSwapChain<Texture2D>(SwapChain, 0)
            )
        {
            RenderTarget = new RenderTargetView(GraphicsDevice, backBuffer);
            GraphicsDevice.ImmediateContext.OutputMerger.SetTargets(RenderTarget);
        }
    }

    private void initViewport()
    {
        GraphicsDevice.ImmediateContext.Rasterizer.SetViewports(
            new Viewport
            {
                Width = ClientSize.Width,
                Height = ClientSize.Height,
            }
            );
    }

    private void disposeDevice()
    {
        UnloadContent();
        RenderTarget.Dispose();
        GraphicsDevice.Dispose();
        SwapChain.Dispose();
    }

    protected virtual void Draw() { }
    protected virtual void LoadContent() { }
    protected virtual void UnloadContent() { }
}

class MyDirectXHelper
{
    public static void CreateDeviceAndSwapChain(
        System.Windows.Forms.Form form,
        out SlimDX.Direct3D11.Device device,
        out SlimDX.DXGI.SwapChain swapChain
        )
    {
        SlimDX.Direct3D11.Device.CreateWithSwapChain(
            DriverType.Hardware,
            DeviceCreationFlags.None,
            new SwapChainDescription
            {
                BufferCount = 1,
                OutputHandle = form.Handle,
                IsWindowed = true,
                SampleDescription = new SampleDescription
                {
                    Count = 1,
                    Quality = 0
                },
                ModeDescription = new ModeDescription
                {
                    Width = form.ClientSize.Width,
                    Height = form.ClientSize.Height,
                    RefreshRate = new SlimDX.Rational(60, 1),
                    Format = Format.R8G8B8A8_UNorm
                },
                Usage = Usage.RenderTargetOutput
            },
            out device,
            out swapChain
            );
    }

    public static Buffer CreateVertexBuffer(
        SlimDX.Direct3D11.Device graphicsDevice,
        System.Array vertices
        )
    {
        using (SlimDX.DataStream vertexStream 
            = new SlimDX.DataStream(vertices, true, true))
        {
            return new Buffer(
                graphicsDevice,
                vertexStream,
                new BufferDescription
                {
                    SizeInBytes= (int)vertexStream.Length,
                    BindFlags = BindFlags.VertexBuffer,
                }
                );
        }
    }
}

myEffect.fx
matrix World;
matrix ViewProjection;

struct VertexPositionColor
{
float4 Position : SV_Position;
float4 Color : COLOR;
};

VertexPositionColor MyVertexShader(VertexPositionColor input)
{
    VertexPositionColor output = input;
output.Position = mul(output.Position, World);
    output.Position = mul(output.Position, ViewProjection);
    return output;
}

float4 MyPixelShader(VertexPositionColor input) : SV_Target
{
    return input.Color;
}


technique10 MyTechnique
{
pass MyPass
{
SetVertexShader( CompileShader( vs_5_0, MyVertexShader() ) );
SetPixelShader( CompileShader( ps_5_0, MyPixelShader() ) );
}
}

このプログラムは2つの三角形を描いています。 描く上で、それぞれ反対方向に平行移動しています。
平行移動の変換マトリクスはWorldという名前の変数に格納されています。
まず平行移動をし、カメラから見たらどう映るかという変換を行っているわけですね。

Worldの値は時間と共にすこしずつ変わっているので、2つの三角形はお互いの周りを回ります。

Tutorial08RotatingTriangles01.jpg

前後関係がおかしい!!

さて、このプログラムにはひとつおかしなところがあります。
それは、遠くのものに近くのものが隠れることがあるということです。

withoutDepthBuffer.jpg

この2つの三角形のもとの大きさは同じです。
違うのは、カメラからの距離で、左の三角形は遠くに、右の三角形は近くにあります。

明らかにおかしいですね。
本来、遠くのものは近くのものに隠れます。
逆ではありません。

こんなおかしな結果になってしまった理由は、
現在このサンプルは単に描画する順番で隠す隠さないを決めているからです。
後から描かれた三角形が、その前に描かれた三角形を隠します。

この解決には深度バッファが必要です。
これは次回にしましょう。


拍手[0回]


C#でDirectX11 SlimDXチュートリアルその07 カメラ(遠近感+回転)

 このチュートリアルは3DCGを想定しておきながら、前回までは2Dと大差ありませんでした。
話を簡単にするために三角形をそのままの大きさで描いていたのです。
これではいけません。
近くにあるものは大きく、遠くにあるものは小さく表示されるのが自然です。

今回はそのように遠近感を出してみましょう。

Tutorial07RotatingTriangle01.jpg

Tutorial07RotatingTriangle01WithDescription.jpg

遠くにあるものは小さく、近くにあるものは大きく。

あとついでに回転させてみましょう。

972835a4.jpg


マトリクス

今回やることは、頂点データを変更するという意味で前回と同じです。
前回はfloatの変数を用意して、その値の分だけX軸方向に三角形を平行移動しました。

今回が前回と違うのは、頂点データの変換が複雑だということです。
今回は回転と遠近法するデータの変換です。
「こうするとたくさん変数が必要そうだし、計算もなんだかめんどくさくなりそうだ」と思われるかもしれません。

しかし実際は簡単です。
変数は1つでいいですし、ベクトルの変換も1行で住みます。
マトリクスという、ベクトルデータの変換方法を示した変数を使うのです。
マトリクスはC#側では構造体で、16個のフィールドを持ち、回転だとか平行移動だとか遠近法だとかいった変換を全て司ります。
おもしろいのは、「回転してから平行移動して遠近法を適用したい」という複数の変換も1つのマトリクスだけですんでしまうということです。
16個のフィールドがその変換を全部表現してくれるのです。
ですから、今回HLSL側に必要な変数はmatrix型の変数1つだけです。
変数の数は前回から増えません。
マトリクス様様です。



マトリクスの生成

マトリクスはベクトルの変換方法を表すデータです。
C#ではSlimDX.Matrix構造体を使います。

public struct Matrix : IEquatable<Matrix>

一口にベクトルの変換と言っても様々です。
回転、平行移動、遠近法…
それぞれに、対応するマトリクスを生成するためのファクトリメソッドが付いています。

今回使うのは2つです。
物体をある方向から見たときに、物体の相対的な座標がどう見えるかは、Matrix.LookAtRHメソッドを使います。 このメソッドはカメラから見たものの見え方にするようなマトリックスを作り出します。

public static Matrix LookAtRH(Vector3 eye, Vector3 target, Vector3 up);

eyeはカメラの位置を表します。
targetはカメラの向いている対象の位置です。target - eyeで、カメラの向いている方向になります。
upはカメラの上方向です。これは普通y方向、new Vector3(0, 1, 0)です。これをnew Vector3(0, -1, 0)にすると画面は上下ひっくり返ります。


プレイヤーが迷路の中をすすんでいくとき、引数eyeを変えていくことになるでしょう。
プレイヤーがその場であたりを見まわし回転したら引数targetの値が変わります。

遠近法を司るマトリクスはMatrix.PerspectiveFovRHメソッドによって作り出します。

public static Matrix PerspectiveFovRH(float fov, float aspect, float znear, float zfar);
CreatePerspectiveFieldOfView.jpg

fovはfieldOfViewの略です。縦の視野角を表します。
aspectはアスペクト比です。つまり横幅 / 縦幅です。
znearは画面のz座標。
zfarは最も遠い地点のz座標です。


こうして作ったマトリクスは、*演算子で合成することができます。
A * Bは「ベクトルをAで変換してからBで変換せよ」というマトリクスを新たに作り出します。
こうして合成しても必要なメモリは全く変わらないというのだから驚きです。

public static Matrix operator *(Matrix left, Matrix right);

leftははじめにベクトルを変換するマトリクス。
rightは次にベクトルを変換するマトリクスです。

マトリクスのセット

C#側からHLSLの変数にマトリクスをセットするには、EffectMatrixVariable.SetMatrix()メソッドを使います。

public Result SetMatrix(Matrix matrix);

matrixはセットするマトリクスです。



マトリクスでベクトルを変換する(HLSL)

今回、HLSL側のコードにはマトリクス(matrix)型の変数がひとつあります。
この変数を使って、頂点位置情報を変換します。
変換にはmul関数使います
float4 mul(float4 position, matrix transform)

positionは変換前の位置ベクトル。
transformはベクトルの変換方法を表すマトリクスです。



コード

Program.cs

using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using SlimDX.D3DCompiler;
 
class Program
{
    static void Main()
    {
        using (Game game = new MyGame())
        {
            game.Run();
        }
    }
}
 
class MyGame : Game
{
    Effect effect;
    InputLayout vertexLayout;
    Buffer vertexBuffer;
 
    protected override void Draw()
    {
        GraphicsDevice.ImmediateContext.ClearRenderTargetView(
            RenderTarget,
            new SlimDX.Color4(1, 0.39f, 0.58f, 0.93f)
            );
 
        updateCamera();
        initTriangleInputAssembler();
        drawTriangle();
 
        SwapChain.Present(0, PresentFlags.None);
    }
 
    private void updateCamera()
    {
        double time = System.Environment.TickCount / 500d;
 
        Matrix view = Matrix.LookAtRH(
            new Vector3((float)System.Math.Cos(time), 0, (float)System.Math.Sin(time)),
            new Vector3(),
            new Vector3(0, 1, 0)
            );
        Matrix projection = Matrix.PerspectiveFovRH(
            (float)System.Math.PI / 2,
            ClientSize.Width / ClientSize.Height,
            0.1f, 1000
            );
        effect.GetVariableByName("ViewProjection")
            .AsMatrix().SetMatrix(view * projection);
 
    }
 
    private void drawTriangle()
    {
        effect.GetTechniqueByIndex(0).GetPassByIndex(0).Apply(GraphicsDevice.ImmediateContext);
        GraphicsDevice.ImmediateContext.Draw(3, 0);
    }
 
    private void initTriangleInputAssembler()
    {
        GraphicsDevice.ImmediateContext.InputAssembler.InputLayout = vertexLayout;
        GraphicsDevice.ImmediateContext.InputAssembler.SetVertexBuffers(
            0,
            new VertexBufferBinding(vertexBuffer, VertexPositionColor.SizeInBytes, 0)
            );
        GraphicsDevice.ImmediateContext.InputAssembler.PrimitiveTopology
            = PrimitiveTopology.TriangleList;
    }
 
    protected override void LoadContent()
    {
        initEffect();
        initVertexLayout();
        initVertexBuffer();
    }
 
 
    private void initEffect()
    {
        using (ShaderBytecode shaderBytecode = ShaderBytecode.CompileFromFile(
            "myEffect.fx", "fx_5_0",
            ShaderFlags.None,
            EffectFlags.None
            ))
        {
            effect = new Effect(GraphicsDevice, shaderBytecode);
        }
    }
 
    private void initVertexLayout()
    {
        vertexLayout = new InputLayout(
            GraphicsDevice,
            effect.GetTechniqueByIndex(0).GetPassByIndex(0).Description.Signature,
            VertexPositionColor.VertexElements
            );
    }
 
    private void initVertexBuffer()
    {
        vertexBuffer = MyDirectXHelper.CreateVertexBuffer(
            GraphicsDevice,
            new[] {
                new VertexPositionColor
                {
                    Position = new Vector3(0, 0.5f, 0),
                    Color = new Vector3(1, 1, 1) 
                },
                new VertexPositionColor
                {
                    Position = new Vector3(0.5f, 0, 0),
                    Color = new Vector3(0, 0, 1)
                },
                new VertexPositionColor
                {
                    Position = new Vector3(-0.5f, 0, 0),
                    Color = new Vector3(1, 0, 0)
                },
            });
    }
 
    protected override void UnloadContent()
    {
        effect.Dispose();
        vertexLayout.Dispose();
        vertexBuffer.Dispose();
    }
}
 
struct VertexPositionColor
{
    public Vector3 Position;
    public Vector3 Color;
 
    public static readonly InputElement[] VertexElements = new[]
    {
        new InputElement
        {
            SemanticName = "SV_Position",
            Format = Format.R32G32B32_Float
        },
        new InputElement
        {
            SemanticName = "COLOR",
            Format = Format.R32G32B32_Float,
            AlignedByteOffset = InputElement.AppendAligned//自動的にオフセット決定
        }
    };
 
    public static int SizeInBytes
    {
        get
        {
            return System.Runtime.InteropServices.
                Marshal.SizeOf(typeof(VertexPositionColor));
        }
    }
}
 
class Game : System.Windows.Forms.Form
{
    public SlimDX.Direct3D11.Device GraphicsDevice;
    public SwapChain SwapChain;
    public RenderTargetView RenderTarget;
 
 
    public void Run()
    {
        initDevice();
        SlimDX.Windows.MessagePump.Run(this, Draw);
        disposeDevice();
    }
 
    private void initDevice()
    {
        MyDirectXHelper.CreateDeviceAndSwapChain(
            this, out GraphicsDevice, out SwapChain
            );
 
        initRenderTarget();
        initViewport();
 
        LoadContent();
    }
 
    private void initRenderTarget()
    {
        using (Texture2D backBuffer
            = SlimDX.Direct3D11.Resource.FromSwapChain<Texture2D>(SwapChain, 0)
            )
        {
            RenderTarget = new RenderTargetView(GraphicsDevice, backBuffer);
            GraphicsDevice.ImmediateContext.OutputMerger.SetTargets(RenderTarget);
        }
    }
 
    private void initViewport()
    {
        GraphicsDevice.ImmediateContext.Rasterizer.SetViewports(
            new Viewport
            {
                Width = ClientSize.Width,
                Height = ClientSize.Height,
            }
            );
    }
 
    private void disposeDevice()
    {
        UnloadContent();
        RenderTarget.Dispose();
        GraphicsDevice.Dispose();
        SwapChain.Dispose();
    }
 
    protected virtual void Draw() { }
    protected virtual void LoadContent() { }
    protected virtual void UnloadContent() { }
}
 
class MyDirectXHelper
{
    public static void CreateDeviceAndSwapChain(
        System.Windows.Forms.Form form,
        out SlimDX.Direct3D11.Device device,
        out SlimDX.DXGI.SwapChain swapChain
        )
    {
        SlimDX.Direct3D11.Device.CreateWithSwapChain(
            DriverType.Hardware,
            DeviceCreationFlags.None,
            new SwapChainDescription
            {
                BufferCount = 1,
                OutputHandle = form.Handle,
                IsWindowed = true,
                SampleDescription = new SampleDescription
                {
                    Count = 1,
                    Quality = 0
                },
                ModeDescription = new ModeDescription
                {
                    Width = form.ClientSize.Width,
                    Height = form.ClientSize.Height,
                    RefreshRate = new SlimDX.Rational(60, 1),
                    Format = Format.R8G8B8A8_UNorm
                },
                Usage = Usage.RenderTargetOutput
            },
            out device,
            out swapChain
            );
    }
 
    public static Buffer CreateVertexBuffer(
        SlimDX.Direct3D11.Device graphicsDevice,
        System.Array vertices
        )
    {
        using (SlimDX.DataStream vertexStream 
            = new SlimDX.DataStream(vertices, true, true))
        {
            return new Buffer(
                graphicsDevice,
                vertexStream,
                new BufferDescription
                {
                    SizeInBytes= (int)vertexStream.Length,
                    BindFlags = BindFlags.VertexBuffer,
                }
                );
        }
    }
}
myEffect.fx

matrix ViewProjection;
 
struct VertexPositionColor
{
float4 Position : SV_Position;
float4 Color : COLOR;
};
 
VertexPositionColor MyVertexShader(VertexPositionColor input)
{
    VertexPositionColor output = input;
    output.Position = mul(output.Position, ViewProjection);
    return output;
}
 
float4 MyPixelShader(VertexPositionColor input) : SV_Target
{
    return input.Color;
}
 
 
technique10 MyTechnique
{
pass MyPass
{
SetVertexShader( CompileShader( vs_5_0, MyVertexShader() ) );
SetPixelShader( CompileShader( ps_5_0, MyPixelShader() ) );
}
}
このプログラムは回転する三角形を描きます。 ただ正確に言うと、回転しているのは三角形の方ではなくてカメラなのですが。
 
Tutorial07HowTheCameraRotates.jpg

結果、三角形が回転しているように見えるのです。

Tutorial07RotatingTriangle01.jpg

Tutorial07RotatingTriangle03.jpg

Tutorial07RotatingTriangle02.jpg

拍手[1回]