Formeln

Showing posts with label Primitives. Show all posts
Showing posts with label Primitives. Show all posts

Tuesday, March 19, 2013

Rendering a Grid with the LineList Primitive

In the next tutorials I am going to integrate a class for a camera. In order to have an orientation where we are going with the camera it is common to render a grid as reference.

Source Code of the Grid Renderable


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 Grid : Renderable
    {
        SlimDX.Direct3D11.Buffer vertexBuffer;
        DataStream vertices;
        
        InputLayout layout;

        int numVertices = 0;

        ShaderSignature inputSignature;
        EffectTechnique technique;
        EffectPass pass;

        Effect effect;
        EffectMatrixVariable tmat;


        public Grid(int cellsPerSide, float cellSize)
        {
            try
            {
                using (ShaderBytecode effectByteCode = ShaderBytecode.CompileFromFile(
                    "transformEffectRasterizer.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();
          

            int numLines = cellsPerSide+1;
            float lineLength = cellsPerSide * cellSize;

            float xStart = -lineLength / 2.0f;
            float yStart = -lineLength / 2.0f;

            float xCurrent = xStart;
            float yCurrent = yStart;

            numVertices = 2 * 2 * numLines;
            int SizeInBytes = 12 * numVertices;

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

            for (int y = 0; y < numLines; y++)
            {
                vertices.Write(new Vector3(xCurrent, 0, yStart));
                vertices.Write(new Vector3(xCurrent, 0, yStart + lineLength));
                xCurrent += cellSize;
            }

            for (int x = 0; x < numLines; x++)
            {
                vertices.Write(new Vector3(xStart, 0, yCurrent));
                vertices.Write(new Vector3(xStart + lineLength, 0, yCurrent));
                yCurrent += cellSize;
            }

            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, SizeInBytes, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
            

        }

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

            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.LineList;
            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(numVertices, 0);
            }
            
        }

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

The constructor of the grid takes the number of cells per side and the cell size as arguments. In the constructor the vertices of the grid are created. I have arranged the grid in a way, that the center of the grid
corresponds with the origin of the grids local coordinate system. If you compare this code to the code for the  triangle used in the previous tutorial, only the creation of the vertices differs.

The Render Method

This is the render method of the triangle of the last tutorial. The transformation matrix of the triangle is set via the Effect Framework.

public override void render()
{
  rot += 0.01f;
  rotMat = Matrix.RotationY(rot);
  tmat.SetMatrix(Matrix.Transpose(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);
  }
}

Compare this to the render method of the grid:

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

  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.LineList;
  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(numVertices, 0);
  }
}
Here the transformation matrix, called ViewPerspective is set to the Identity Matrix, resulting in no transformation. This is the point, where the transformation from the camera will come into play in the next tutorial.

While we could call in the triangle class the Draw method with 3 vertices, we have to use for the grid class a variable called numVertices, as the vertices of the lines are created in the constructor and depend on the number of cells of our grid.

The next thing to note is that the primitive typology for the triangle was PrimitiveTopology.TriangleList and in the primitive typology for the grid is PrimitiveTopology.LineList.

The reference to the SlimDX Primitive Topology Enumeration is here:
http://slimdx.org/docs/html/T_SlimDX_Direct3D11_PrimitiveTopology.htm

The most common primitives are:

  • PointList
  • LineList
  • LineStrip
  • TriangleList
  • TriangleStrip
We have used the LineList and the TriangleList so far.

Result

So far, we get the following picture, when compiling and executing the code:


The result is quite sobering, as we just see an additional line in the center of the window. This is because, the view is aligned with the horizontal plane and we see the grid from the side.
You can try to rotate the grid programmatically like in the triangle class. Hint:  Matrix.RotationX(float angle) is your friend.
In the next tutorial I will introduce an Orbit Camera, that allows you to zoom and rotate around the origin of the global coordinate system.

You can download the source code to this tutorial here.

Saturday, May 19, 2012

Rendering a Triangle: Cleaning Up

The SlimDX tutorial about rendering a triangle puts all the code for setting up the device and the swapchain
into one file. Let's organize the code some more by creating a Class for the Triangle. I added a Folder
for Renderables, where I will place all further Classes, that are responsible for holding the resources and methods to render things.

