XNA for C#
DirectX 9 for C#
DirectX 9 for C++
DirectX 9 for VB
Forum
   
My XNA Book
      
       Go to section on this site

Additional info


Latest Forum posts

 Account settings
  Posted by: Anonymous
  When: 07/05/2014 at 09:48:39

 forced subtitle
  Posted by: Applefly
  When: 07/05/2014 at 06:00:48

 convert DVD into PMS
  Posted by: Applefly
  When: 07/05/2014 at 05:55:25

 DVD to Digital Copy easily
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:52:29

 DVD on Xbox 360/Xbox One Console
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:51:47

 Extract .Srt Subtitles
  Posted by: Applefly
  When: 04/05/2014 at 03:54:38

 Encode Movie collection
  Posted by: Applefly
  When: 04/05/2014 at 03:52:41

 Convert DVD to WMV
  Posted by: Applefly
  When: 29/04/2014 at 05:53:50

 rip DVDs into digital files
  Posted by: Applefly
  When: 29/04/2014 at 05:51:20

 iTunes movies/music to Kindle Fire
  Posted by: ciciyu80
  When: 29/04/2014 at 05:10:20


Ads

Drawing the triangle using Shaders

Up to this point, we have a vertex buffer, filled with only 3 vertices defining a single triangle. We also have the metadata, the VertexDeclaration, which describes what kind of data is encapsulated in the vertex data, together with the offset to that kind of data.

We also have a very simple vertex shader. From our vertex stream, it extracts only the position data, the color data is not yet used. Every vertex is transformed to 2D screen coordinates, and passed to the default pixel shader, which, well, simply draws the pixels to the screen, without any modification. To perform this transformation, we multiply each vertex with the matrix which is the combination of the View and Projection matrix, which is at this point not yet being passed on to our HLSL code.

In our DirectX app, we will first load the .fx file into an Effect object, and set the transformation matrix. So first we need an Effect object to store our effect:

 private Effect effect;

Next we’re going to load our .fx file into this object. Because an effect is a managed resource, we can put this line in our AllocateResources method:

 effect = D3D.Effect.FromFile(device, @"../../OurHLSLFile.fx", null, null, ShaderFlags.None, null);

As first arguments, this method takes the device and the filename of the .fx file. Since the executable file resides in the \bin\Debug directory and I put my .fx file in the same map as my project and .cs files, I need to define the ‘..\..\’ to go back 2 maps.

The third argument allows to use #include directive in your HLSL file, while the fourth let’s you define which parts of the HLSL code to skip. Together with the last argument, which lets you share this effect among multiple apps, these arguments are beyond the scope of this chapter, so we simply pass null. The 5th argument can be used to have your .fx code compiled in a special way, but passing the ShaderFlags.None directive usually gives best performance.

Now it’s time to update our OnPaint method. Put your focus on the code between the calls to BeginScene and EndScene. The UpdateFrameRate line is the only one we’ll keep, you can delete the rest. You can already put this code:

 device.SetStreamSource(0, vb, 0);            
 device.VertexDeclaration = vd;
 
 effect.Technique = "Simplest";
 effect.SetValue("xViewProjection", matView * matProjection);

The first line indicates which vertex stream we want to be drawn from. The second line, which is quite important, defines what data is in the vertex stream.

Because one .fx file can contain multiple techniques, we first need to select the one we want, which is what the third line does. Then we need to set all of the constants in our vertex shader. In our case, this is only xViewProjection, which we fill with the combination of our view and projection matrices. To make things easier, I’ll put an ‘x’ in front of each HLSL variable, so you don’t get confused while setting them through DirectX. Take some time now to go back in your HLSL code, and find your technique ‘Simplest’, as well as the variable xViewProjection.

Note that this change will not be performed right now, you’ll first have to call effect.CommitChanges or effect.Begin.

Now it’s time to start drawing. Because one technique can have multiple passes, we need to iterate through them. Although our technique only has one pass, it’s good practice to use the code shown below:

 int numpasses = effect.Begin(0);
 for (int i = 0; i < numpasses; i++)
 {
     effect.BeginPass(i);
     device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
     effect.EndPass();
 }
 effect.End();

Our call to effect.Begin returns the number of passes in our technique, which will be 1 in our case. More important, this method also updates the changes to the shader constants that were requested by our call to effect.SetValue.

For each pass, you first put a call to BeginPass, then you draw your scene and afterwards you put a call to EndPass, much like the calls the BeginScene and EndScene. Our actual scene consists of only 1 triangle, which we draw with the DrawPrimitives method. This method passes the vertex stream to the GPU and invokes our vertex shader. Make sure you end your effect by placing a call to effect.End().

