Formeln

Showing posts with label Shader. Show all posts
Showing posts with label Shader. Show all posts

Thursday, April 25, 2013

Procedural Meshes: The Sphere

Introduction

This tutorial is one part of a series of tutorials about generating procedural meshes. See here for an outline.

In this tutorial I will show how to create a sphere mesh procedurally.

Formula

I use the following formula to create the vertices of the sphere:

x = radius * Cos(theta) * Cos(phi)
y = radius * Cos(theta) * Sin(phi)
z = radius * Sin(theta)

If you are looking at the origin an down the positive z-axis, theta is the angle between the x-axis and the line made from this angle. Theta runs in the interval from PI/2 to -PI/2 which corresponds to the latitude if you compare it to a globe:




If you look from top down on the sphere, phi is the angle between the x-axis and the line from the angle. Phi runs from 0 to 2 * PI, which corresponds to the longitude, compared with a globe:



If you fill in the values in radian for theta and phi, you will get the according point on the sphere.

From here on it is pretty straight forward: have a double nested for-loop and iterate from the top to the bottom of the sphere in the outer loop (theta) and create the vertices to on this circle in the inner loop by circling once around the y-axis (phi).

Creating the Vertex Buffer

This is, how it looks in source code. Please note, that I swap the y and z values when creating the vertices. I find it easier to do the math in a coordinate system where the z-axis points up, while in 3D coordinate systems used in computer graphics the y-axis points up.

int numVerticesPerRow = slices + 1;
int numVerticesPerColumn = stacks + 1;

numVertices = numVerticesPerRow * numVerticesPerColumn;

vertexStride = Marshal.SizeOf(typeof(Vector3)); // 12 bytes
int SizeOfVertexBufferInBytes = numVertices * vertexStride;

vertices = new DataStream(SizeOfVertexBufferInBytes, true, true);

float theta = 0.0f;
float phi = 0.0f;

float verticalAngularStride = (float)Math.PI / (float)stacks;
float horizontalAngularStride = ((float)Math.PI * 2) / (float)slices;

for (int verticalIt = 0; verticalIt < numVerticesPerColumn; verticalIt++)
{
  // beginning on top of the sphere:
  theta = ((float)Math.PI / 2.0f) - verticalAngularStride * verticalIt;

  for (int horizontalIt = 0; horizontalIt < numVerticesPerRow; horizontalIt++)
  {
    phi = horizontalAngularStride * horizontalIt;

    // position
    float x = radius * (float)Math.Cos(theta) * (float)Math.Cos(phi);
    float y = radius * (float)Math.Cos(theta) * (float)Math.Sin(phi);
    float z = radius * (float)Math.Sin(theta);

    Vector3 position = new Vector3(x, z, y);
    vertices.Write(position);
  }
}

vertices.Position = 0;

// create the vertex layout and buffer
var elements = new[] { 
    new InputElement("POSITION", 0, Format.R32G32B32_Float, 0)
};
layout = new InputLayout(DeviceManager.Instance.device, inputSignature, elements);


vertexBuffer = new SlimDX.Direct3D11.Buffer(DeviceManager.Instance.device,
    vertices,
    SizeOfVertexBufferInBytes,
    ResourceUsage.Default,
    BindFlags.VertexBuffer,
    CpuAccessFlags.None,
    ResourceOptionFlags.None,
    0);


Creating the Index Buffer

Just like in the tutorial for the grid mesh, I am iterating through the rows of vertices and create two triangles at each position.

numIndices = slices * stacks * 6;
indices = new DataStream(2 * numIndices, true, true);

for (int verticalIt = 0; verticalIt < stacks; verticalIt++)
{
    for (int horizontalIt = 0; horizontalIt < slices; horizontalIt++)
    {
        short lt = (short)(horizontalIt + verticalIt * (numVerticesPerRow));
        short rt = (short)((horizontalIt + 1) + verticalIt * (numVerticesPerRow));

        short lb = (short)(horizontalIt + (verticalIt + 1) * (numVerticesPerRow));
        short rb = (short)((horizontalIt + 1) + (verticalIt + 1) * (numVerticesPerRow));
     
        indices.Write(lt);
        indices.Write(rt);
        indices.Write(lb);

        indices.Write(rt);
        indices.Write(rb);
        indices.Write(lb);
    }
}

indices.Position = 0;

indexBuffer = new SlimDX.Direct3D11.Buffer(
    DeviceManager.Instance.device,
    indices,
    2 * numIndices,
    ResourceUsage.Default,
    BindFlags.IndexBuffer,
    CpuAccessFlags.None,
    ResourceOptionFlags.None,
    0);



Source Code

This is the source code of the sphere:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX.D3DCompiler;
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using System.Runtime.InteropServices;
using System.Drawing;

namespace Apparat.Renderables
{
    public class Sphere : Renderable
    {
        SlimDX.Direct3D11.Buffer vertexBuffer;
        SlimDX.Direct3D11.Buffer indexBuffer;
        DataStream vertices;
        DataStream indices;

        InputLayout layout;

        int numVertices = 0;
        int numIndices = 0;

        int vertexStride = 0;

        ShaderSignature inputSignature;
        EffectTechnique technique;
        EffectPass pass;

        Effect effect;
        EffectMatrixVariable tmat;
        EffectVectorVariable mCol;
        EffectVectorVariable wfCol;

        float radius = 0;

