baobao
11/14/2018 - 12:18 PM

RotationTriangle.cs

using System.Windows.Forms;
using SlimDX.DXGI;
using SlimDX.Windows;
using SlimDX.Direct3D11;
using Device = SlimDX.Direct3D11.Device;
using SlimDX;
using SlimDX.D3DCompiler;
using System.Diagnostics;
using System.Threading;

public class RotationTriangle:Form
{
    struct VertexData
    {
        public Vector3 position;
        public Vector3 color;

        public static int SizeInBytes => System.Runtime.InteropServices.Marshal.SizeOf(typeof(VertexData));

        public static InputElement[] VertexInputElements = new[]
        {
            new InputElement
            {
                SemanticName = "SV_Position",
                Format = Format.R32G32B32_Float
            },
            new InputElement
            {
                SemanticName = "COLOR",
                Format = Format.R32G32B32_Float,
                // アドレスの自動配置
                AlignedByteOffset = InputElement.AppendAligned
            }
        };
    }


    Device device;
    SwapChain swapChain;
    RenderTargetView renderTarget;
    InputLayout vertexLayout;
    Effect effect;
    SlimDX.Direct3D11.Buffer vertexBuffer;
    Stopwatch sw = new Stopwatch();
    int fps = 30;
    long frameCount = 0;

    public void Run()
    {
        Test.CreateDeviceWithSwapChain(this, out device, out swapChain);
        
        // 描画対象の初期化
        using (var backBuffer = SlimDX.Direct3D11.Resource.FromSwapChain<Texture2D>(swapChain, 0))
        {
            renderTarget = new RenderTargetView(device, backBuffer);
            device.ImmediateContext.OutputMerger.SetTargets(renderTarget);
        }
        
        // シェーダのコンパイル
        effect = CompileShader("rotation.fx");

        vertexLayout = new InputLayout(
            device,
            effect.GetTechniqueByIndex(0)
            .GetPassByIndex(0).Description.Signature,
            VertexData.VertexInputElements
        );

        var vertexDatas = new VertexData[]
        {
            new VertexData
            {
                position = new Vector3(0f, 0.5f, 0),
                color = new Vector3(1,0,0)
            },
            new VertexData
            {
                position =new Vector3(0.5f, -0.5f, 0),
                color = new Vector3(0,1,0)
            },
            new VertexData
            {
                position = new Vector3(-0.5f, -0.5f, 0),
                color = new Vector3(1, 1, 1)
            }
        };

        // 頂点バッファの作成
        vertexBuffer = CreateVertexBuffer(
            device,
            vertexDatas
         );

        // ViewPort設定
        device.ImmediateContext.Rasterizer.SetViewports(
            new Viewport
            {
                Width = ClientSize.Width,
                Height = ClientSize.Height
            }
        );

        MessagePump.Run(this, Loop);

        vertexBuffer.Dispose();
        effect.Dispose();
        vertexLayout.Dispose();
    }

    private Effect CompileShader(string path)
    {
        using (var shader = ShaderBytecode.CompileFromFile(
            path,
            "fx_5_0",
            ShaderFlags.None,
            EffectFlags.None))
        {
            return new Effect(device, shader);
        }
    }
    
    void Loop()
    {
        sw.Restart();
        Draw();
        // 経過ミリ秒
        var elapsedMilliSec = sw.ElapsedMilliseconds;
        var sleepTime = 1000 / fps - elapsedMilliSec;
        if (sleepTime > 0)
        {
            Thread.Sleep((int)sleepTime);
        }
        frameCount++;
    }

    void Draw()
    {
        // 背景クリア
        device.ImmediateContext.ClearRenderTargetView(
            renderTarget,
            new Color4(1,0.5f, 0.5f, 0.5f)
        );

        // 1フレームにおける回転するオイラー角
        var eulerAnglePerFrame = 40f / (float)fps;
        float radian = eulerAnglePerFrame * (float)frameCount * (float)System.Math.PI / 180f;

        // Z軸回転行列生成
        var axis = new Vector3(0.5f, 0, 1f);
        Matrix rotateMatrix = Matrix.RotationAxis(axis, radian);
        var matrix = effect.GetVariableByName("RotateMatrix");
        matrix.AsMatrix().SetMatrix(rotateMatrix);


        // 三角形描画初期化
        device.ImmediateContext.InputAssembler.InputLayout = vertexLayout;
        device.ImmediateContext.InputAssembler.SetVertexBuffers(
            0, new VertexBufferBinding(
                vertexBuffer, VertexData.SizeInBytes, 0)
        );
        device.ImmediateContext.InputAssembler.PrimitiveTopology
            = PrimitiveTopology.TriangleList;


        // 三角形描画
        effect.GetTechniqueByIndex(0).GetPassByIndex(0).Apply(device.ImmediateContext);
        device.ImmediateContext.Draw(3, 0);

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


    SlimDX.Direct3D11.Buffer CreateVertexBuffer(
        Device device, 
        System.Array vertices
    )
    {
        using (DataStream vertexStream 
            = new DataStream(vertices, true, true))
        {
            return new SlimDX.Direct3D11.Buffer(
                device,
                vertexStream,
                new BufferDescription()
                {
                    // バッファのサイズ
                    SizeInBytes = (int)vertexStream.Length,
                    // バッファの種類
                    BindFlags = BindFlags.VertexBuffer
            });
        }
    }
    
    /// <summary>
    /// デバイスとSwapChainを作成
    /// </summary>
    static public SlimDX.Result CreateDeviceWithSwapChain(
        Form form,
        out SlimDX.Direct3D11.Device device, 
        out SwapChain swapChain
        )
    {
        return Device.CreateWithSwapChain(
            // Direct3Dの機能を持つデバイス(GPU)を使用する
            DriverType.Hardware,
            // デバイスに特別なふるまいをさせるときは設定するが、基本はNone
            DeviceCreationFlags.None,
            // SwapChainの詳細設定
            new SwapChainDescription()
            {
                // 2:ダブルバッファでティアリングを回避
                BufferCount = 1,
                // 表示するウィンドウ
                OutputHandle = form.Handle,
                // falseにするとフルスクリーン
                IsWindowed = true,
                // SwapChainのマルチサンプル方法設定
                SampleDescription = new SampleDescription()
                {
                    Count = 1,
                    Quality = 0
                },
                // ウィンドウの大きさやリフレッシュレートに関する設定
                ModeDescription = new ModeDescription()
                {
                    // ウィンドウの幅
                    Width = form.ClientSize.Width,
                    // ウィンドウの高さ
                    Height = form.ClientSize.Height,
                    // リフレッシュレート60Hz
                    RefreshRate = new SlimDX.Rational(60, 1),
                    // ウィンドウのフォーマット
                    // https://msdn.microsoft.com/ja-jp/library/ee416140(v=vs.85).aspx
                    Format = Format.R8G8B8A8_UNorm
                },
                // 描画対象の使用方法
                // ウィンドウにただ表示するだけなので、RenderTargetOutputをセット
                Usage = Usage.RenderTargetOutput
            },
            out device,
            out swapChain
        );
    }
}
matrix RotateMatrix;

struct VertexData
{
    float4 position : SV_Position;
    float4 color : COLOR;
};

// 頂点シェーダ
VertexData MyVertexShader(VertexData input)
{
    // 頂点座標に回転行列を反映する
    input.position = mul(input.position, RotateMatrix);
    return input;
}

// ピクセルシェーダ
float4 MyPixelShader(VertexData input) : SV_Target
{
    // インプットのカラーで描画
    return input.color;
}

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