Saturday, January 23, 2010

Prime numbers

Erik Meijer has done this excellent series in functional programming which is available at Channel9.msdn.com
In one of the lectures he gives this example of generating an infinite list of prime numbers using lazy evaluation. His example was in Haskell. Here is the equivalent in F#:


let rec sieve nums = seq {
let head = Seq.head nums
yield head
let tail = Seq.skip 1 nums
let notMultipleOf n x = x % n <> 0
yield! sieve (Seq.filter (notMultipleOf head) tail)
}

let rec numbersAbove n = seq { yield n
yield! numbersAbove (n+1) }

let primes = sieve (numbersAbove 2)

Tuesday, January 12, 2010

Creating an interactive game using F#


One of the interesting things about functional programming is the notion of immutable types. Functional programmers avoid shared mutable state like the plague. In most functional languages, all values are immutable by default. A value cannot change once it is created. If you want to change a value in struct with many fields, you need to create a new struct, copy all the values and provide a new value for the field that needs to change. My initially thoughts were that this seemed incredibly inefficient. What it buys us though is that we can now modify values without worrying about another thread trying to access the same value. This all sounds great in theory but how do you write a game without having mutable state? The following example is kindof silly, but it serves to illustrate the point. How do you do the equivalent of:

void update() =
int rocketHeight = 0;
while(true)
{
drawRocketAt(rocketHeight);
rocketHeight++;
}

One of the ways of maintaining state is to pass it around in a recursive function. The following snippet has no mutable state, yet the rocket is drawn at a new position each frame.

let rec update(rocketHeight) =
drawRocketAt(rocketHeight)
update(rocketHeight + 1)
update(0)


This is all well and good, but surely you cannot pass all the state of every object in the same loop.
Agents to the rescue!
An agent or Actor is a lightweight object that runs in its own thread. Communication between actors happens through message passing. In F#, the MailboxProcessor implements this functionality and is essentially an actor/agent.

let mailbox = MailboxProcessor.Start(fun inbox ->
let rec update(state) =
async {
let! msg = inbox.Receive()
match msg with
| ActionA ->
let newState = OnActionA()
return! update(newState)
| ActionB ->
let newState = OnActionB()
return! update(newState)
| Stop -> return ()
}
update(initialState) )
mailbox.Post(Action)


Agents allows us to separate concerns and avoid having to pass unrelated game state in our main recursive loop. Of course agents are also multi-threaded so chances are that some work done by agents will actually automatically happen in parallel. The mechanism for passing messages are also thread safe. Only one request can be handled at a time.

In the past week I have written the classic game Pong using these ideas. It is hopelessly over engineered for a game as simple, but it helps to see how this might fit into a game with a bigger scope. In this game there are 3 agents. The main loop, Score and PlayerController. Since there are two player controllers, this is actually 4 different threads. The main loop is implemented as an agent that post an update message to itself for each frame. This allows the Application thread to send a Stop message to end update loop.
The Score agent listens for collision messages. Whenever the ball hits any of the oponents' zones, the score is updated.

type ScoreMessage =
| Collision of Surface
| Stop

type Score(physics:Physics) =

let mailbox = MailboxProcessor.Start(fun inbox ->
let rec update(green, red) =
async {
let! msg = inbox.Receive()
match msg with
| Collision(surface) ->
let score =
match surface with
| GreenZone -> (green, red+1)
| RedZone -> (green+1, red)
| Wall -> (green, red)
Debug.Trace "Score: Green-%d Red-%d" (fst score) (snd score)
return! update(score)
| Stop -> return ()
}
update(0,0) )

let OnCollision(player) =
mailbox.Post(player)

do physics.Event.AddHandler(fun player -> mailbox.Post(Collision(player)))

For the moment it only prints a value to the debug output. (It turns out SlimDX Direct2D is not completely implemented yet. You can not draw to the same surface as a Direct3D renderTarget.)
The PlayerController listens for events from the keyboard and updates the paddle positions accordingly.
The above example used the normal way of handling events. F# also allows you to manipulate the event stream using the Observable class. One can create an event stream that listents for events, reformat them and publish this modified event stream. One can even filter out events that you are not interested in. In the Input class I map from SlimDX KeyInputEventArgs to my own KeyEvent. I only have a subset of all the available keys because I would like them to fit into a 64bit integer bitfield. I therefor need to first map it to my representation and then filter out those that I don't have a mapping for.