        public Sphere(float radius, int slices, int stacks )
        {
            try
            {
                using (ShaderBytecode effectByteCode = ShaderBytecode.CompileFromFile(
                    "Shaders/transformEffectWireframe.fx",
                    "Render",
                    "fx_5_0",
                    ShaderFlags.EnableStrictness,
                    EffectFlags.None))
                {
                    effect = new Effect(DeviceManager.Instance.device, effectByteCode);
                    technique = effect.GetTechniqueByIndex(0);
                    pass = technique.GetPassByIndex(0);
                    inputSignature = pass.Description.Signature;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            this.radius = radius;

            tmat = effect.GetVariableByName("gWVP").AsMatrix();
            mCol = effect.GetVariableByName("colorSolid").AsVector();
            wfCol = effect.GetVariableByName("colorWireframe").AsVector();
         
            mCol.Set(new Color4(1, 0, 1, 0));
            wfCol.Set(new Color4(1, 0, 0, 0));

            int numVerticesPerRow = slices + 1;
            int numVerticesPerColumn = stacks + 1;

            numVertices = numVerticesPerRow * numVerticesPerColumn;

            vertexStride = Marshal.SizeOf(typeof(Vector3)); // 12 bytes
            int SizeOfVertexBufferInBytes = numVertices * vertexStride;

            vertices = new DataStream(SizeOfVertexBufferInBytes, true, true);

            float theta = 0.0f;
            float phi = 0.0f;

            float verticalAngularStride = (float)Math.PI / (float)stacks;
            float horizontalAngularStride = ((float)Math.PI * 2) / (float)slices;

            for (int verticalIt = 0; verticalIt < numVerticesPerColumn; verticalIt++)
            {
                // beginning on top of the sphere:
                theta = ((float)Math.PI / 2.0f) - verticalAngularStride * verticalIt;

                for (int horizontalIt = 0; horizontalIt < numVerticesPerRow; horizontalIt++)
                {
                    phi = horizontalAngularStride * horizontalIt;

                    // position
                    float x = radius * (float)Math.Cos(theta) * (float)Math.Cos(phi);
                    float y = radius * (float)Math.Cos(theta) * (float)Math.Sin(phi);
                    float z = radius * (float)Math.Sin(theta);

                    Vector3 position = new Vector3(x, z, y);
                    vertices.Write(position);
                }
            }

            vertices.Position = 0;

            // create the vertex layout and buffer
            var elements = new[] { 
                new InputElement("POSITION", 0, Format.R32G32B32_Float, 0)
            };
            layout = new InputLayout(DeviceManager.Instance.device, inputSignature, elements);

            
            vertexBuffer = new SlimDX.Direct3D11.Buffer(DeviceManager.Instance.device,
                vertices,
                SizeOfVertexBufferInBytes,
                ResourceUsage.Default,
                BindFlags.VertexBuffer,
                CpuAccessFlags.None,
                ResourceOptionFlags.None,
                0);

            numIndices = slices * stacks * 6;
            indices = new DataStream(2 * numIndices, true, true);

            for (int verticalIt = 0; verticalIt < stacks; verticalIt++)
            {
                for (int horizontalIt = 0; horizontalIt < slices; horizontalIt++)
                {
                    short lt = (short)(horizontalIt + verticalIt * (numVerticesPerRow));
                    short rt = (short)((horizontalIt + 1) + verticalIt * (numVerticesPerRow));

                    short lb = (short)(horizontalIt + (verticalIt + 1) * (numVerticesPerRow));
                    short rb = (short)((horizontalIt + 1) + (verticalIt + 1) * (numVerticesPerRow));
                 
                    indices.Write(lt);
                    indices.Write(rt);
                    indices.Write(lb);

                    indices.Write(rt);
                    indices.Write(rb);
                    indices.Write(lb);
                }
            }

            indices.Position = 0;

            indexBuffer = new SlimDX.Direct3D11.Buffer(
                DeviceManager.Instance.device,
                indices,
                2 * numIndices,
                ResourceUsage.Default,
                BindFlags.IndexBuffer,
                CpuAccessFlags.None,
                ResourceOptionFlags.None,
                0);


        }

        public override void render()
        {
            Matrix ViewPerspective = CameraManager.Instance.ViewPerspective;

            tmat.SetMatrix(ViewPerspective);

            // configure the Input Assembler portion of the pipeline with the vertex data
            DeviceManager.Instance.context.InputAssembler.InputLayout = layout;
            DeviceManager.Instance.context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
            DeviceManager.Instance.context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, vertexStride, 0));
            DeviceManager.Instance.context.InputAssembler.SetIndexBuffer(indexBuffer, Format.R16_UInt, 0);

            technique = effect.GetTechniqueByName("Render");

            EffectTechniqueDescription techDesc;
            techDesc = technique.Description;

            for (int p = 0; p < techDesc.PassCount; ++p)
            {
                technique.GetPassByIndex(p).Apply(DeviceManager.Instance.context);
                DeviceManager.Instance.context.DrawIndexed(numIndices, 0, 0);
            }
        }

        public override void dispose()
        {

        }
    }
}

As I am using a different shader than before which sets an uniform color for the mesh and a color for the wireframe, I will post the complete code of the shader used here. I will use this shader also for the next tutorial in which I will show how to create other geometric primitives. I use the variables mCol and wfCol to set the colors of the mesh and the wireframe in the code above via the effect framework.


matrix gWVP;
float4 colorSolid;
float4 colorWireframe;

float4 VShader(float4 position : POSITION) : SV_POSITION
{
  return mul( position, gWVP);
}

float4 PShader(float4 position : SV_POSITION) : SV_Target
{
  return colorSolid;
}

float4 PShaderWireframe(float4 position : SV_POSITION) : SV_Target
{
  return colorWireframe;
}

RasterizerState SolidState
{
  FillMode = Solid;
};

RasterizerState WireframeState
{
  FillMode = Wireframe;
  SlopeScaledDepthBias = -0.5f;
};

technique10 Render
{
  pass P0
  {
    SetVertexShader( CompileShader( vs_4_0, VShader() ));
    SetGeometryShader( NULL );
    SetPixelShader( CompileShader( ps_4_0, PShader() ));
    SetRasterizerState(SolidState);
  }

  pass P1
  {
    SetVertexShader( CompileShader( vs_4_0, VShader() ));
    SetGeometryShader( NULL );
    SetPixelShader( CompileShader( ps_4_0, PShaderWireframe() ));
    SetRasterizerState(WireframeState);
  }
}



Conclusion


This picture shows a sphere created with 8 slices and 8 stacks:


This sphere was created with 32 slices and 32 stacks:


You can download the code to this tutorial here.

Thursday, April 18, 2013

Depth/Stencil Buffer

This tutorial is one part of a series of tutorials about generating procedural meshes. See here for an outline.

Problem

So far, we haven't cared about setting up and using the depth buffer. This results in the following problem, when rendering more than one object: the objects that is drawn last is painted over all other objects, regardless of their real order in 3D space.


In the picture above the grid is drawn first and then the cube is drawn. Because the cube is painted over the grid, the line of the gird marked in the red rectangle is not visible.


Conversely, if the grid is drawn last, the grid is always visible and the cube looks transparent. This looks cool when playing around with the camera, but is not exactly what we want in every situation ;)

The solution for painting objects in the right order to the screen according to their 3D positions is using a depth buffer.

Depth/Stencil Buffer 

A depth buffer, also called z-buffer, is a texture with the same size as the render target. The depth buffer is applied in one of the last stages of the pipeline, namely the output merger stage. What happens at the output merger stage?

