HLSL kicks ass

24 September 2010
by Arno

We’ve been working hard at our game, we will be showing a video in a week or so.

But now, let’s amaze ourselves at the awesomeness of shaders, specifically HLSL shaders.
HLSL (High Level Shader Language) shaders are little bits of code that run on the graphics card of your machine, and it basically takes input (a model), and transforms that into something nice. Best part is that this is all done on the GPU, so you can do some funky stuff with it.

I’ve been working yesterday on a new model for our tank, really, 3 cubes stacked on each other works fine for development, but for screenshots, it sucks ass.


This is the new tank model, it’s about 800 polygons, and thanks to it’s edges really looks like a toy tank model. I’ve kept the model simple since we won’t show them all that up close, it’s more a defined shape that will help players recognise the object.
It has some rim lighting (think of Super Mario Galaxy, where everything seems to have a huge light behind it), and of course some standard shading.

Here are the stages of the shader:

Basic ambient light
Skylight
Light
Rimlight

Now, after a couple of hours of modelling and shader writing, I got this:

I think that this will look pretty nice in-game, and as soon as I get my xbox 360 running again (hard drive broke, RROD) will show you some examples of in-game.

As far as other development goes, we got networking pretty much working now, scoring is a bit iffy but works.
Ruben is working on a new map, while I made a better debug map: Blockland. The thing is textured but I’m not on my development PC so here is a render of the mesh itself.
A big piece of scenery appears when you enter the middle of the map so that you can’t see from outside what is happening in the base.

For those who are interested:

/*
This code is put into public domain by Team Lolkat
*/
 
// diffuse colour, the standard colour of the object
half4 diffuse : Diffuse
<
   string UIName = “Diffuse Color”;
> = {1.0f, 0.5f, 0.0f, 1.0f};
 
// specular colour, highlights appear in this color
half4 specular : Specular
<
   string UIName = “Specular Color”;
> = { 1.0f, 1.0f, 1.0f, 1.0f };
 
// how much specular light the object casts, less is more diffuse texture and thus larger specular highlight
float shininess <
      string UIName = “Shininess”;
      float UIMin = 0.0f;
      float UIMax = 100.0f;
      float UIStep = 0.2f;
>   = 50.0f;
 
// where does the rim light start to shade (0 = normal in alignment with camera, 1 = normal facing camera)
float Rim_Start <
      string UIName = “Rim Begin”;
      float UIMin = 0.0f;
      float UIMax = 1.0f;
      float UIStep = 0.01f;
>   = 0.0f;
 
// where does the rim light end to shade (0 = normal in alignment with camera, 1 = normal facing camera)
float Rim_End <
      string UIName = “Rim End”;
      float UIMin = 0.0f;
      float UIMax = 1.0f;
      float UIStep = 0.01f;
>   = 1.0f;
 
// rim light multiplier, 1 is a good value to start with
float Rim_Multiplier <
      string UIName = “Rim Multipler”;
      float UIMin = 0.0f;
      float UIMax = 5.0f;
      float UIStep = 0.01f;
>   = 1.0f;
 
/************** light info **************/
 
half4 light1Pos : POSITION
<
      string UIName = “Light Position”;
      string Object = “PointLight”;
      string Space = “World”;
      int refID = 0;
> = {100.0f, 100.0f, 100.0f, 0.0f};
 
half4 light1Color : LIGHTCOLOR
<
      int LightRef = 0;
> = { 1.0f, 1.0f, 1.0f, 0.0f };
 
 
// matrixes from renderer, these are variables that 3dsmax sets so that it can be used with the DirectX material for easy tweaking
half4x4 wvp : WorldViewProjection < string UIWidget = “None”; >; // World*View*Projection;
half4x4 worldIT : WorldInverseTranspose < string UIWidget = “None”; >; // Matrix.Invert(Matrix.Transpose(World));
half4x4 viewInv : ViewInverse < string UIWidget = “None”; >; // Matrix.Invert(View);
half4x4 world : World < string UIWidget = “None”; >; // World;
 
 
// input from application
      struct a2v {
      half4 position            : POSITION;
      half3 normal            : NORMAL;
 
};
 
// output to fragment program
struct v2f {
        half4 position          : POSITION;
            half3 eyeVec            : TEXCOORD1;
            half3 lightVec         : TEXCOORD2;
            half3 worldNormal      : TEXCOORD3;
};;
 
// Blinn lighting function, so more lights can be added later without huge swats of copy-pasted code
half4 blinn2(half3 N,
            half3 L,
            half3 V,
            uniform half4 diffuseColor,
            uniform half4 specularColor,
            uniform half shininess
            )
      {
      half3 H = normalize(V+L);
      half4 lighting = lit(dot(L,N), dot(H,N), shininess);
      return diffuseColor *lighting.y + specularColor*lighting.z;
      }
 
// Vertex Shader
v2f v(a2v In, uniform half4 lightPosition)
{
      v2f Out;
   Out.worldNormal = mul(In.normal, worldIT).xyz;
 
   half3 worldSpacePos = mul(In.position, world);
   Out.lightVec = lightPosition – worldSpacePos;
   Out.eyeVec = viewInv[3].xyz – worldSpacePos;
   Out.position = mul(In.position, wvp);
   return Out;
}
 
// Pixel Shader
float4 f(v2f In,uniform half4 lightColor) : COLOR
{
 // Normalize, can’t be sure what gets in here
 half3 N = normalize(In.worldNormal);
 
  //create lighting vectors – view vector and light vector
 half3 V = normalize(In.eyeVec);
 half3 L = normalize(In.lightVec.xyz);
 
  // Ambient light is just the diffuse color, but darkened
 half4 C = diffuse*0.1f;
 
  // Regular light, takes the position and does a normal blinn function (see above)
 C += light1Color * blinn2(N, L, V, diffuse, specular, shininess);
 
 // Rim light, for giving it that cartoony look.
 float rim = smoothstep(Rim_Start , Rim_End , 1- dot(N,V));
 C +=  rim*Rim_Multiplier * diffuse;
 
  // This lights up the top-side of the model, and darkens the bottom
 C -= diffuse*length((half3(0.0f,0.0f,-1.0f)+N))*0.3f;
 C += diffuse*length((half3(0.0f,0.0f,1.0f)+N))*0.3f;
 return C;
}
 
// use the XNA technique in XNA (duh), and use the MODEL technique in 3DSMAX
technique XNA
{
    pass envPass
    {            
      VertexShader = compile vs_1_1 v(light1Pos);
      ZEnable = true;
      ZWriteEnable = true;
      CullMode = CCW;
      AlphaBlendEnable = false;
      PixelShader = compile ps_2_0 f(light1Color);
   }
}
 
technique MODEL
{
    pass envPass
    {            
      VertexShader = compile vs_1_1 v(light1Pos);
      ZEnable = true;
      ZWriteEnable = true;
      CullMode = CW;
      AlphaBlendEnable = false;
      PixelShader = compile ps_2_0 f(light1Color);
   }
}
 
 

2 Comments

1 Trackback or Pingback

Previous Post
«
Next Post
»