Finally, our code is ready to run. Hit F5, and if everything is OK you should see a solid white triangle. White, because we coded our vertex shader to draw every vertex white. Not a lot of fun, so let’s add some color to it. Go to the vertex shader in your .fx file, and update it to this code:

VertexToPixel SimplestVertexShader( float4 inPos : POSITION, float4 inColor : COLOR0)
{
    VertexToPixel Output = (VertexToPixel)0;
    
    Output.Position =mul(inPos, xViewProjection);
    Output.Color = inColor;
    
    return Output;    
}

Remember we also defined some color information in our vertex buffer? And in the VertexDeclaration we defined its offset. So now we can simply use the color of every vertex as input in our vertex shader. This is what is updated in the first line of the code above.

The only change that has been made to the interior of the method, is that we simply route this color we get from our DirectX app immediately to our pixel shader, instead of simple white.

Now when you run this code, you should see the same colored triangle as before. This means we have implemented the default vertex shader! Our vertex shader simply transforms the 3D coordinates to 2D screen coordinates, and passes these coordinates together with the correct color to the pixel shader.



Now have another look at our flowchart. You’ll notice the rasterizer between the vertex and the pixel shader. This rasterizer determines which pixels on the screen are occupied by our triangle. And now something important: the interpolator next to it, interpolates all the data sent by the vertex shader to the pixel shader. In our example, this means the colors are nicely shaded from corner to corner. Our next code will demonstrate this, and how this can cause problems.

So you can still be asking yourself “What’s the use of all this ??”. Even at this point, you can see we can perform some manipulations on the vertex positions. You could also do this in your DirectX app, but then your CPU would have to perform those calculations, which would lower your framerate. Now you can have these calculations done by the GPU, which is A LOT faster at it.

Using the vertex shader, you could also adjust the color, which we’ll do now as little exercise. So for this example, we will throw away the color information provided to us by the vertex stream, and define our own colors. Say, we want to have the red color component indicate the y coordinate, the green componetn the x coordinate, and the blue color component indicate the z coordinate. This simple code should do the trick:

Output.Color.rgb = inPos.yxz;

Note that for any color the value 0 and below means ‘none’, and 1 and above means ‘full’. You’ll notice I have chosen the coordinates of our vertexes between the [-2,2] range, which I did especially for this example. So, for every color, you could expect that the color is not present in to [-2,0] region, then shaded from black to full color in the [0,1] region, to remain at full color in the [1,2] region. For example, the point (0,0,0), which is part of our triangle, should be drawn black. (If you fail miserably at imagining this, you can have a peek at the bottom image of next chapter, where the correct colors are shown.) However, when you run the code, you’ll see the colors remains nicely shaded over the full size of the triangle!

So what is happening? Before the colors are passed to the interpolator, every color value is being clipped to the [0,1] region. This interpolator simply gives every pixel in the triangle the color that is the interpolation of the 3 clipped color values.

In short: when only using a vertex shader, the contents of a triangle can only change linearly, and then again only with outer values in the [0,1] range. In the next chapter, we’re going to code our first pixel shader, where we can code the color of every specific pixel!




DirectX Tutorial 4 - Shaded triangle

If you appreciate the amount of time I spend creating and updating
these pages, feel free to donate -- any amount is welcome !



Click here to go to the forum on this chapter!