To explain this, we need to take a step back in the rending pipeline and look at the rasterizer stage. (See here for the MSDN documentation on the Direct3D 11 graphics pipeline) When you provided some geometry and applied all the transformations to finally render your objects to the screen, your objects have to be rasterized. This means the scene gets sampled and discretized into pixels, or in other words: a raster is laid over your scene and each cell of this raster represents a pixel. This pixel is written to the render target.


At this point the depth buffer and the output merger stage comes into play. The depth buffer has the same height and width as you render target, because each pixel in the depth buffer corresponds to a pixel on the render target. When a object is rasterized into pixels and painted to the render target, the GPU has information about the depth of this pixel, or how far it is away from the camera. The depth buffer is for holding the depth of the current pixel on the render target. If the pixel of the current object in nearer to the camera than the pixel that is already at the according position, the old pixel is overwritten on the render target and the depth value in the depth buffer is updated to the new pixels depth. If the depth of a pixel is higher than an existing pixel, the old pixel will not be overwritten.
To sum up: the depth buffer keeps track of the depth of the pixels on the render target during the rasterization process. Only the nearest pixels of the scene make it to the final render target buffer.

As we don't need the stencil buffer right now, I won't go into much detail. Depth and stencil buffer are often combined in one texture. A common format is using 24 bit for the depth buffer and 8 bit for the stencil buffer.
The stencil is, as its name implies, a  template that can be applied in the output merger stage. This buffer can be used in various ways according to the flags set in the GPU. You can use it as a mask and don't allow rending to these pixels or you can use the buffer to count your write accesses to this pixel in one render cycle.

Creating the Depth/Stencil Buffer

Like I mentioned above, we need a texture for the depth buffer. This is initialized with the width and height of the control, we are rendering on. With this texture we can now create the depth/stencil buffer, which is in SlimDX a class called DepthStencilView:


Texture2D DSTexture = new Texture2D(
  device,
  new Texture2DDescription()
  {
    ArraySize = 1,
    MipLevels = 1,
    Format = Format.D32_Float,
    Width = form.ClientSize.Width,
    Height = form.ClientSize.Height,
    BindFlags = BindFlags.DepthStencil,
    CpuAccessFlags = CpuAccessFlags.None,
    SampleDescription = new SampleDescription(1, 0),
    Usage = ResourceUsage.Default
  }
);

depthStencil = new DepthStencilView(
  device,
  DSTexture,
  new DepthStencilViewDescription()
  {
    ArraySize = 0,
    FirstArraySlice = 0,
    MipSlice = 0,
    Format = Format.D32_Float,
    Dimension = DepthStencilViewDimension.Texture2D
  }
);

Setting the DepthStencil State

Now that we have set up the depth buffer, we need to create a depth/stencil state and assign it to the DepthStencilState of the output merger stage.

context.OutputMerger.DepthStencilState = DepthStencilState.FromDescription(
  device,
  new DepthStencilStateDescription()
  {
    DepthComparison = Comparison.Always,
    DepthWriteMask = DepthWriteMask.All,
    IsDepthEnabled = true,
    IsStencilEnabled = false
  }
);

context.OutputMerger.SetTargets(depthStencil, renderTarget);

DepthStencilStateDescription dssd = new DepthStencilStateDescription
{
  IsDepthEnabled = true,
  IsStencilEnabled = false,
  DepthWriteMask = DepthWriteMask.All,
  DepthComparison = Comparison.Less,
};

DepthStencilState depthStencilStateNormal;
depthStencilStateNormal = DepthStencilState.FromDescription(DeviceManager.Instance.device, dssd);
DeviceManager.Instance.context.OutputMerger.DepthStencilState = depthStencilStateNormal;

Putting it all together

This is the complete code of my method to create the depth/stencil buffer and to set the state:


public void CreateDepthStencilBuffer(System.Windows.Forms.Control form)
{
  Texture2D DSTexture = new Texture2D(
    device,
    new Texture2DDescription()
    {
      ArraySize = 1,
      MipLevels = 1,
      Format = Format.D32_Float,
      Width = form.ClientSize.Width,
      Height = form.ClientSize.Height,
      BindFlags = BindFlags.DepthStencil,
      CpuAccessFlags = CpuAccessFlags.None,
      SampleDescription = new SampleDescription(1, 0),
      Usage = ResourceUsage.Default
    }
  );

  depthStencil = new DepthStencilView(
    device,
    DSTexture,
    new DepthStencilViewDescription()
    {
      ArraySize = 0,
      FirstArraySlice = 0,
      MipSlice = 0,
      Format = Format.D32_Float,
      Dimension = DepthStencilViewDimension.Texture2D
    }
   );

  context.OutputMerger.DepthStencilState = DepthStencilState.FromDescription(
    device,
    new DepthStencilStateDescription()
    {
      DepthComparison = Comparison.Always,
      DepthWriteMask = DepthWriteMask.All,
      IsDepthEnabled = true,
      IsStencilEnabled = false
    }
  );

  context.OutputMerger.SetTargets(depthStencil, renderTarget);

  DepthStencilStateDescription dssd = new DepthStencilStateDescription
  {
    IsDepthEnabled = true,
    IsStencilEnabled = false,
    DepthWriteMask = DepthWriteMask.All,
    DepthComparison = Comparison.Less,
  };

  DepthStencilState depthStencilStateNormal;
  depthStencilStateNormal = DepthStencilState.FromDescription(DeviceManager.Instance.device, dssd);
  DeviceManager.Instance.context.OutputMerger.DepthStencilState = depthStencilStateNormal;
}


Clearing the Depth/Stencil Buffer

Because the GPU writes in every render cycle to the depth/stencil buffer, we need to clear this buffer in every render cycle. This is done with ClearDepthStencilView.

public void RenderScene()
{
  while (true)
  {
    fc.Count();
       
    DeviceManager dm = DeviceManager.Instance;
    dm.context.ClearDepthStencilView(dm.depthStencil, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0);
    dm.context.ClearRenderTargetView(dm.renderTarget, new Color4(0.75f, 0.75f, 0.75f));

    Scene.Instance.render();

    dm.swapChain.Present(syncInterval, PresentFlags.None);
  }
}


Camera

The next thing to do is to check, if you are setting the values for znear and zfar right, when setting up the perspective for the camera. When using the depth buffer, these values come into play in order to decide in which interval (from znear to zfar) objects are rendered.

I create my perspective matrix this way:

perspective = Matrix.PerspectiveFovLH((float)Math.PI / 4, 1.3f, 0.1f, 1000.0f);


Result

As you can see in the picture in the green rectangle, the grid is now rendered above the cube, if a line is in front of the cube. 


