Formeln

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.

No comments:

Post a Comment