Or click on one of the topics on this chapter to go there:
  • The Effect class
          My Microsoft.DirectX.Direct3D does not contain the...
  • Access Violation on execute.
          I got an access violation when I called DrawPrimit...
  • Cannot compile FX file
          I'm using directX, and I can't compile any fx fi...
  • small errors in your code
          1) In AllocateResources() you have : effect = ...
  • The @ Sign...
          You said that the @ sign indicates the current dir...
  • Bug in source
          Mistakes in source code....
  • Bug in loading .fx
          Just a heads up that the line to load the .fx file...
  • fxComposer
          Hi Riemer, Like the new series, its going well...


    From this chapter on, I’ll always list the DirectX code, preceded by the HLSL code, as most changes throughout this series will happen in the HLSL code.

    struct VertexToPixel
    {
        float4 Position     : POSITION;
        float4 Color        : COLOR0;
    };

    float4x4 xViewProjection;


     VertexToPixel SimplestVertexShader( float4 inPos : POSITION, float4 inColor : COLOR0)

    {
        VertexToPixel Output = (VertexToPixel)0;
        
        Output.Position =mul(inPos, xViewProjection);

         Output.Color.rgb = inPos.yxz;

        
        return Output;    
    }

    technique Simplest
    {
        pass Pass0
        {        
            VertexShader = compile vs_1_1 SimplestVertexShader();
            PixelShader = NULL;
        }
    }

     using System;
     using System.Drawing;
     using System.Collections;
     using System.ComponentModel;
     using System.Windows.Forms;
     using System.Data;
     using Microsoft.DirectX;
     using Microsoft.DirectX.Direct3D;
     using D3D = Microsoft.DirectX.Direct3D;
     
     namespace DirectX_Tutorial
     {
         struct myownvertexformat
         {
             public Vector3 Pos;
             public int Color;
     
             public myownvertexformat(Vector3 _Pos, int _Color)
             {
                 Pos = _Pos;
                 Color = _Color;
             }
         }
     
         public class WinForm : System.Windows.Forms.Form
         {        
             private System.ComponentModel.Container components = null;
             private D3D.Device device;        
             private VertexBuffer vb;
             private Vector3 CameraPos;
             private VertexDeclaration vd;
             private Effect effect;
     
             private Matrix matView;
             private Matrix matProjection;
     
             private int LastTickCount = 1;
             private int Frames = 0;
             private float LastFrameRate = 0;
             private D3D.Font text;
     
             public WinForm()
             {
                 InitializeComponent();
                 this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
             }
     
             public void InitializeDevice()
             {
                 PresentParameters presentParams = new PresentParameters();
                 presentParams.Windowed = true;
                 presentParams.SwapEffect = SwapEffect.Discard;
                 presentParams.AutoDepthStencilFormat = DepthFormat.D16;
                 presentParams.EnableAutoDepthStencil = true;
     
                 Caps DevCaps = D3D.Manager.GetDeviceCaps(0, D3D.DeviceType.Hardware);
                 D3D.DeviceType DevType = D3D.DeviceType.Reference;
                 CreateFlags DevFlags = CreateFlags.SoftwareVertexProcessing;
                 if ((DevCaps.VertexShaderVersion >= new Version(2, 0)) && (DevCaps.PixelShaderVersion >= new Version(2, 0)))
                 {
                     DevType = D3D.DeviceType.Hardware;                
                     if (DevCaps.DeviceCaps.SupportsHardwareTransformAndLight)
                     {
                         DevFlags = CreateFlags.HardwareVertexProcessing;
                         if (DevCaps.DeviceCaps.SupportsPureDevice)
                         {
                             DevFlags |= CreateFlags.PureDevice;
                         }
                     }                
                 }
     
                 device = new D3D.Device(0, DevType, this, DevFlags, presentParams);
                 device.DeviceReset += new EventHandler(this.HandleDeviceReset);            
             }
     
             private void HandleDeviceReset(object sender, EventArgs e)
             {
                 FillResources();        
             }
     
             private void AllocateResources()
             {
                 vb = new VertexBuffer(typeof(myownvertexformat), 3, device, Usage.WriteOnly, VertexFormats.Position | VertexFormats.Normal | VertexFormats.Texture0, Pool.Managed);                        
                 InitializeFont();
                 effect = D3D.Effect.FromFile(device, @"../../OurHLSLFile.fx", null, null, ShaderFlags.None, null);
             }
     
             private void FillResources()
             {
                 myownvertexformat[] vertices = new myownvertexformat[30];
     
                 vertices[0] = new myownvertexformat(new Vector3(2, -2, -2), Color.Red.ToArgb());
                 vertices[1] = new myownvertexformat(new Vector3(0, 2, 0), Color.Yellow.ToArgb());
                 vertices[2] = new myownvertexformat(new Vector3(-2, -2, 2), Color.Green.ToArgb());
     
                 vb.SetData(vertices, 0, LockFlags.None);
     
                 SetUpCamera();
     
     
                 VertexElement[] velements = new VertexElement[]
                 {
                     new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
                     new VertexElement(0, 12, DeclarationType.Color, DeclarationMethod.Default, DeclarationUsage.Color, 0),
                     VertexElement.VertexDeclarationEnd
                 };
                 vd = new VertexDeclaration(device, velements);
             }
     
             private void InitializeFont()
             {
                 System.Drawing.Font systemfont = new System.Drawing.Font("Arial", 12f, FontStyle.Regular);
                 text = new D3D.Font(device, systemfont);
             }
     
             private void DrawMesh(Mesh mesh, Material[] meshmaterials, Texture[] meshtextures)
             {
                 for (int i = 0; i < meshmaterials.Length; i++)
                 {
                     if (meshtextures.Length > 3) device.SetTexture(0, meshtextures[i]);
                     mesh.DrawSubset(i);
                 }
             }
     
             protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
             {
                 device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0);
                 device.BeginScene();
     
                 device.SetStreamSource(0, vb, 0);            
                 device.VertexDeclaration = vd;
                 effect.Technique = "Simplest";
                 effect.SetValue("xViewProjection", matView * matProjection);
                 int numpasses = effect.Begin(0);
                 for (int i = 0; i < numpasses; i++)
                 {
                     effect.BeginPass(i);
                     device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
                     effect.EndPass();
                 }
                 effect.End();
     
                 UpdateFramerate();
     
                 device.EndScene();
                 device.Present();
                 this.Invalidate();    
             }
     
             private void UpdateFramerate()
             {
                 Frames++;
                 if (Math.Abs(Environment.TickCount - LastTickCount) > 1000)
                 {
                     LastFrameRate = (float)Frames * 1000 / Math.Abs(Environment.TickCount - LastTickCount);
                     LastTickCount = Environment.TickCount;
                     Frames = 0;
                 }
                 text.DrawText(null, string.Format("Framerate : {0:0.00} fps", LastFrameRate), new Point(10, 430), Color.Red);
             }
     
             private void SetUpCamera()
             {
                 CameraPos = new Vector3(0, -6, 5);
                 matProjection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1f, 20f);
                 matView = Matrix.LookAtLH(CameraPos, new Vector3(0, -1, 0), new Vector3(0, 1, 1));
     
                 device.Transform.Projection = matProjection;
                 device.Transform.View = matView;
             }
     
             private void LoadMesh(string filename, ref Mesh mesh, ref Material[] meshmaterials, ref Texture[] meshtextures)
             {
                 ExtendedMaterial[] materialarray;
                 GraphicsStream adj = null;
     
                 mesh = Mesh.FromFile(filename, MeshFlags.Managed, device, out adj, out materialarray);
     
                 if ((materialarray != null) && (materialarray.Length > 0))
                 {
                     meshmaterials = new Material[materialarray.Length];
                     meshtextures = new Texture[materialarray.Length];
     
                     for (int i = 0; i < materialarray.Length; i++)
                     {
                         meshmaterials[i] = materialarray[i].Material3D;
                         meshmaterials[i].Ambient = meshmaterials[i].Diffuse;
     
                         if ((materialarray[i].TextureFilename != null) && (materialarray[i].TextureFilename != string.Empty))
                         {
                             meshtextures[i] = TextureLoader.FromFile(device, materialarray[i].TextureFilename);
                         }
                     }                
                 }
     
                 mesh = mesh.Clone(mesh.Options.Value, CustomVertex.PositionNormalTextured.Format, device);
                 mesh.ComputeNormals();
             }
     
             protected override void Dispose(bool disposing)
             {
                 if (disposing)
                 {
                     if (components != null)
                     {
                         components.Dispose();
                     }
                 }
                 base.Dispose(disposing);
             }
     
             private void InitializeComponent()
             {
                 this.components = new System.ComponentModel.Container();
                 this.Size = new System.Drawing.Size(500, 500);
                 this.Text = "Riemer's DirectX & HLSL Tutorial using C# -- Season 3";
             }
     
             static void Main()
             {
                 using (WinForm our_directx_form = new WinForm())
                 {
                     our_directx_form.InitializeDevice();                
                     our_directx_form.AllocateResources();
                     our_directx_form.FillResources();
                     Application.Run(our_directx_form);
                 }
             }
         }
     }


    Google
     
    Webwww.riemers.net


    If you appreciate the amount of time I spend creating and updating
    these pages, feel free to donate -- any amount is welcome !



    - Website design & XNA + DirectX code : Riemer Grootjans -
    ©2003 - 2011 Riemer Grootjans
  • Translations

    This site in English
    This site in Korean
    This site in Czech

    Microsoft MVP Award



    2007 - 2011 MVP Award
    DirectX - XNA

    Contents

    News
    Home
    Forum
    XNA 2.0 Recipes Book (8)
    XNA 3.0 Recipes Book (8)
    Downloads
    Extra Reading (3)
    Matrices: geometrical
    Matrix Mathematics
    Homogenous matrices
    Community Projects (1)
    Tutorials (160)
    XNA 4.0 using C# (89)
    DirectX using C# (54)
    Series 1:Terrain (14)
    Series 2: Flightsim (19)
    Series 3: HLSL (19)
    Starting point
    HLSL Introduction
    Vertex Shader
    Shaded triangle
    Pixel Shader
    Textured Triangle
    Triangle Strip
    World transform
    Adding normals
    The first light
    Shadow mapping
    Render To Texture
    Projective texturing
    The first shadow
    Shaping the light
    Preshaders
    Multiple lights
    Adjusting Z values
    Finishing touch
    Short Tuts (2)
    DirectX using C++ (15)
    DirectX using VB (2)
    -- Expand all --


    Thank you!

    Support this site --
    any amount is welcome !

    Stay up-to-date

    I don't have the time to keep a News section, so stay informed about the updates by clicking on this RSS file!