If you take a look at the right rectangle, you can see that the wireframe on the cube seems to be painted in dotted lines. But this is not how it is supposed to look. What is happening here is called z-buffer fighting. Because the cube and the wireframe are rendered on base of the same geometry, the output merger stage can't decide which pixel to render, because both pixels have the same depth.

To avoid this z-fighting, I add a depth bias to the wireframe in the rasterizer state of the shader:

RasterizerState WireframeState
{
  FillMode = Wireframe;
  SlopeScaledDepthBias = -0.5f;
};

Here is the documentation on MSDN on depth bias:
http://msdn.microsoft.com/en-us/library/windows/desktop/cc308048(v=vs.85).aspx

Now everything looks OK:



You can download the source code to this tutorial here.


Wednesday, April 17, 2013

Rendering a Wireframe over a Mesh

This tutorial is one part of a series of tutorials about generating procedural meshes. See here for an outline.

Rendering a wireframe over a given mesh is relatively simple and requires just a second pass in the shader and two states for the rasterizer.

So far we used an effect technique with one pass:

technique10 Render
{
  pass P0
  {
    SetVertexShader( CompileShader( vs_4_0, VShader() ));
    SetGeometryShader( NULL );
    SetPixelShader( CompileShader( ps_4_0, PShader() ));
  }
}



This is the shader code for rendering the wireframe over the mesh:


matrix gWVP;
float4 wireFrameColor;

struct VOut
{
  float4 position : SV_POSITION;
  float4 color : COLOR;
};

VOut VShader(float4 position : POSITION, float4 color : COLOR)
{
  VOut output;

  output.position = mul( position, gWVP);
  output.color = color;

  return output;
}

float4 PShader(float4 position : SV_POSITION, float4 color : COLOR) : SV_TARGET
{
  return color;
}

float4 PShaderWireframe(float4 position : SV_POSITION, float4 color : COLOR) : SV_TARGET
{
  return wireFrameColor;
}

RasterizerState WireframeState
{
  FillMode = Wireframe;
};

RasterizerState SolidState
{
  FillMode = Solid;
};

technique10 Render
{
  pass P0
  {
    SetVertexShader( CompileShader( vs_4_0, VShader() ));
    SetGeometryShader( NULL );
    SetPixelShader( CompileShader( ps_4_0, PShader() ));
    SetRasterizerState(SolidState);
  }

  pass P1
  {
    SetVertexShader( CompileShader( vs_4_0, VShader() ));
    SetGeometryShader( NULL );
    SetPixelShader( CompileShader( ps_4_0, PShaderWireframe() ));
    SetRasterizerState(WireframeState);
  }
}

In the first pass P0 I set the fillmode state to Solid, to render the mesh. In the second pass P1 the fillmode state is set to Wireframe. Observe, that while I use in P0 and P1 the same vertex shader (which is kind of obious, because the wireframe needs the same transformations as the solid mesh), but use a different pixel shader, called PShaderWireframe. In this second pixel shader I set the color of the wireframe pixels to the variable wireFrameColor, to render the wireframe in a given color.

The variable wireFrameColor is set via the effect framework in the renderable object. This way there is no need to recompile the shader in case I want to use a different color for the wireframe.

In the code for the renderable I have to declare a variable of the type EffectVectorVariable:

EffectVectorVariable wireFrameColor;

This variable is bound to the shader variable in the constructor of the renderable object with this statement:

wireFrameColor = effect.GetVariableByName("wireFrameColor").AsVector();
Vector4 col = new Vector4(0, 0, 0, 1);
wireFrameColor.Set(col);

I just need to set this variable once in the constructor. If you want to do things like changing the color at runtime, you need to put the asignment wireFrameColor.Set(col) into the render method to make sure, it gets called in every frame.

Result




You can download the source code for this tutorial here.

The Color Cube: Vertices with Color

This tutorial is one part of a series of tutorials about generating procedural meshes. See here for an outline.

Vertices

So far I used simple vertices with only information about position in it. The vertex buffer consisted of an array of Vector3 structs. As I mentioned earlier, vertices can be more complex objects, holding further information about color, normals, texture coordinates and so on. In previous tutorials I used the pixel shader and hardcoded the color of the the pixel of an object:

float4 PShader(float4 position : SV_POSITION) : SV_Target
{
  return float4(0.0f, 1.0f, 0.0f, 1.0f);
}

This simple pixel shader just colors every of an object lime green, as the first three values of the float4 struct correspond to the RGB color model (standing for red, greed, blue).

First, we need a vertex structure, that can hold additional information about color:

[StructLayout(LayoutKind.Sequential)]
public struct Vertex
{
  public Vector3 Position;
  public int Color;

  public Vertex(Vector3 position, int color)
  {
    this.Position = position;
    this.Color = color;
  }
}

From here on we need to create a DataStream and write new vertices to this stream like in this statement:

vertices.Write(new Vertex(new Vector3(1.0f, 1.0f, 1.0f), Color.FromArgb(255, 0, 0).ToArgb()));

We create a new vertex at position x = 1, y = 1 and z = 1 and we tell the Color struct that we want the color red.

We are not done yet. The vertex buffer is just a stream of bytes and we need to tell our device how to interpret this data. This is exactly was the InputLayout is made for.
In previous tutorials I used this InputLayout:

var elements = new[] { new InputElement("POSITION", 0, Format.R32G32B32_Float, 0) };
layout = new InputLayout(DeviceManager.Instance.device, inputSignature, elements);

The InputLayout needs an array of InputElements. The InputElement array so far just consisted of the one element defined above, holding only information about the position. So we need to add a further InputElement for color:

var elements = new[] { 
  new InputElement("POSITION", 0, Format.R32G32B32_Float, 0),
  new InputElement("COLOR", 0, Format.B8G8R8A8_UNorm, 12, 0) 
};
layout = new InputLayout(DeviceManager.Instance.device, inputSignature, elements);

The second InputElement for color also gives information about its offset from the beginning of InputElement structure. As the first InputElement consists of three floats and one float is four bytes big, the color InputElement starts at byte 12.

Just like before, we need to set the input layout in the device before making the draw call:

DeviceManager.Instance.context.InputAssembler.InputLayout = layout;

We also have to adjust the shader, but I will come to this later. First let us create some geometry to render.


Color Cube

I will use the color cube as an example and this is what we are aiming at:


The cube consists of 8 vertices and each has a different color. Pixels that lie on the surface of the cube are being interpolated according to their position in the corresponding triangle.

I define the vertices of the cube, so that the center of the cube corresponds with the origin of its local coordinate system. Shorter: the center of the cube is (0,0,0).




