シェーダーを使ってティーポットを描画

シェーダー(.fx)を使ってティーポットを描画します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プロジェクトの作成

  1. Sample Brower を使って作成したプロジェクトには、シェーダー(.fx) が組み込まれています。
    Sample Brower でティーポットを描画 では、シェーダーを使わずに作成しました。
    今回はシェーダー(.fx)を使ってティーポットを描画します。
    プロジェクトの作成は「Sample Brower でティーポットを描画」を参照して下さい。
  2. プロジェクトのフォルダーに次のファイルを格納して下さい。
    Sample Brower で自動的に作成された EmptyProject.fx は使えません。
    ティーポットを描画するプログラムです。
    /********************************************************/
    /*★ マウスでティーポットを操作(fx を使う)    前田 稔 ★*/
    /********************************************************/
    using System;
    using Microsoft.DirectX;
    using Microsoft.DirectX.Direct3D;
    using Microsoft.Samples.DirectX.UtilityToolkit;
    
    namespace EmptyProjectSample
    {
        public class EmptyProject : IFrameworkCallback, IDeviceCreation
        {
            // Variables
            private Framework sampleFramework = null;
            private Mesh    mesh = null;
            private Effect  effect = null;
            private ModelViewerCamera camera = new ModelViewerCamera();
    
            public EmptyProject(Framework f)
            { sampleFramework = f; }
    
            public bool IsDeviceAcceptable(Caps caps, Format adapterFormat, Format backBufferFormat, bool windowed)
            {
                if (!Manager.CheckDeviceFormat(caps.AdapterOrdinal, caps.DeviceType, adapterFormat, 
                    Usage.QueryPostPixelShaderBlending, ResourceType.Textures, backBufferFormat))
                    return false;
                return true;
            }
    
            public void ModifyDeviceSettings(DeviceSettings settings, Caps caps)
            {
                if ( (!caps.DeviceCaps.SupportsHardwareTransformAndLight) ||
                    (caps.VertexShaderVersion < new Version(1,1)) )
                {   settings.BehaviorFlags = CreateFlags.SoftwareVertexProcessing;  }
                else
                {   settings.BehaviorFlags = CreateFlags.HardwareVertexProcessing;  }
    
                if ( (caps.DeviceCaps.SupportsPureDevice) && 
                    ((settings.BehaviorFlags & CreateFlags.HardwareVertexProcessing) != 0 ) )
                    settings.BehaviorFlags |= CreateFlags.PureDevice;
    
                if (settings.DeviceType == DeviceType.Reference)
                {
                    Utility.DisplaySwitchingToRefWarning(sampleFramework, "EmptyProject");
                }
            }
    
            private void OnCreateDevice(object sender, DeviceEventArgs e)
            {
                ShaderFlags shaderFlags = ShaderFlags.NotCloneable;
    
                // Read the D3DX effect file
                string path = Utility.FindMediaFile("EmptyProject.fx");
                effect = ResourceCache.GetGlobalInstance().CreateEffectFromFile(e.Device,
                    path, null, null, shaderFlags, null);
    
                //ティーポットの生成
                mesh = Mesh.Teapot(e.Device);
            }
            
            private void OnResetDevice(object sender, DeviceEventArgs e)
            {
                SurfaceDescription desc = e.BackBufferDescription;
    
                // Setup the camera's projection parameters
                float aspectRatio = (float)desc.Width / (float)desc.Height;
                camera.SetViewParameters(new Vector3(0.0f, 2.0f, -7.0f), Vector3.Empty);
                camera.SetProjectionParameters((float)Math.PI / 4, aspectRatio, 1.0f, 500.0f);
                camera.SetWindow(desc.Width, desc.Height);
            }
    
            private void OnDestroyDevice(object sender, EventArgs e)
            {
            }
    
            public void OnFrameMove(Device device, double appTime, float elapsedTime)
            {
                // Update the camera's position based on user input 
                camera.FrameMove(elapsedTime);
            }
    
            public IntPtr OnMsgProc(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam, ref bool noFurtherProcessing)
            {
                // Pass all remaining windows messages to camera so it can respond to user input
                camera.HandleMessages(hWnd, msg, wParam, lParam);
                return IntPtr.Zero;
            }
    
            public void OnFrameRender(Device device, double appTime, float elapsedTime)
            {
                bool beginSceneCalled = false;
    
                // Clear the render target and the zbuffer 
                device.Clear(ClearFlags.ZBuffer | ClearFlags.Target, 0x00424B79, 1.0f, 0);
                try
                {
                    device.BeginScene();
                    beginSceneCalled = true;
    
                    effect.SetValue("g_mWorldViewProjection", camera.WorldMatrix * camera.ViewMatrix * camera.ProjectionMatrix);
                    effect.SetValue("g_mWorld", camera.WorldMatrix);
                    effect.SetValue("g_fTime", (float)appTime);
    
                    int passes = effect.Begin(0);
                    for(int pass = 0; pass < passes; pass++)
                    {
                        effect.BeginPass(pass);
                        mesh.DrawSubset(0);
                        effect.EndPass();
                    }
                    effect.End();
                }
                finally
                {
                    if (beginSceneCalled)   device.EndScene();
                }
            }
    
            static int Main() 
            {
                System.Windows.Forms.Application.EnableVisualStyles();
                using(Framework sampleFramework = new Framework())
                {
                    EmptyProject sample = new EmptyProject(sampleFramework);
    
                    sampleFramework.Disposing += new EventHandler(sample.OnDestroyDevice);
                    sampleFramework.DeviceCreated += new DeviceEventHandler(sample.OnCreateDevice);
                    sampleFramework.DeviceReset += new DeviceEventHandler(sample.OnResetDevice);
    
                    sampleFramework.SetWndProcCallback(new WndProcCallback(sample.OnMsgProc));
    
                    sampleFramework.SetCallbackInterface(sample);
                    try
                    {
                        // Show the cursor and clip it when in full screen
                        sampleFramework.SetCursorSettings(true, true);
    
                        sampleFramework.Initialize( true, true, true );
                        sampleFramework.CreateWindow("EmptyProject");
                        sampleFramework.CreateDevice( 0, true, Framework.DefaultSizeWidth,
                            Framework.DefaultSizeHeight, sample);
    
                        sampleFramework.MainLoop();
                    }
                    catch
                    {
                        // Ignore any exceptions here, they would have been handled by other areas
                        return (sampleFramework.ExitCode == 0) ? 1 : sampleFramework.ExitCode;
                    }
    
                    return sampleFramework.ExitCode;
                }
            }
        }
    }
    
    シェーダープログラムです。
    //--------------------------------------------------------------------------------------
    // File: Teapot.fx
    //--------------------------------------------------------------------------------------
    
    
    //--------------------------------------------------------------------------------------
    // Global variables
    //--------------------------------------------------------------------------------------
    float    g_fTime;                   // App's time in seconds
    float4   g_vDiffuse = float4(1,0.8,1,1);    // Material diffuse color
    float4x4 g_mWorld;                  // World matrix for object
    float4x4 g_mWorldViewProjection;    // World * View * Projection matrix
    texture  g_txScene;
    
    
    sampler g_samScene =
    sampler_state
    {
        Texture = <g_txScene>;
        MinFilter = Linear;
        MagFilter = Linear;
        MipFilter = Linear;
    };
    
    
    void VertScene( float4 Pos : POSITION,
                    float3 Normal : NORMAL,
                    float2 Tex : TEXCOORD0,
                    out float4 oPos : POSITION,
                    out float2 oTex : TEXCOORD0,
                    out float4 Diffuse : COLOR0 )
    {
        oPos = mul( Pos, g_mWorldViewProjection );
    
        float3 N = normalize( mul( Normal, (float3x3)g_mWorld ) );
        Diffuse = saturate( dot( (float3)N, float3( 0.0f, 0.0f, -1.0f ) ) ) * g_vDiffuse;
    
        oTex = Tex;
    }
    
    
    float4 PixScene( float2 Tex : TEXCOORD0,
                     float4 Diffuse : COLOR0 ) : COLOR0
    {
        return tex2D( g_samScene, Tex ) * Diffuse;
    }
                    
    
    //--------------------------------------------------------------------------------------
    // Techniques
    //--------------------------------------------------------------------------------------
    technique RenderScene
    {
        pass P0
        {
            VertexShader = compile vs_1_1 VertScene();
            PixelShader = compile ps_1_1 PixScene();
        }
    }
    
  3. EmptyProject_2005.csproj から起動して下さい。
    x86 に設定してコンパイル&実行をするとティーポットのメッシュが描画されます。
    シェーダーの詳細は DirectX10 を参照して下さい。