module Keyboard =
...
do Device.RegisterDevice(UsagePage.Generic, UsageId.Keyboard, DeviceFlags.None)
// Listen only for key events for which we have a mapping defined
let Input = Device.KeyboardInput
|> Observable.map ( fun e ->
match keyToFlag.TryFind(e.Key) with
| Some(key) ->
{Key = key; IsPressed = e.State = KeyState.Pressed}
| None -> KeyEvent.None )
|> Observable.filter
(fun keyEvent -> keyEvent.Key <> KeyFlags.None)

Clients of the Keyboard module can then subscribe to this event stream. The PlayerController listens for these events and sends the appropriate message to its message queue.

type PlayerController =
...
let keyboardInput = Keyboard.Input.Subscribe(fun (keyEvent:KeyEvent) ->
if keyEvent.Pressed(left) then mailbox.Post(Left)
elif keyEvent.Pressed(right) then mailbox.Post(Right) )

Monday, January 4, 2010

Tessellation




DirectX 11 provides three new stages to the graphics pipeline that allows us to increase the complexity of objects by generating additional polygons. For this example we will look at tesselating the triangles of an icosahedron to generate a sphere. Our vertex shader for this example simply passes the vertex position to the hull shader.

HSInOut VertShader(VSInput input )
{
HSInOut Out;
Out.Position = input.Position;
return Out;
}

Like the geometry shader, the hull shader takes a draw primitive (in this case a triangle) as input is called once for each control point(vertex). The hull shader has two components. A patch constant function and the shader function. The patch constant function is responsible for calculating the tesselation factors for the edges and inside of the triangle before passing it along to the tesselation unit. The main shader can optionally perform additional computations. In this example, it only passes each vertex to the tesselator.

HSConst PatchConstantsHS( InputPatch< HSInOut, 3 > p )
{
HSConst output = (HSConst)0;
output.Edges[0] = output.Edges[1] = output.Edges[2] = 4;
output.Inside = 4;
return output;
}