In the center of the cube is the coordinate frame. A widely used color scheme is to map the axis to RGB color model: x-axis: red, y-axis: green, z-axis: blue. So what is up with those plusses and minusses? In order to keep the graphic clear, I omitted the values of the positions and depicted only the signs of the vector elements. Take a look at the x-axis: every vertex of the cube that lies in the positive x-axis, has a plus sign (all vertices on the right) and every vertex in the negative x-axis (all vertices on the left) have a negative sign.

And what is the purpose of this? If I have a negative sign at the position element (x,y or z), I set the corresponding color element (R,G or B) value to zero and if I have a positive sign, I set the color element to 255. This
is how I fill the vertex buffer and I colored the corresponding values green and red, to make this pattern more visible:


Now that we have set up the vertex buffer it is time to set up the index buffer. This picture depicts the order in which I have defined the vertices:


The sequence of vertex definitions is completely arbitrary, but once we have defined the vertices we need to stay consistent with this definition to get the triangles rendered in a right way. The default way DirectX handles triangle definition is by enumerating the vertices clockwise. If you are looking at a particular side, you have to enumerate the indices in the right order:



Look at the picture above and look at the case when looking straight at the top of the cube. We have indices 0,1,2 and 3. The triangulation I chose is: (0,1,2) and (2,3,0). This is also arbitrary as you also could  triangulate this side with (3,0,1) and (1,2,3). As long as you enumerate the indices in a clockwise order you get a valid triangulation.

I fill the index buffer corresponding to the picture above:

// Cube has 6 sides: top, bottom, left, right, front, back

// top
indices.WriteRange(new short[] { 0, 1, 2 });
indices.WriteRange(new short[] { 2, 3, 0 });

// right
indices.WriteRange(new short[] { 0, 5, 6 });
indices.WriteRange(new short[] { 6, 1, 0 });

// left
indices.WriteRange(new short[] { 2, 7, 4 });
indices.WriteRange(new short[] { 4, 3, 2 });

// front
indices.WriteRange(new short[] { 1, 6, 7 });
indices.WriteRange(new short[] { 7, 2, 1 });

// back
indices.WriteRange(new short[] { 3, 4, 5 });
indices.WriteRange(new short[] { 5, 0, 3 });

// bottom
indices.WriteRange(new short[] { 6, 5, 4 });
indices.WriteRange(new short[] { 4, 7, 6 });


Source Code

Putting everything together, this is the complete source code for the ColorCube Renderable:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using SlimDX.D3DCompiler;
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using System.Runtime.InteropServices;

namespace Apparat.Renderables
{
    public class ColorCube : Renderable
    {
        ShaderSignature inputSignature;
        EffectTechnique technique;
        EffectPass pass;

        Effect effect;

        InputLayout layout;
        SlimDX.Direct3D11.Buffer vertexBuffer;
        SlimDX.Direct3D11.Buffer indexBuffer;
        DataStream vertices;
        DataStream indices;

        int vertexStride = 0;
        int numVertices = 0;
        int indexStride = 0;
        int numIndices = 0;

        int vertexBufferSizeInBytes = 0;
        int indexBufferSizeInBytes = 0;

        EffectMatrixVariable tmat;

        [StructLayout(LayoutKind.Sequential)]
        public struct Vertex
        {
            public Vector3 Position;
            public int Color;

            public Vertex(Vector3 position, int color)
            {
                this.Position = position;
                this.Color = color;
            }
        }

        public ColorCube()
        {
            try
            {
                using (ShaderBytecode effectByteCode = ShaderBytecode.CompileFromFile(
                    "Shaders/colorEffect.fx",
                    "Render",
                    "fx_5_0",
                    ShaderFlags.EnableStrictness,
                    EffectFlags.None))
                {
                    effect = new Effect(DeviceManager.Instance.device, effectByteCode);
                    technique = effect.GetTechniqueByIndex(0);
                    pass = technique.GetPassByIndex(0);
                    inputSignature = pass.Description.Signature;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            var elements = new[] { 
                new InputElement("POSITION", 0, Format.R32G32B32_Float, 0),
                new InputElement("COLOR", 0, Format.B8G8R8A8_UNorm, 12, 0) 
            };
            layout = new InputLayout(DeviceManager.Instance.device, inputSignature, elements);


            tmat = effect.GetVariableByName("gWVP").AsMatrix();

            // half length of an edge
            float offset = 0.5f;

            vertexStride = Marshal.SizeOf(typeof(Vertex)); // 16 bytes
            numVertices = 8;
            vertexBufferSizeInBytes = vertexStride * numVertices;

            vertices = new DataStream(vertexBufferSizeInBytes, true, true);

            vertices.Write(new Vertex(new Vector3(+offset, +offset, +offset), Color.FromArgb(255, 255, 255).ToArgb())); // 0
            vertices.Write(new Vertex(new Vector3(+offset, +offset, -offset), Color.FromArgb(255, 255, 000).ToArgb())); // 1
            vertices.Write(new Vertex(new Vector3(-offset, +offset, -offset), Color.FromArgb(000, 255, 000).ToArgb())); // 2
            vertices.Write(new Vertex(new Vector3(-offset, +offset, +offset), Color.FromArgb(000, 255, 255).ToArgb())); // 3

            vertices.Write(new Vertex(new Vector3(-offset, -offset, +offset), Color.FromArgb(000, 000, 255).ToArgb())); // 4
            vertices.Write(new Vertex(new Vector3(+offset, -offset, +offset), Color.FromArgb(255, 000, 255).ToArgb())); // 5
            vertices.Write(new Vertex(new Vector3(+offset, -offset, -offset), Color.FromArgb(255, 000, 000).ToArgb())); // 6
            vertices.Write(new Vertex(new Vector3(-offset, -offset, -offset), Color.FromArgb(000, 000, 000).ToArgb())); // 7

            vertices.Position = 0;

            vertexBuffer = new SlimDX.Direct3D11.Buffer(
               DeviceManager.Instance.device,
               vertices,
               vertexBufferSizeInBytes,
               ResourceUsage.Default,
               BindFlags.VertexBuffer,
               CpuAccessFlags.None,
               ResourceOptionFlags.None,
               0);

            numIndices = 36;
            indexStride = Marshal.SizeOf(typeof(short)); // 2 bytes
            indexBufferSizeInBytes = numIndices * indexStride;

            indices = new DataStream(indexBufferSizeInBytes, true, true);

            // Cube has 6 sides: top, bottom, left, right, front, back

            // top
            indices.WriteRange(new short[] { 0, 1, 2 });
            indices.WriteRange(new short[] { 2, 3, 0 });

            // right
            indices.WriteRange(new short[] { 0, 5, 6 });
            indices.WriteRange(new short[] { 6, 1, 0 });

            // left
            indices.WriteRange(new short[] { 2, 7, 4 });
            indices.WriteRange(new short[] { 4, 3, 2 });

            // front
            indices.WriteRange(new short[] { 1, 6, 7 });
            indices.WriteRange(new short[] { 7, 2, 1 });

            // back
            indices.WriteRange(new short[] { 3, 4, 5 });
            indices.WriteRange(new short[] { 5, 0, 3 });

            // bottom
            indices.WriteRange(new short[] { 6, 5, 4 });
            indices.WriteRange(new short[] { 4, 7, 6 });

            indices.Position = 0;

            indexBuffer = new SlimDX.Direct3D11.Buffer(
                DeviceManager.Instance.device,
                indices,
                indexBufferSizeInBytes,
                ResourceUsage.Default,
                BindFlags.IndexBuffer,
                CpuAccessFlags.None,
                ResourceOptionFlags.None,
                0);

        }

        public override void render()
        {
            Matrix ViewPerspective = CameraManager.Instance.ViewPerspective;
            tmat.SetMatrix(ViewPerspective);

            DeviceManager.Instance.context.InputAssembler.InputLayout = layout;
            DeviceManager.Instance.context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
            DeviceManager.Instance.context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, vertexStride, 0));
            DeviceManager.Instance.context.InputAssembler.SetIndexBuffer(indexBuffer, Format.R16_UInt, 0);

            technique = effect.GetTechniqueByName("Render");

            EffectTechniqueDescription techDesc;
            techDesc = technique.Description;

            for (int p = 0; p < techDesc.PassCount; ++p)
            {
                technique.GetPassByIndex(p).Apply(DeviceManager.Instance.context);
                DeviceManager.Instance.context.DrawIndexed(numIndices, 0, 0);
            }
        }