プログラムの説明

  1. プロジェクトの共通領域です。
    sampleFramework は Direct3D を制御する Framework Class です。
    mesh はティーポットのメッシュです。
    effect がシェーダーを格納する領域です。
    camera はカメラを設定する領域です。
            private Framework sampleFramework = null;
            private Mesh    mesh = null;
            private Effect  effect = null;
            private ModelViewerCamera camera = new ModelViewerCamera();
    
  2. OnCreateDevice() で effect にシェーダーを格納します。
    ティーポットのモデルを生成して mesh に格納します。
            private void OnCreateDevice(object sender, DeviceEventArgs e)
            {
                ShaderFlags shaderFlags = ShaderFlags.NotCloneable;
    
                // Read the D3DX effect file
                string path = Utility.FindMediaFile("EmptyProject.fx");
                effect = ResourceCache.GetGlobalInstance().CreateEffectFromFile(e.Device,
                    path, null, null, shaderFlags, null);
    
                //ティーポットの生成
                mesh = Mesh.Teapot(e.Device);
            }
    
  3. OnResetDevice() で camera に View, Projection を設定します。
    シェーダーで光源の設定やモデルの色を計算するので、マテリアルやライトの設定は不要です。
            private void OnResetDevice(object sender, DeviceEventArgs e)
            {
                SurfaceDescription desc = e.BackBufferDescription;
    
                // Setup the camera's projection parameters
                float aspectRatio = (float)desc.Width / (float)desc.Height;
                camera.SetViewParameters(new Vector3(0.0f, 2.0f, -7.0f), Vector3.Empty);
                camera.SetProjectionParameters((float)Math.PI / 4, aspectRatio, 1.0f, 500.0f);
                camera.SetWindow(desc.Width, desc.Height);
            }
    
  4. OnFrameRender() でシェーダーを使ってティーポットのメッシュを描画します。
            public void OnFrameRender(Device device, double appTime, float elapsedTime)
            {
                bool beginSceneCalled = false;
    
                // Clear the render target and the zbuffer 
                device.Clear(ClearFlags.ZBuffer | ClearFlags.Target, 0x00424B79, 1.0f, 0);
                try
                {
                    device.BeginScene();
                    beginSceneCalled = true;
    
                    effect.SetValue("g_mWorldViewProjection", camera.WorldMatrix * camera.ViewMatrix * camera.ProjectionMatrix);
                    effect.SetValue("g_mWorld", camera.WorldMatrix);
                    effect.SetValue("g_fTime", (float)appTime);
    
                    int passes = effect.Begin(0);
                    for(int pass = 0; pass < passes; pass++)
                    {
                        effect.BeginPass(pass);
                        mesh.DrawSubset(0);
                        effect.EndPass();
                    }
                    effect.End();
                }
                finally
                {
                    if (beginSceneCalled)   device.EndScene();
                }
            }
    

超初心者のプログラム入門(C# Frame Work)