忍者ブログ

Memeplexes

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

C#でDirectX11 SlimDXチュートリアルその12 ワイヤーフレーム

 今回はワイヤーフレームです。
ワイヤーフレームとは何かというと、
こんな三角形を

Tutorial03WhiteTriangle.jpg

こう表示することです:

Tutorial12WireFrame.jpg


図形の骨組みだけ表示されるのです。
上の三角形で言うと、3つの辺だけが描かれ、その内部は描かれません。

これは一体何に使うのか?
ということですが、ポリゴンの様子がよくわかるということで、デバッグ用といいますか、DirectXのポリゴンの分割サンプルにはよく使われているようです。

SubD11WithoutWireframe.jpg

SubD11WithWireframe.jpg

上は普通のモデルだけ描画、下は普通のモデル+そのワイヤーフレームを描画しています。
こうしてみるとポリゴンがどんな様子なのか一目瞭然です。(このサンプルはポリゴンの分割のデモなので、ポリゴンが見えると嬉しいのです)

あるいはレトロSF風味でカッコイイということもあるかもしれませんね。

ラスタライザーステート

ワイヤーフレームを描くには、デバイスをワイヤーフレームがオンになった状態にしてやります。
その状態管理を行うのが、RasterizerWrapper.Stateプロパティです。
GraphicsDevice.ImmediateContext.Rasterizer.State = ~~というふうに設定します。

public RasterizerState State { get; set; }

SlimDX.Direct3D11.RasterizerStateクラスはデバイスの描画設定をつかさどります。
今回のようにワイヤーフレームをオンにしたい場合は、そういう設定をしたこのクラスのインスタンスをデバイスにセットします。
初期化にはRasterizerState.FromDescriptionメソッドを使います。

public class RasterizerState : DeviceChild
{
    public static RasterizerState FromDescription(
        Device device, 
        RasterizerStateDescription description
        );
...
}

deviceはRasterizerStateを作るデバイス。
descriptionはRasterizerStateが設定するデバイスの描画設定です。つまり今回の設定はdescriptionに対してセットします。

SlimDX.Direct3D11.RasterizerStateDescription構造体のメンバのうち、今回セットする必要があるのはCullModeFillModeプロパティです。

public struct RasterizerStateDescription
{
    public CullMode CullMode { get; set; }
    public FillMode FillMode { get; set; }
...
}

CullModeは三角形が描かれなくする方向です。
三角形ポリゴンには方向があります。
FrontとBackです(CullMode.FrontCullMode.Back)。
この方向はIsFrontCounterclockwiseプロパティ(今回は使いませんが)によって時計回りか反時計回りかがセットされるのです。
CullModeプロパティによってセットされた方向では三角形が見えなくなります。
これはより近くのポリゴンによって隠れてしまうポリゴンはどうせ描かれないので最初っから描かないことにしようというものです。

例えば地球を描く事を考えます。
日本が表を向いた地球を描くとき、地球の反対側のチリ共和国を構成しているポリゴンはどうあがいたって描かれません。
なら最初から地球の裏側は描かないことにすれば描画コストを削減できるというわけです。
地球の裏側にあるかどうかというのを、三角形の頂点が時計回りに見えるかどうかというのを基準に考えるのです。


FillModeは図形内部を色塗りする方法です。
FillMode.WireframeFillMode.Solidのどちらかをセットします。
前回までやってきた三角形内部を塗りつぶすのはSolidで、今回のワイヤーフレームはWireframeです。

本当は今回のワイヤーフレームを使うのにCullModeは関係ありません。
しかし、デフォルトの設定ではAPIがエラーを投げるのです。
これはしかたがありません。
とりあえず今回はCullModeにはBackをセットしておきます。

コード

Program.cs
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, 0, 1)
            );
 
        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, sizeof(float) * 3, 0)
            );
        GraphicsDevice.ImmediateContext.InputAssembler.PrimitiveTopology
            = PrimitiveTopology.TriangleList;
    }
 
    protected override void LoadContent()
    {
        initEffect();
        initVertexLayout();
        initVertexBuffer();
        initWireframeRasterizerState();
    }
 
    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,
            new[] { 
                    new InputElement
                    {
                        SemanticName = "SV_Position",
                        Format = Format.R32G32B32_Float
                    }
                }
            );
    }
 
    private void initVertexBuffer()
    {
        vertexBuffer = MyDirectXHelper.CreateVertexBuffer(
            GraphicsDevice,
            new[] {
                new SlimDX.Vector3(0, 0.5f, 0),
                new SlimDX.Vector3(0.5f, 0, 0),
                new SlimDX.Vector3(-0.5f, 0, 0),
            });
    }
 
    private void initWireframeRasterizerState()
    {
        var desc = new RasterizerStateDescription
        {
            CullMode = CullMode.Back,
            FillMode = FillMode.Wireframe,
        };
 
        GraphicsDevice.ImmediateContext.Rasterizer.State
            = RasterizerState.FromDescription(
                GraphicsDevice,
                desc
            );
    }
 
    protected override void UnloadContent()
    {
        GraphicsDevice.ImmediateContext.Rasterizer.State.Dispose();
        effect.Dispose();
        vertexLayout.Dispose();
        vertexBuffer.Dispose();
    }
}
 
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
float4 MyVertexShader(float4 position : SV_Position) : SV_Position
{
    return position;
}

float4 MyPixelShader() : SV_Target
{
    return float4(1, 1, 1, 1);
}

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

このプログラムはワイヤーフレームな三角形を表示します。

d4d15310.jpg

デバイスが初期化するときに、ラスタライザーステートをデバイスにセットするのです。
そのラスタライザーステートには、ワイヤーフレームで表示せよ、ということが書いてあります。
結果として、前回まで中身がきちんと描かれていた三角形は、周りの辺だけが描かれることになったのです。





























拍手[0回]

PR