        public override void dispose()
        {
            effect.Dispose();
            inputSignature.Dispose();
            vertexBuffer.Dispose();
            layout.Dispose();
        }
    }
}



Shader

Like I mentioned above, we also have to modify our shader in order to render the color of a vertex:

matrix gWVP;

struct VOut
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};

VOut VShader(float4 position : POSITION, float4 color : COLOR)
{
    VOut output;

    output.position = mul( position, gWVP);
    output.color = color;

    return output;
}

float4 PShader(float4 position : SV_POSITION, float4 color : COLOR) : SV_TARGET
{
    return color;
}

RasterizerState WireframeState
{
    FillMode = Wireframe;
    CullMode = None;
    FrontCounterClockwise = false;
};

technique10 Render
{
 pass P0
 {
  SetVertexShader( CompileShader( vs_4_0, VShader() ));
  SetGeometryShader( NULL );
  SetPixelShader( CompileShader( ps_4_0, PShader() ));
  //SetRasterizerState(WireframeState);
 }
}

Not much going on in the vertex shader VShader. The position of the vertex is multiplied with the WorldViewPerspective matrix from our camera to transform it to the right screen position and the color of the vertex is just handed through to the output of the shader.

Well, something is new. Take a look at the vertex shaders used in previous tutorials:

float4 VShader(float4 position : POSITION) : SV_POSITION
{
  return mul( position, gWVP);
}

This shader performed the above mentioned transformation from the local coordinate system of the model to the screen space and it returned a float4 structure.

Compare this to the new vertex shader. This has as output a new defined struct called VOut. To be able to hand down the color information of the vertex to the pixel shader, we need to have a structure, that also holds the color information.


Result

Now we can render vertices with color and get a nice color cube:


In the next tutorial I will show how to render a wireframe over this colored cube. If you download the code and play around with this example, you will notice that the grid will not be rendered over the cube even if it is in between the camera and the cube. This is because we haven't set up a depth buffer by now and this will be addressed in a further tutorial.


You can download the source code to this tutorial here.

Tuesday, March 19, 2013

Setting Transformations in a Shader with the Effect Framework

In the previous tutorial I showed how to set a transformation in a shader via a Contant Buffer, resulting
in a rotating triangle. In this tutorial I will implement the same functionality with the Effect Framework.

Triangle Source Code


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX.D3DCompiler;
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;

namespace Apparat.Renderables
{
    public class TriangleEF : Renderable
    {
        ShaderSignature inputSignature;
        EffectTechnique technique;
        EffectPass pass;

        Effect effect;

        InputLayout layout;
        SlimDX.Direct3D11.Buffer vertexBuffer;

        EffectMatrixVariable tmat;

        public TriangleEF()
        {
            try
            {
                using (ShaderBytecode effectByteCode = ShaderBytecode.CompileFromFile(
                    "transformEffect.fx",
                    "Render",
                    "fx_5_0",
                    ShaderFlags.EnableStrictness,
                    EffectFlags.None))
                {
                    effect = new Effect(DeviceManager.Instance.device, effectByteCode);
                    technique = effect.GetTechniqueByIndex(0);
                    pass = technique.GetPassByIndex(0);
                    inputSignature = pass.Description.Signature;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            tmat = effect.GetVariableByName("gWVP").AsMatrix();

            // create test vertex data, making sure to rewind the stream afterward
            var vertices = new DataStream(12 * 3, true, true);
            vertices.Write(new Vector3(0.0f, 0.5f, 0.5f));
            vertices.Write(new Vector3(0.5f, -0.5f, 0.5f));
            vertices.Write(new Vector3(-0.5f, -0.5f, 0.5f));
            vertices.Position = 0;

            // create the vertex layout and buffer
            var elements = new[] { new InputElement("POSITION", 0, Format.R32G32B32_Float, 0) };
            layout = new InputLayout(DeviceManager.Instance.device, inputSignature, elements);
            vertexBuffer = new SlimDX.Direct3D11.Buffer(
                DeviceManager.Instance.device,
                vertices,
                12 * 3,
                ResourceUsage.Default,
                BindFlags.VertexBuffer,
                CpuAccessFlags.None,
                ResourceOptionFlags.None,
                0);
        }

        public override void dispose()
        {
            effect.Dispose();
            inputSignature.Dispose();
            vertexBuffer.Dispose();
            layout.Dispose();
        }

        float rot = 0.0f;
        Matrix rotMat;

        public override void render()
        {
            rot += 0.01f;
            rotMat = Matrix.RotationY(rot);
            tmat.SetMatrix(rotMat);
           
            // configure the Input Assembler portion of the pipeline with the vertex data
            DeviceManager.Instance.context.InputAssembler.InputLayout = layout;
            DeviceManager.Instance.context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
            DeviceManager.Instance.context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, 12, 0));
            
            technique = effect.GetTechniqueByName("Render");

            EffectTechniqueDescription techDesc;
            techDesc = technique.Description;

            for (int p = 0; p < techDesc.PassCount; ++p)
            {
                technique.GetPassByIndex(p).Apply(DeviceManager.Instance.context);
                DeviceManager.Instance.context.Draw(3, 0);
            }
        }
    }
}