First let's copy and paste the code for rendering the triangle into the contructor of the Triangle class a method called render. After adding the needed using directices the Triangle class looks like this:




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
{
    class Triangle
    {
        ShaderSignature inputSignature;
        VertexShader vertexShader;
        PixelShader pixelShader;

        public Triangle()
        {
            
            #region shader and triangle


            // load and compile the vertex shader
            using (var bytecode = ShaderBytecode.CompileFromFile("triangle.fx", "VShader", "vs_4_0", ShaderFlags.None, EffectFlags.None))
            {
                inputSignature = ShaderSignature.GetInputSignature(bytecode);
                vertexShader = new VertexShader(device, bytecode);
            }

            // load and compile the pixel shader
            using (var bytecode = ShaderBytecode.CompileFromFile("triangle.fx", "PShader", "ps_4_0", ShaderFlags.None, EffectFlags.None))
                pixelShader = new PixelShader(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) };
            var layout = new InputLayout(device, inputSignature, elements);
            var vertexBuffer = new SlimDX.Direct3D11.Buffer(device, vertices, 12 * 3, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);

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

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

            #endregion
        }

        public void render()
        {
        }

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

Now we move all the code that configures the input assembler and sets the shaders to the render method.
If we have more objects, shaders have to be set for every object on every render call.

Further the calls to context and device have to be adjusted, because those are member variables of the DeviceManager.
So for example
context.VertexShader.Set(vertexShader);
becomes
DeviceManager.Instance.context.VertexShader.Set(vertexShader);

Finally we have to create an object of the triangle and call its render method in the render loop.

public void renderScene()
{
  Renderables.Triangle triangle = new Renderables.Triangle();

  while (true)
  {
    DeviceManager dm = DeviceManager.Instance;
    dm.context.ClearRenderTargetView(dm.renderTarget, new Color4(0.25f, 0.75f, 0.25f));

    triangle.render();
                
    dm.swapChain.Present(0, PresentFlags.None);
  }
}

You can download the source code here: http://apparat.codeplex.com/SourceControl/changeset/changes/3882dce2a134

Thursday, May 17, 2012

Rendering a Triangle

Now let's integrate the last of the SlimDX tutorials into our Engine. It is called
Direct3D11 - SimpleTriangle. I will start by hardcoding the relevant code from
the SlimDX tutorial into the Engine. I will not go into any detail about shaders, vertices and
the render pipeline in this post.

Take the following code and copy-paste it into the DeviceManager Class of our Engine
just behind the line


context.Rasterizer.SetViewports(viewport);


#region shader and triangle


// load and compile the vertex shader
using (var bytecode = ShaderBytecode.CompileFromFile("triangle.fx", "VShader", "vs_4_0", ShaderFlags.None, EffectFlags.None))
{
    inputSignature = ShaderSignature.GetInputSignature(bytecode);
    vertexShader = new VertexShader(device, bytecode);
}

// load and compile the pixel shader
using (var bytecode = ShaderBytecode.CompileFromFile("triangle.fx", "PShader", "ps_4_0", ShaderFlags.None, EffectFlags.None))
                pixelShader = new PixelShader(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) };
var layout = new InputLayout(device, inputSignature, elements);
var vertexBuffer = new SlimDX.Direct3D11.Buffer(device, vertices, 12 * 3, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);

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

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

#endregion

Add this using directive to your usings:


using SlimDX.D3DCompiler;

Add the following declarations to the member variables of the DeviceManager Class:


ShaderSignature inputSignature;
VertexShader vertexShader;
PixelShader pixelShader;

And add the following lines at the beginning of our shutDown Method in our DeviceManager Class:


pixelShader.Dispose();
vertexShader.Dispose();
inputSignature.Dispose();

Now add a new Element to our Engine Class Library by right-clicking on it and select Add->New Item.
Type "triangle.fx" as name and create the element.

Open the file in your Solution Explorer, delete all content and copy-paste the following code:


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

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

Right-click on the file "triangle.fx" in your Solution Explorer and click on Properies.
Select "Copy to Output Directory" and select "copy always".

Change the code in the RenderManager Class in the renderScene Method and call
dm.context.Draw(3,0) :


public void renderScene()
{
  while (true)
  {
    DeviceManager dm = DeviceManager.Instance;
    dm.context.ClearRenderTargetView(dm.renderTarget, new Color4(0.25f, 0.75f, 0.25f));
    dm.context.Draw(3, 0);
    dm.swapChain.Present(0, PresentFlags.None);
  }
}

After compiling the Class Library you can recompile your Main Project and should get the following result:


You can download the Project here: http://apparat.codeplex.com/SourceControl/changeset/changes/a13132898eaa