Example 01.01: Implementing a shading algorithm

When looking around for examples on writing a shader program in HLSL I stumbled upon various ways of writing a basic shader.

At the core of all this, for rendering opaque surfaces, is the Bidirectional Reflectance Distribution Function (BRDF)  that you choose to implement. It’s a function that determines the distribution of reflected light based on two directions: the view (or camera) direction and the light direction.

brdf

Figure 1. Distribution of reflected light

So why not start at the beginning with one of the earliest models: the Phong reflection model. This was first published in 1973, seven years before I was even born, thats prehistoric in the field of computer science!  After seeing the picture below on wikipedia I thought to myself: “Hey, I can do that, how hard could it be?”. We all know the hard work that comes after that thought, right? 🙂

01_01_figure_1

Figure 2. The different components of the Phong equation

Now the first thing that I need is that nice blob model that will show off some interesting shading and reflection. As I’m doing this series to learn, why not jump in the deep end and try to create that model myself. There are a few well known commercial 3d modeling applications (3D Studio Max, Maya, ZBrush,…) but as I am doing this for fun, a free / open source modeling program sounded like music to my ears. The best know, fully featured, open-source 3d modeling tool is probably Blender.  So that’s the tool that I picked for the job.

I had read that the user interface of Blender was an acquired taste and I can confirm that most things did not work as I initially thought they would. So after a long and rather frustrating evening figuring out the basics of Blender I ended up with this:

blender_blob

Figure 3. A blob is born

It may not look like much, but I was pretty proud of the result. Now let’s render this thing!

The Code

Most initialization code is the same as in the spaceship sample, the one new thing is that we’ll use the proper bone transforms. I hope I get this right, but as far as I understand it, the position of each mesh part of a model is dependent of their parent bone position. So if you move the bone, all meshparts move with it.

_model = Content.Load("phong_object");  
_bones = new Matrix[_model.Bones.Count];  
_model.CopyAbsoluteBoneTransformsTo(_bones);  

Code Snippet 1. Bones transforms

The draw portion for each frame is again pretty similar to the spaceship sample, but we are using a custom shader here instead of the BasicEffect and taking the bone transforms into account. We loop all the passes of the shader, but our shader only has one pass for now. When using a custom shader we can’t use the built-in draw function of the ModelMesh class so we just set the vertex and index buffer and then call DrawIndexedPrimitives for each mesh part.

 GraphicsDevice.Clear(Color.Black);  
 foreach (ModelMesh mesh in _model.Meshes)  
 {  
  foreach (ModelMeshPart part in mesh.MeshParts)  
  {  
   // calculate WorldMatrix  
   var modelPartWorldMatrix = _bones[mesh.ParentBone.Index] * _worldMatrix;  
   _effect.Parameters["matWorldViewProj"].SetValue(modelPartWorldMatrix * _viewMatrix * _projectionMatrix);  
   foreach (EffectPass pass in _effect.CurrentTechnique.Passes)  
   {  
    pass.Apply();  
    // Render meshpart  
    _graphicsDM.GraphicsDevice.SetVertexBuffer(part.VertexBuffer);  
    _graphicsDM.GraphicsDevice.Indices = part.IndexBuffer;  
    _graphicsDM.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.VertexOffset, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount);  
   }  
  }  
 }  

 Code Snippet 2. drawing the model

I just wanted to get the wireframe rendered as a first step before beginning to implement the first component of the Phong reflection model. The resulting shader is the most minimalistic yet.

In the vertex shader function we define a combined world-view-projection matrix and transform all vertices with that.

 float4x4 matWorldViewProj;  
 VS_OUT VertexShaderFunction(VS_IN input)  
 {  
  VS_OUT output;  
  output.Pos = mul(input.Pos, matWorldViewProj);  
  return output;  
 }  

Code Snippet 3. Vertex shader function

And our pixel shader function simply returns the color white.

float4 PixelShaderFunction(VS_OUT input) : COLOR
{
  return float4(1.0, 1.0, 1.0, 1.0);
}

Code Snippet 4. Pixel shader function

The only trick we need to apply to get only the wireframe rendered and not rasterize the surface in between is set the FillMode to WireFrame. We also set the CullMode to None so the triangles facing away from the camera are not culled.

 technique DefaultTechnique  
 {  
  pass P0  
  {  
   CullMode = None;  
   FillMode = WireFrame;  
   VertexShader = compile vs_2_0 VertexShaderFunction();  
   PixelShader = compile ps_2_0 PixelShaderFunction();  
  }  
 }  

Code Snippet 5. Shader pass

That’s all for now, in the next example we’ll start implementing the Phong reflection model.

Have fun!

next post

Downloads

Download XNA code
Download MonoGame code
Blob model

This entry was posted in Uncategorized and tagged . Bookmark the permalink.

Leave a comment