Shader Source Code


matrix gWVP;

float4 VShader(float4 position : POSITION) : SV_POSITION
{
 return mul( position, gWVP);
}

float4 PShader(float4 position : SV_POSITION) : SV_Target
{
 return float4(0.0f, 0.0f, 1.0f, 1.0f);
}

technique10 Render
{
 pass P0
 {
  SetVertexShader( CompileShader( vs_4_0, VShader() ));
  SetGeometryShader( NULL );
  SetPixelShader( CompileShader( ps_4_0, PShader() ));
 }
}

Explaining the Shader Source Code

In contrast to the previous shader in the last tutorial I do not declare the matrix variable gWVP in a
ConstantBuffer but directly as a matrix.

Also. when using the Effect Framework you have to define a Technique with at least one Pass:


technique10 Render
{
 pass P0
 {
  SetVertexShader( CompileShader( vs_4_0, VShader() ));
  SetGeometryShader( NULL );
  SetPixelShader( CompileShader( ps_4_0, PShader() ));
 }
}

The Technique is your interface to your shader from your code and in the Pass the shaders are set. I will explain in the next section how to interface with your shader.

Explaining the Triangle Source Code

In the last tutorial you had to load the VertexShader and the PixelShader seperately. With the Effect Framework you just have to load the ShaderBytecode for the effect:

try
{
  using (ShaderBytecode effectByteCode = ShaderBytecode.CompileFromFile(
    "transformEffect.fx",
    "Render",
    "fx_5_0",
    ShaderFlags.EnableStrictness,
    EffectFlags.None))
  {
    effect = new Effect(DeviceManager.Instance.device, effectByteCode);
    technique = effect.GetTechniqueByIndex(0);
    pass = technique.GetPassByIndex(0);
    inputSignature = pass.Description.Signature;
  }
}
catch (Exception ex)
{
  Console.WriteLine(ex.ToString());
}

tmat = effect.GetVariableByName("gWVP").AsMatrix();

When compiling the ShaderBytecode you have to set the name of the Technique as a parameter, in this case "Render". You have to create the effect by calling the constructor of the Effect Class with the device and the ShaderBytecode as parameters. Also you have access the technique and pass to get the InputSignature of the shader.

In order to access the matrix variable in your shader, you have to use the getVariableByName function of the effect. The matrix from the effect file is asigned to a matrix called tmat, which is of the type EffectMatrixVariable. We will use the variable tmat again in the render function of the triangle to set the transformation of the triangle:

public override void render()
{
  rot += 0.01f;
  rotMat = Matrix.RotationY(rot);
  tmat.SetMatrix(rotMat);
           
  // configure the Input Assembler portion of the pipeline with the vertex data
  DeviceManager.Instance.context.InputAssembler.InputLayout = layout;
  DeviceManager.Instance.context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
  DeviceManager.Instance.context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, 12, 0));
            
  technique = effect.GetTechniqueByName("Render");

  EffectTechniqueDescription techDesc;
  techDesc = technique.Description;

  for (int p = 0; p < techDesc.PassCount; ++p)
  {
    technique.GetPassByIndex(p).Apply(DeviceManager.Instance.context);
    DeviceManager.Instance.context.Draw(3, 0);
  }
}

The statement tmat.SetMatrix(rotMat) sets the variable for the transformation matrix in the effect.

Now we get a rotating triangle again:


Observe, that the triangle is rotating counter-clockwise, while the triangle in the previous tutorial was rotating clockwise. As far as i know, DirectX uses per default a left-handed coordinate system and the triangle should rotate clockwise with positively growing values for rotation around the y-axis. This can be resolved by transposing the matrix before passing it to the Effect Framework: tmat.SetMatrix(Matrix.Transpose(rotMat)) and the triangle is rotating clockwise again.

You can download the source code here.


Saturday, November 10, 2012

Setting Transformations in a Shader with a Constant Buffer

In this tutorial I will show you, how you can set Transformations in a Shader via the Constant Buffer.
We will render a Triangle and perform a rotation on it while rendering.

Triangle Source Code


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX.D3DCompiler;
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;

namespace Apparat.Renderables
{
    public class TriangleCB : Renderable
    {
        ShaderSignature inputSignature;
        VertexShader vertexShader;
        PixelShader pixelShader;

        InputLayout layout;
        SlimDX.Direct3D11.Buffer vertexBuffer;

        public TriangleCB()  
        {

            #region shader and triangle

            try
            {
                // load and compile the vertex shader
                using (var bytecode = ShaderBytecode.CompileFromFile("transform.fx", "VShader", "vs_4_0", ShaderFlags.None, EffectFlags.None))
                {
                    inputSignature = ShaderSignature.GetInputSignature(bytecode);
                    vertexShader = new VertexShader(DeviceManager.Instance.device, bytecode);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            // load and compile the pixel shader
            using (var bytecode = ShaderBytecode.CompileFromFile("transform.fx", "PShader", "ps_4_0", ShaderFlags.None, EffectFlags.None))
            {
                pixelShader = new PixelShader(DeviceManager.Instance.device, bytecode);
            }

            // create test vertex data, making sure to rewind the stream afterward
            var vertices = new DataStream(12 * 3, true, true);
            vertices.Write(new Vector3(0.0f, 0.5f, 0.5f));
            vertices.Write(new Vector3(0.5f, -0.5f, 0.5f));
            vertices.Write(new Vector3(-0.5f, -0.5f, 0.5f));
            vertices.Position = 0;

            // create the vertex layout and buffer
            var elements = new[] { new InputElement("POSITION", 0, Format.R32G32B32_Float, 0) };
            layout = new InputLayout(DeviceManager.Instance.device, inputSignature, elements);
            vertexBuffer = new SlimDX.Direct3D11.Buffer(
                DeviceManager.Instance.device,
                vertices,
                12 * 3,
                ResourceUsage.Default,
                BindFlags.VertexBuffer,
                CpuAccessFlags.None,
                ResourceOptionFlags.None,
                0);
            #endregion
        }

        public override void dispose()
        {
            pixelShader.Dispose();
            vertexShader.Dispose();
            inputSignature.Dispose();
        }

        float rot = 0.0f;
        Matrix rotMat; 

        public override void render()
        {
            rot += 0.01f;
            rotMat = Matrix.RotationY(rot);

            var matStream = new DataStream(64, true, true);
            matStream.Write(rotMat);
            matStream.Position = 0;

            using (SlimDX.Direct3D11.Buffer matBuffer = new SlimDX.Direct3D11.Buffer(DeviceManager.Instance.device,     //Device
                                                             matStream, //Stream
                                                             64,         // Size                
                                                                // Flags
                                                             ResourceUsage.Dynamic,
                                                             BindFlags.ConstantBuffer,
                                                             CpuAccessFlags.Write,
                                                             ResourceOptionFlags.None,
                                                             4))
            {
                DeviceManager.Instance.context.VertexShader.SetConstantBuffer(matBuffer, 0);
            }

            

            // configure the Input Assembler portion of the pipeline with the vertex data
            DeviceManager.Instance.context.InputAssembler.InputLayout = layout;
            DeviceManager.Instance.context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
            DeviceManager.Instance.context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, 12, 0));

            //// set the shaders
            DeviceManager.Instance.context.VertexShader.Set(vertexShader);
            DeviceManager.Instance.context.PixelShader.Set(pixelShader);

            // render the triangle
            DeviceManager.Instance.context.Draw(3, 0);
        }


    }
}