[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[patchconstantfunc("PatchConstantsHS")]
[outputcontrolpoints(3)]
HSInOut PassThroughHS(
InputPatch< HSInOut, 3 > inputPatch,
uint uCPID : SV_OutputControlPointID )
{
HSInOut output = (HSInOut)0;
output.Position = inputPatch[uCPID].Position;
return output;
}

The real action happens in the Domain shader. Every additional vertex that is output from the tesselator is passed to the domain shader. It provides the original primitive that was tesselated, as well as the coordinates of the newly generated vertex in relation to the original. For a triangle patch, this is the barycentric coordinates of the vertex. While this sounds really special, it is really just the weights with respect to the original triangle vertices. It can easily be transformed by multiplying each weight with the original vertex coordinate as seen in the snippet below. The newly generated vertices are still in the same plane of the original triangle. In order to provide a better looking sphere the domain shader will also push out these new vertices so that they lie on the surface of our sphere before doing the transformation to screen space.

[domain("tri")]
PSInput DS(
HSConst input, const OutputPatch< HSInOut, 3 > TrianglePatch,
float3 BarycentricCoordinates : SV_DomainLocation )
{
PSInput output = (PSInput)0;

// Interpolate local space position with barycentric coordinates
float3 position = BarycentricCoordinates.x * TrianglePatch[0].Position
+ BarycentricCoordinates.y * TrianglePatch[1].Position
+ BarycentricCoordinates.z * TrianglePatch[2].Position;
position = normalize(position);
output.Position = mul( float4(position, 1.0), WorldViewProjection );
output.Normal = mul( position, (float3x3)World );
return output;
}

When creating the hull and domain shader it is important to specify shader model 5.0.

type Geometry(device, shape) =
...
let hullShader =
let byteCode = ShaderBytecode.CompileFromFile(
"Simple.hlsl",
"PassThroughHS",
"hs_5_0",
ShaderFlags.None,
EffectFlags.None)
new HullShader(device, byteCode)

let domainShader =
let byteCode = ShaderBytecode.CompileFromFile(
"Simple.hlsl",
"DS",
"ds_5_0",
ShaderFlags.None,
EffectFlags.None)
new DomainShader(device, byteCode)

Something extremely important. The primitive topology should be set to a patch list with 3 control points. Failing to do this will result in several hours of pulling out your hair to figure out why things don't work.

type Geometry(device, shape) =
...
member this.Prepare(transforms) =
do deviceContext.InputAssembler.PrimitiveTopology <-
PrimitiveTopology.PatchListWith3ControlPoint
do deviceContext.HullShader.Set(hullShader)
do deviceContext.DomainShader.Set(domainShader)
do deviceContext.DomainShader.SetConstantBuffer(
vsConstBuffer.Update(transforms),0)
...

Note also that the DomainShader now requires access to the transforms shader constants.

Saturday, January 2, 2010

Geometry shaders for Dummies



DirectX 11 offers 2 new programmable shader stages in the graphics pipeline, the Hull Shader and the Domain Shader. Before I investigate those, I wanted to first look at Geometry shaders, which was actually introduced in DirectX 10 already. Geometry shaders allows you to treat your data at a primitive level rather than the vertex level. Where in a Vertex shader you have access to only a single vertex at a time, the geometry shaders gives you the three vertices of a triangle as input. The shader can then modify the vertices, add more triangles or even discard it entirely. For my example I will tessellate an Icosahedron. This is a convenient shape to subdivide since it results in a sphere with evenly distributed vertices. Each triangle of the original icosahedron is recursively subdivided into 4 triangles. The newly created vertices are then normalized so that the lie on the surface of a unit sphere.
The vertex shader which would usually transform the vertices form local space to screen space, will now just pass the vertices along to the pipeline without any transformation.

struct GSInput
{
float4 Position : POSITION;
};
GSInput VertShader(VSInput input )
{
GSInput Out;
Out.Position = input.Position;
return Out;
}

Note that the original normal of the vertices are not really of interest since for a unit sphere, the normal is the same as the position. We only pass on the vertex position in local space.
SphereVertex is a helper function used by the Geometry shader that now does the vertex transformation.

PSInput SphereVertex(float4 position)
{
PSInput output;
output.Position = mul( position, WorldViewProjection );
output.Normal = mul(position.xyz, (float3x3)World);
return output;
}

Below is the algorithm for a single subdevision of an icosahedron. Here I ran into a limitation of HLSL. It turns out that recursive functions are not supported... One could write the algorithm iteratively but it is a bit more messy, and this exercise was really just a stab creating a geometry shader that does something useful. DirectX 11 actually has a hardware tesselator for this sort of thing. In the next tutorial I will create Hull and Domain shaders to do tessellation with arbitrary complexity.

[maxvertexcount(12)]
void GeomShader( triangle GSInput input[3], inout TriangleStream OutputStream )
{
float4 va = input[0].Position;
float4 vb = input[1].Position;
float4 vc = input[2].Position;
float4 v1 = float4(normalize(va.xyz + vb.xyz),1);
float4 v2 = float4(normalize(va.xyz + vc.xyz),1);
float4 v3 = float4(normalize(vb.xyz + vc.xyz),1);
OutputStream.Append( SphereVertex(vc) );
OutputStream.Append( SphereVertex(v2) );
OutputStream.Append( SphereVertex(v3) );
OutputStream.Append( SphereVertex(v1) );
OutputStream.Append( SphereVertex(vb) );
OutputStream.RestartStrip();
OutputStream.Append( SphereVertex(va) );
OutputStream.Append( SphereVertex(v1) );
OutputStream.Append( SphereVertex(v2) );
OutputStream.RestartStrip();
}

When setting up the geometry, one creates a geometry shader in very much the same way as a vertex or pixel shader.

type Geometry(device, shape) =
...
let gsByteCode = ShaderBytecode.CompileFromFile(
"Simple.hlsl",
"GeomShader",
"gs_4_0",
ShaderFlags.None,
EffectFlags.None)

let geomShader = new GeometryShader(device, gsByteCode)
....
member this.Prepare(transforms) =
....
do deviceContext.GeometryShader.Set(geomShader)
do deviceContext.GeometryShader.SetConstantBuffer(
vsConstBuffer.Update(transforms),0)



Note that the transforms constants (World/WorldViewProjection) that were previously used by the vertex shader must now be provided to the geometry shader instead.


For interest sake, here is what the subdivision algorithm in F# looks like if it were to be done on the CPU instead.



static member Sphere subdivisions =
let t = (1.0f + sqrt(5.0f))/2.0f;
let s = sqrt(1.0f + t*t);
let icosahedronVertices = [|
Vector3( t, 1.0f, 0.0f)/s;
Vector3( -t, 1.0f, 0.0f)/s;
Vector3( t,-1.0f, 0.0f)/s;
Vector3( -t,-1.0f, 0.0f)/s;
Vector3( 1.0f, 0.0f, t)/s;
Vector3( 1.0f, 0.0f, -t)/s;
Vector3(-1.0f, 0.0f, t)/s;
Vector3(-1.0f, 0.0f, -t)/s;
Vector3( 0.0f, t, 1.0f)/s;
Vector3( 0.0f, -t, 1.0f)/s;
Vector3( 0.0f, t,-1.0f)/s;
Vector3( 0.0f, -t,-1.0f)/s;
|]

let icosahedronIndices = [|
0; 8; 4;
1;10; 7;
2; 9;11;
7; 3; 1;
0; 5;10;
3; 9; 6;
3;11; 9;
8; 6; 4;
2; 4; 9;
3; 7;11;
4; 2; 0;
9; 4; 6;
2;11; 5;
0;10; 8;
5; 0; 2;
10; 5; 7;
1; 6; 8;
1; 8;10;
6; 1; 3;
11; 7; 5;
|]

let rec subdevide(vertices:array, indices, level) =
if level < subdivisions then
let subdivideHelper(accVertices:array,
accIndices,
triangle:list) index =
match triangle with
| ib::ia::[] ->
let ic = index
let va, vb, vc = vertices.[ia], vertices.[ib], vertices.[ic]
let v1, v2, v3 =
Vector3.Normalize(va + vb),
Vector3.Normalize(va+vc),
Vector3.Normalize(vb + vc)
let i1 = accVertices.Length
let i2 = i1+1
let i3 = i2+1
(Array.append accVertices [|v1;v2;v3|],
Array.append accIndices [|i1;i3;i2;
ia;i1;i2;
ic;i2;i3;
ib;i3;i1|],
[])
| _ -> (accVertices,accIndices, index::triangle)

let vertices, indices, _ =
Array.fold subdivideHelper (vertices,[||],[]) indices
subdevide(vertices, indices, level+1)
else
vertices, indices

let vertices, indices = subdevide(icosahedronVertices, icosahedronIndices, 0)
{ Vertices = Array.map (fun vec -> Vertex(vec, vec)) vertices; Indices = indices }

Thursday, December 31, 2009

Indexed drawing

I was getting annoyed with having to cast Math.PI which is double to float32 every time. F#, like C# 3.0 allows you to extend existing types by adding your own functionality. You don't have access to any of the internals, but you can add methods and properties that access the public interface. Here is my extension to System.Math:

module Flow.TypeExtensions
open System

type System.Math with
static member Pi = float32(Math.PI)
static member PiOver2 = Math.Pi/2.0f
static member TwoPi = Math.Pi*2.0f


In this tutorial we are drawing a cube. Since a cube requires more vertices than a single triangle and many of them will be the same, we are going to use an index buffer to minimize the amount of vertices. We still need 4 vertices per face since the normals are unique per face. Still, 4 is better than 6. We create a new class type called Shape which will hold our vertices and indices. The helper function Shape.Box will create our cube by contructing a single face and rotating it to form each of the sides.

type Shape = {
Vertices : array< Vertex >
Indices : array< int32 >
}
with
//-------------------------------------------------------------------------
static member Box =
let vertices =
let normal = Vector3( 0.0f, 0.0f,-1.0f)
let face = [|
Vector3(-1.0f, 1.0f,-1.0f);
Vector3( 1.0f,-1.0f,-1.0f);
Vector3(-1.0f,-1.0f,-1.0f);
Vector3( 1.0f, 1.0f,-1.0f)
|]

let rotatedFace rotation =
let vertices = Vector3.TransformCoordinate(face, ref rotation)
let normal = Vector3.TransformCoordinate(normal, rotation)
Array.map (fun vec -> Vertex(vec, normal)) vertices

let aboutY angle = Matrix.RotationY(angle)
let aboutX angle = Matrix.RotationX(angle)

// Create the sides by rotating the front face 4 times about the Y axis
let rec sides(angle, acc) =
if (angle < Math.TwoPi) then
sides(angle+Math.PiOver2, Array.append acc (rotatedFace(aboutY angle)))
else
acc
// Create the top and bottom by rotating the front face 90 and
// -90 degrees about the X axis
let top, bottom = rotatedFace(aboutX Math.PiOver2), rotatedFace(aboutX -Math.PiOver2)
Array.concat ( seq { yield sides(0.0f, [||])
yield top
yield bottom } )

let indices =
// Create indices by incrementing each element of the first face by
// the base offset of the face. This will ensure the order is consistent
// so all the triangles are front facing.
let nextIndices indices increment =
Array.map (fun x -> x+increment) indices

let frontIndices = [|0;1;2;0;3;1|]
let increments = [|for i in [0..5] do yield i*4|]

Array.collect (nextIndices frontIndices) increments
{ Vertices = vertices; Indices = indices }

Our Geometry object will now have an index buffer.

type Geometry(device, shape) =
...
let indexBufferDesc = new BufferDescription(
BindFlags = BindFlags.IndexBuffer,
SizeInBytes = shape.Indices.Length*sizeof< uint32 >,
Usage = ResourceUsage.Immutable)
let indexBuffer = new SlimDX.Direct3D11.Buffer(
device,
new DataStream(shape.Indices, true, false),
indexBufferDesc)

When preparing this geometry we set the index buffer. When drawing we now have to use DrawIndexed instead of the normal Draw call.

member this.Prepare(transforms) =
...
do deviceContext.InputAssembler.SetIndexBuffer(indexBuffer, Format.R32_UInt, 0)

member this.Draw() =
deviceContext.DrawIndexed(shape.Indices.Length, 0, 0)


That pretty much it for this tutorial. As usual the full source code can be found on Google Code

Let there be light

Our previous example was pretty boring since we always used the same color. To spice up the realism we need to add some color and light. To allow different colors we need to add constants that the pixel shader can use. We also need to add normals to our vertices and a light direction vector so we can shade our triangle based on how much it is oriented towards the light. We will also rotate the triangle at a constant velocity so we can see the effect of the light.

Lets look at the changes in the shaders first. We added a constant buffer storing a diffuse color. This will be used by our pixel shader.

cbuffer psMain
{
float4 DiffuseColor;
};

In addition to the WorldViewProjection matrix we now also need the World matrix. We use this to transform the normal to world space in order to calculate the angle between the light direction and the normal.

cbuffer vsMain
{
row_major matrix World;
row_major matrix WorldViewProjection;
};

Our Vertex shader now expects a normal in addition to the position for each vertex. It will transform the normal and pass it along to the pixel shader for the lighting calculation.

struct VSInput
{
float4 Position : POSITION;
float3 Normal : NORMAL;
};

struct VSOutput
{
float4 Position : SV_POSITION;
float3 Normal : NORMAL;
};
When calculating the Normal in world space we are not interested in its translation. Our light is a directional light and just described by a direction vector without a position. We therefor have to transform our normal only by the rotation in order for both vectors to be at the origin. Fortunately, that is exactly what happens when you multiply a vector3 with a matrix4x4. The last component of the vector is implicitly zero which effectively cancels any translation component the matrix may have had.

VSOutput VertShader(VSInput input )
{
VSOutput Out;
Out.Position = mul( input.Position, WorldViewProjection );
Out.Normal = mul( input.Normal, World );
return Out;
}

In the pixel shader we hard code the light direction for now. This should really be a pixel shader constant in order to change the light dynamically. The amount of light for the pixel is dot product between the normal and the light direction. We multiply that value by our diffuse color constant to get our final color. Later we will add some more properties like specular and ambient color.

float4 PixShader( VSOutput In ) : SV_TARGET
{
float3 LightDirection = float3(0.0f, 0.0f, 1.0f);
return float4(DiffuseColor.rgb*(dot(In.Normal, -LightDirection)), DiffuseColor.a);
}

The Geometry, Material and ShaderConstants types now reside in their own files.
For Geometry we now need to specify that our vertices consist of a position and a normal. A vertex is now defined as:

#nowarn "9"
[<Struct; StructLayout(LayoutKind.Sequential)>]
type Vertex(position : Vector3, normal : Vector3) =
member this.Position = position
member this.Normal = normal

Note that when describing our layout, we have to be explicit about the offset of the normal in relation to the position. Since our structure layout is defined as sequential the normal will start immediately after the position.

let inputLayout =
let position = InputElement(
SemanticName = "POSITION",
Format = Format.R32G32B32_Float,
Classification = InputClassification.PerVertexData)
let normal = InputElement(
SemanticName = "NORMAL",
Format = Format.R32G32B32_Float,
AlignedByteOffset = sizeof,
Classification = InputClassification.PerVertexData)
new InputLayout(
device,
ShaderSignature.GetInputSignature(vsByteCode),
[|position; normal|])

Our previous example only had a single set of vertex shader constants that represented only the world view projection transformation in a single matrix. We now also have a second set of constants associated with the pixel shader. I have moved the VS constants to be updated together with the geometry while the PS constants are updated together with the material.
Since we now have more than one VS constant we need to specify the structure.

#nowarn "9"
[<Struct; StructLayout(LayoutKind.Sequential)>]
type Transforms(world : Matrix,
worldViewProjection : Matrix) =
member this.World = world
member this.WorldViewProjection = worldViewProjection

Our PS constants only contains the diffuse color for now.

#nowarn "9"
[<Struct; StructLayout(LayoutKind.Sequential)>]
type MaterialProperties(diffuseColor : Color4) =
member this.DiffuseColor = diffuseColor

In our main application we now have to specify the normals for each of the vertices in our triangle. We also give the material color when asking the renderer to create a drawable object.

let triangleVertices = [|
Vertex(Vector3(-1.0f,0.0f,0.0f), Vector3(0.0f,0.0f,-1.0f));
Vertex(Vector3(0.0f,1.0f,0.0f), Vector3(0.0f,0.0f,-1.0f));
Vertex(Vector3(1.0f, 0.0f, 0.0f), Vector3(0.0f,0.0f,-1.0f))|]

let green = new Color4(Red = 0.0f, Green = 1.0f, Blue = 0.0f, Alpha = 1.0f)
let triangle = renderer.CreateDrawable(triangleVertices, MaterialProperties(green))

The other interesting change in this iteration is our main loop. In the previous examples we relied on the Paint event to signal when we should draw. It turns out this event is only sent when the form thinks it should be updated. Usually only the first time the window is shown or if it is minimized and then maximized. To make our triangle move, we need to draw several times per second. Our main loop now looks like this:

let OnApplicationIdle _ =
while (Windows.AppStillIdle()) do
...
// as before
...
let rotationSpeed = float32(Math.PI/2.0) // per second
let elapsedSeconds = float32(timer.ElapsedMilliseconds)/1000.0f;
let world = Matrix.RotationY(rotationSpeed*elapsedSeconds)
let transforms = Transforms(world, world*view*projection)
renderer.Draw(triangle, transforms)

do Application.Idle.Add(OnApplicationIdle)
do Application.Run(form)

We only update while our application is 'idle' As soon as any windows event happens we yield control to our window. The Windows.AppStillIdle() is a native method that does a PeekMessage() to see if there is anything on the event queue. When the app is done handling the event we will receive another idle event and we continue spinning our green triangle.

Tuesday, December 29, 2009

Drawing a triangle

For my next trick, I am going to show you how to draw a triangle. It turns out there is quite a lot of water that needs to flow underneath the bridge before we can achieve this seemingly simple feat. To start off, I moved everything related to DirectX and graphics into a separate file called Renderer under a new namespace called Flow.Graphics. This file contains several new types/classes that will help us draw things to our render window. Our new main program is now much shorter. I'll start with the new main method and then delve deeper into each of the new classes in Renderer.


open System
open System.Windows.Forms

open SlimDX
open Flow.Graphics

//---------------------------------------------------------------------------------------
[<STAThread>]
[<EntryPoint>]
let main(args) =
let width, height = 400, 300
let form = new Form(Text = "Blue skies", Width = width, Height = height)
let renderer = new Renderer(form.Handle, width, height)
let triangleVertices = [|
Vector3(-1.0f,0.0f,0.0f);
Vector3(0.0f,1.0f,0.0f);
Vector3(1.0f, 0.0f, 0.0f)|]
let triangle = renderer.CreateDrawable(triangleVertices)

let paint _ =
let eye, lookAt, up =
Vector3(0.0f,0.0f,-5.0f),
Vector3.Zero,
Vector3(0.0f,1.0f,0.0f)
// Look at the origin
let view = Matrix.LookAtLH(eye, lookAt, up)
// Place our triangle at the origin
let world = Matrix.Identity
let fovy, aspect, near, far =
float32(Math.PI)/4.0f,
float32(width)/float32(height),
1.0f,
20.0f
let projection = Matrix.PerspectiveFovLH(fovy, aspect, near, far)

renderer.Draw(triangle, world*view*projection)

do form.Paint.Add(paint)
do Application.Run(form)
for item in ObjectTable.Objects do
item.Dispose()
0


Most of the action happens in Renderer. We ask it to create a drawable object with the supplied vertices. This list represents the three corners of our triangle. During the 'paint' callback we calculate a world, view and projection matrix in order to transform our triangle from world space to screen space. We then ask the renderer to draw our triangle with the given transformation.


type Renderer(windowHandle, width, height) =
...
// Create device,swap chain and rendertarget view as before
...
//Create a viewport with the same size as the window
let viewport =
new Viewport(
Width = float32(width),
Height = float32(height),
MinZ = 0.0f,
MaxZ = 1.0f,
X = 0.0f,
Y = 0.0f)
let deviceContext = device.ImmediateContext

do deviceContext.OutputMerger.SetTargets([|renderTargetView|] )
do deviceContext.Rasterizer.SetViewports([|viewport|])

member this.CreateDrawable(triangle) = new Drawable(device, triangle)
member this.Draw(drawable : Drawable, transform) =
do device.ImmediateContext.ClearRenderTargetView(
renderTargetView,
new Color4(Alpha = 1.0f, Red = 0.0f, Green = 0.0f, Blue = 1.0f))
do drawable.Draw(transform)
do swapChain.Present(0, PresentFlags.None) |> ignore


Now that we are going to draw something in our window we need to tell create a viewport to describe which part of the window to use. We will use the entire window. The draw method looks similiar to our previous 'paint' callback, except that we now actually draw something between clearing the back buffer and switching to the front. This is our triangle object that was created with CreateDrawable(...) Let's look at this class.

type Drawable(device, triangle) =
let geometry = new Geometry(device, triangle)
let material = new Material(device)
let constants = new ShaderConstants(device)

member this.Draw(transform) =
constants.Prepare(transform)
material.Prepare()
geometry.Prepare()
geometry.Draw()

The Drawable class serves as a container for all the components that is needed to draw something to the screen. The Geometry describes what needs to be drawn. All the vertex data resides here. A Material describes what an object's surface should look like. The constants are additional data needed to draw an object. In our example, we need a transformation matrix in order to know where to draw the geometry. This typically changes every frame so we pass in this information in our draw call. Before drawing the geometry, the device context needs to be prepared by telling it which vertex and pixels shaders to use and to copy the constants associated with this object to the GPU. This is done in the prepare methods of our constants, material and geometry objects.

The Material in this example is extremely simple. It creates a pixel shader found in the Simple.hlsl file. When the time comes to prepare the renderer with this material, it sets the pixel shader for the device context to this shader.

type Material(device) =
let pixelShader =
let psByteCode = ShaderBytecode.CompileFromFile(
"Simple.hlsl",
"PixShader",
"ps_4_0",
ShaderFlags.None,
EffectFlags.None)
new PixelShader(device, psByteCode)
member this.Prepare() =
do device.ImmediateContext.PixelShader.Set(pixelShader)


The pixel shader will always draw the object in red. We will create some more interesting materials in the near future...

float4 PixShader(float4 position : SV_POSITION ) : SV_TARGET
{
return float4(1,0,0,1); //RED
}


-To setup our geometry we compile the vertex shader found in the same file as our pixel shader.
-We have to specify an input layout so the GPU knows how to interpret the vertex data. For now we are just using a Vector3 to represent a vertex position. In the future we will expand on this by adding normal and texture coordinates to our vertices.
- Next we create a vertex buffer which will copy our vertices to the GPU. Note that this buffer is immutable. We will not be able to change these vertices again.
- When preparing to draw this geometry, we need to specify the input layout, the primitive topology(triangle list in this case), our vertex buffer and our vertex shader. This could change between different objects so we have to update these states before drawing.
- When drawing we need to tell the renderer how many vertices to draw.

type Geometry(device, vertices:array) =
let vsByteCode = ShaderBytecode.CompileFromFile(
"Simple.hlsl",
"VertShader",
"vs_4_0",
ShaderFlags.None,
EffectFlags.None)

let vertexShader = new VertexShader(device, vsByteCode)
let inputLayout =
let position = new InputElement(
SemanticName = "POSITION",
Format = Format.R32G32B32_Float,
Classification = InputClassification.PerVertexData)
new InputLayout(
device,
ShaderSignature.GetInputSignature(vsByteCode),
[|position|])
let vertexSize = sizeof
let vertexBuffer =
let vertexBufferDesc = new BufferDescription(
BindFlags = BindFlags.VertexBuffer,
SizeInBytes = vertexSize*vertices.Length,
Usage = ResourceUsage.Immutable)
new SlimDX.Direct3D11.Buffer(device,
new DataStream(vertices, true, false),
vertexBufferDesc)

let deviceContext = device.ImmediateContext
member this.Prepare() =
do deviceContext.InputAssembler.PrimitiveTopology <-
PrimitiveTopology.TriangleList
do deviceContext.InputAssembler.InputLayout <- inputLayout
do deviceContext.VertexShader.Set(vertexShader)
do deviceContext.InputAssembler.SetVertexBuffers(
0, new VertexBufferBinding(vertexBuffer, vertexSize, 0))

member this.Draw() =
deviceContext.Draw(vertices.Length, 0)

Our vertex shader is also very simple. It simply transforms a vertex from world space to screen space by multiplying with the given transformation matrix.

float4 VertShader(float4 position : POSITION ) : SV_POSITION
{
return mul( position, WorldViewProjection );
}


Finally we have our shader constants. This will be used to update our transformation matrix each frame.

type ShaderConstants(device) =
let vsConstBuffer =
let vsConstSize = sizeof
let vsConstBufferDesc = new BufferDescription(
BindFlags = BindFlags.ConstantBuffer,
SizeInBytes = vsConstSize,
CpuAccessFlags = CpuAccessFlags.Write,
Usage = ResourceUsage.Dynamic)
new SlimDX.Direct3D11.Buffer( device, vsConstBufferDesc )

let deviceContext = device.ImmediateContext
let updateShaderConstants constBuffer sizeInBytes data =
let constData = deviceContext.MapSubresource(
constBuffer,
0,
sizeInBytes,
MapMode.WriteDiscard,
SlimDX.Direct3D11.MapFlags.None)
Marshal.StructureToPtr( data, constData.Data.DataPointer, false )
deviceContext.UnmapSubresource( constBuffer, 0 )

member this.Prepare( worldViewProjection ) =
updateShaderConstants vsConstBuffer sizeof worldViewProjection
do deviceContext.VertexShader.SetConstantBuffers([|vsConstBuffer|], 0, 1)

The buffer is created similar to the VertexBuffer. In this case though we specified that it is dynamic since we want to update this buffer later. When we prepare the shader constants, we copy the new transformation matrix directly to the mapped region reserved for our constant data. The layout needs to exactly map to the layout described in HLSL below. In this case though we only have one element so it is trivial.

cbuffer vsMain
{
row_major matrix WorldViewProjection;
};

Wow, what a lot of work to draw a triangle!
The full sourcecode can be found on GoogleCode. I use Mercurial for version control. Why? Mostly because I have always wanted to experiment with distributed version control and at the moment I have the time to do it. If you are used to SVN and Tortoise, you will find that TortoiseHg is very similiar. So far I am enjoying Mercurial. The main benefit for me at the moment is that I can make local commits. I like to commit often to give myself a safe place to revert to. The code may not be in a state that I want to commit it to the server yet, so I just commit it locally. Even when my internet is down, I can still commit. Later when everything works and I want others to benefit from from my changes, I push all my commits to the server. It is pretty painless.