Shader Source Code


cbuffer ConstBuffer : register(c0)
{
 float4x4 gWVP;
}

float4 VShader(float4 position : POSITION) : SV_POSITION
{
 return mul( position, gWVP);
}

float4 PShader(float4 position : SV_POSITION) : SV_Target
{
 return float4(1.0f, 0.0f, 0.0f, 1.0f);
}

Explanation of the Shader Source Code

In order to set transformations, we need to have a variable in the Shader to assign the transformation.
In this shader I call the variable gWVP and it is declared in the ConstantBuffer:


cbuffer ConstBuffer : register(c0)
{
 float4x4 gWVP;
}


To apply the transformation to each vertex of our triangle, we have to apply a Multiplication with
the Transformation Matrix.

float4 VShader(float4 position : POSITION) : SV_POSITION
{
 return mul( position, gWVP);
}


Explanation of the Triangle Source Code

If you compare the Constructor of the TriangleCB class above with the Constructor of the Triangle class
in the "Rendering a Triangle" Tutorial, everything remains the same:

We create a VertexShader, a Pixel Shader, we write the Vertices of the Triangle to a DataStream and create a VertexBuffer.

I added two Variables for the rotation of the Triangle and a Matrix to hold the Transformation Matrix:


float rot = 0.0f;
Matrix rotMat; 

The interesting part happens in the render Function of our Triangle. Let's have a short look at our original Version of our Triangle:

public override void render()
{
  // configure the Input Assembler portion of the pipeline with the vertex data
  DeviceManager.Instance.context.InputAssembler.InputLayout = layout;
  DeviceManager.Instance.context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
  DeviceManager.Instance.context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, 12, 0));

  // set the shaders
  DeviceManager.Instance.context.VertexShader.Set(vertexShader);
  DeviceManager.Instance.context.PixelShader.Set(pixelShader);

  // render the triangle
  DeviceManager.Instance.context.Draw(3, 0);
}

The Input Assembler Stage is set with an InputLayout, we provide a Primitive Topology and the Vertex Buffer is set. The Vertex and Pixel Shader are set in the Device and we can finally draw the Triangle.

Our new render function looks like this:



public override void render()
{
  rot += 0.01f;
  rotMat = Matrix.RotationY(rot);

  var matStream = new DataStream(64, true, true);
  matStream.Write(rotMat);
  matStream.Position = 0;

  using (SlimDX.Direct3D11.Buffer matBuffer = new SlimDX.Direct3D11.Buffer(
    DeviceManager.Instance.device,     //Device
    matStream, //Stream
    64,         // Size                
    // Flags
    ResourceUsage.Dynamic,
    BindFlags.ConstantBuffer,
    CpuAccessFlags.Write,
    ResourceOptionFlags.None,
    4)){             DeviceManager.Instance.context.VertexShader.SetConstantBuffer(matBuffer, 0);
  }

  // configure the Input Assembler portion of the pipeline with the vertex data
  DeviceManager.Instance.context.InputAssembler.InputLayout = layout;
  DeviceManager.Instance.context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
  DeviceManager.Instance.context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, 12, 0));

  // set the shaders
  DeviceManager.Instance.context.VertexShader.Set(vertexShader);
  DeviceManager.Instance.context.PixelShader.Set(pixelShader);

  // render the triangle
  DeviceManager.Instance.context.Draw(3, 0);
}

The lower part of the Source Code is exactly the same. In the upper part I add 0.01f of rotation in every frame. Then this rotation value is used to create a Rotation Matrix, that performs a Rotation around the Y-Axis:

rot += 0.01f;
rotMat = Matrix.RotationY(rot);

Next I create a DataStream, to write the Rotation Matrix to:

var matStream = new DataStream(64, true, true);
matStream.Write(rotMat);
matStream.Position = 0;

The SlimDX DataStream Class has the following Constructor:

public DataStream(long sizeInBytes, bool canRead, bool canWrite);

So when creating the DataStream the Size of the Matrix in Bytes has to be given as a Parameter.
A Matrix is a struct with 4x4 float, a float consists of 4 Bytes, so the Matrix struct has the size of 64 Bytes, thus we have to create the DataStream with a size of 64 Bytes. And we assign the Matrix rotMat by writing it to the stream. Finally we have to rewind the Stream by setting its Position to 0.

Finally we have to assign the Transformation Matrix to the VertexShader:

using (SlimDX.Direct3D11.Buffer matBuffer = new SlimDX.Direct3D11.Buffer(DeviceManager.Instance.device, //Device
  matStream, //Stream
  64,        // Size                
  // Flags
  ResourceUsage.Dynamic,
  BindFlags.ConstantBuffer,
  CpuAccessFlags.Write,
  ResourceOptionFlags.None,
  4))
{
  DeviceManager.Instance.context.VertexShader.SetConstantBuffer(matBuffer, 0);
}

This is done by creating a Buffer that holds the DataStream with the Data of the Rotation Matrix. Then we use this Buffer to set the ConstantBuffer of the VertexShader.

When Creating the Triangle, adding it to the Scene and let the Program run, you a get a rotating red Triangle:





You can download the Project here:
http://apparat.codeplex.com/SourceControl/changeset/9d048db48302