Category Archives: Programming

Rendering Glow and Soft Edged Object

Often, I have found myself trying to render light shaft, laser, godray and other similar special effect.

A formula I like to use in my pixel shader is

abs(dot(NormalVec, ViewVec)); //NdotV

This would give 1 from the center of the object to 0 at the edge of object. Using this value to apply to alpha blending or multiply it to the color, would give a soft edge to the object.

It is also useful to control how soft the edges of the object are, we could use

float finalBlending = pow(NdotV, softness).

This would make the edge softer the higher the softness value.

For effects that have hard edge and soft center, use 1 – NdotV.

Recently I also have been using

2 * abs(NdotV – 0.5);

to create effects which have hard center and hard edge with a gradual fade off between the two.

Try this with normal mapping to create more interesting effect.

Simple KeyPress Buffer

Using a keyboard buffer for intuitive user inputs

Objective: Using a keyboard buffer for intuitive user inputs
Difficulty: Beginner
Code length: Short
 
Until recent years, user inputs had often been neglected in software development. After all, controllers and keyboards are rather idiot proof, there are only so many ways to use them Is it?
 
In a simple walk around the world program, the user press  ‘a’, the avatar move left. Alright now the user press ‘s’, so the avatar move down. Now the user press both ‘a’ and ‘s’, intuitively the avatar move south west.
 
What happen if the gameplay do not support diagonal movement?. The avatar move the direction of the last key pressed. However if the player release the ‘s’ key while holding ‘a’ key. This is where keyboard buffering come into play.
 
In many gameplay situations, it may not be feasible to process certain key inputs simultaneously. This article will explain how to create a simple keyboard buffer for an arcade style game movement.
 
This is a simple buffer for two keys, which I am going to store as a player actions.
_lastActions[1] is the latest player action.
_lastAction[0]  is the action buffer.
 
The default action for the buffer is  PLAYER_NOT_MOVING, this is what happens when the player releases both keys
 
int _lastActions[2] = {PLAYER_NOT_MOVING, PLAYER_NOT_MOVING};
 
//When key is pressed, save the previous action.
void keyPressed(KeyEvent e)
{
   if(e.key == keyMap[PLAYER_MOVE_UP])
   {
      _lastActions[0] = _lastActions[1];
      _lastActions[1] = PLAYER_MOVE_UP;
      //player move up
   }
}
 
Here will check which key the player release, did he release the latest key?, If so process the buffer.
In any case got to reset the action buffer.
 
void keyReleased(KeyEvent e)
{
   if(_lastActions[1] == PLAYER_MOVE_UP)
   {
      _lastActions[1] = _lastKeys[0];
      //process the previous player action.
      _lastActions[0] = PLAYER_NOT_MOVING;
   }
   else if(_lastActions[0] == PLAYER_MOVE_UP)
      _lastActions[0] = PLAYER_NOT_MOVING;
}
 
It is not a good idea to map the keyPressed to the keycode as in “if(e.key == key.ESCAPE)”. A better pratice is to store all the keycodes in an array and use an enum of player actions  to reference them. In this way not only your keys configuration can be read from an external file, you can also customise your keys during gameplay.
 
Here is an example of how to reference your keycodes from an enum.
 
enum playerInputs
{
   PLAYER_NOT_MOVING = 0,
   PLAYER_MOVE_UP,
   PLAYER_MOVE_DOWN,
   PLAYER_INPUTS_COUNT
};
 
KeyCode[] keyMap = new keyMap[PLAYER_INPUTS_COUNT]; //populate your array.
 

NaN

Not A Number

NaN is an insidious bug that often plague games and shader programs during development. If you are lucky, you would probably never encounter it before, but the first time would often be very painful.

It begin with a single unit disappearing from the map, then suddenly whole armies are teleported somewhere, and at last the player is teleported to limbo.

On a shader program, it is less obvious, as the color of NaN is BLACK!!! So for most of the time it would seem just like a few black speck on the screen. But turning on effect like HDR when there is a NaN would cause the whole screen to blacken out.

The worst thing about NaN is, it is more contagious than the plague. Any maths operator with a NaN would give you a NaN

Luckily there are not many ways to cause NaN. The most common ways and solution are

Division by zero
Have a check to ensure your divisor is not zero. In certain situations with a positive divisor, we can add a very small number to the divisor to ensure it is not zero.

Normalize a zero vector
Remember to normalize a vector is to divide every component of a vector with it magnitude. So to normalize a zero vector would cause a divison by zero. Do a check on the vector length or lengthSquare to ensure it is not zero before normalizing it.
Zero vector Normals are also very common in exported model, it could be the normal is not exported or some bug with teh exported, to export a zero normal. I often find a shader to display normals as color to be useful in such situation. If you have control over the content pipeline, Assert when there is a zero normal.

Calling ACos on a number outside -1 to 1
Dot product of two unit vectors would give the cosAngle. if any of the two vectors is not unit length, the result would probably be outside -1 to 1. Ensure both vectors are unit length, and try not to call ACos as cosAngle is very sufficent to solve most problems.

Boost Bind

Boost Bind imo is one of the most useful library in C++, yet at times can be the most frustrating one too!

For the uninitiate
This is a small example on the usefulness of boost bind. We have a FunctionClass which that in a function, and will execute the function sometime later. I will not go into detail on why the function will be executed later, below is the class.

class FunctionClass
{
public:
typedef boost::function Callback;
Callback callback_;

FunctionClass (const Callback & callback) :
callback_(callback)
{
}

virtual void executeCallback()
{
callback_();
}
};

Now the class will only take in a function that take in no parameters and return no parameters. If we writing our own function, we can still ensure it work with the Callback. Let say we need to call a function from a third pty library.

Eg. SceneNode::setPosition at Vector3(0, 0, 0);

We could always create a helper function

void setSceneNodePositionToZero()

What happen if we need to call setPosition at multiple differnt location???

This is where boost bind come to the rescue!!!

FunctionClass

Callback myCallback = boost

bind(&SceneNode::setPosition, myNode, Vector3(0, 0, 0));

A simple one line of code to fufill our needs!!!

…. more examples to follow

Optimising Shaders

I am using HLSL shader model 3, but this article should apply to other languages and shader models.

It is Good to MAD

One of the most basic optimisation is to use mad operation, which is to multiply 2 values and add a third value to the result.
This is two instructions for the price of one, luckily the compiler is usually smart enough to snap this bargain when they sense it.
It is still useful to look at the compiled asm to combine any exceptions

The Power of 4

The beauty of GPU is most instructions are process in 4 components data block. This will caused any instruction that uses 1 data to cost as much as the same instruction using 4 data.

Eg pow(NdotH, shininess) cost the same as pow(float4(light1NdotH, light2NdotH, light3NdotH, light4NdotH), shininess)

Step operation and comparision operator also work in 4
float4 sampleDepths;
float depthToCompare;

float4 results = step(sampleDepths, depthToCompare);
float4 results = (sampleDepths > depthToCompare);

When 4 become 1

After comparision, it is often desirable to combine the results together to get total number of samples compared correctly.
This is where dotproduct shine.
Remember dot product of 2 float4 is

vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z + vec1.w * vec2.w

which is 4 mad instruction for 1 dp4 instruction

float numberOfResultsTrue = dot(results, 1);

Cheap Matrix Inverse

Remember orthonormal matrices Inverse is equal to their Transpose. Tangent, Binormal(Bitangent), Normal are usually in unit length and are perpendicular to each other. Use their transpose to convert any tangent space vector back to object space.

Eg
float3x3 matTan = float3x3(T, B, N);
float3x3 matTanInverse = transpose(matTan);
float3 normalFromNormalMap;
float3 worldNormal = mul(matTanInverse, normalFromNormalMap);

Random From UV

RenderMonkey has a very Cool Texture generator, that allow you to generate a texture from HLSL.

Well It is cool until you need to generate a random texture from UV.

Here are some quick code that is prob not mathematically correct, but it work for me.

float4 Texture2DFill( float2 vTexCoord : POSITION,
float2 vTexelSize : PSIZE) : COLOR
{
float uvFactor = dot(vTexCoord, float2( 1528.4f, 51.3f));
float rand = frac(fmod(uvFactor * 51223.00445, 20)) * 20;
rand *= uvFactor;
rand *= sqrt(rand * 347);
rand = fmod(rand, 2 * 3.1412 * uvFactor);
rand = frac(rand);
return rand;
}

Playing with NormalMap

Combining tangent space normal map is different from combining normal images. Here are some ways to combine two normal map together. The general idea is the identity normal, float3(0, 0, 1), will use the vertex default normal.

Adding

Adding two normals and preserving both details can be done this way.
finalNormal = float3(normal1.xy + normal2.xy, normal1.z * normal2.z);
finalNormal = normalize(finalNormal);

Blending

Using lerp. we can easily blend two normal
lerp(normal1, normal2, factor)

Strengthen

To make the normal map look stronger we could do

normal.xy *= strength;
normalize(normal);

This will cause any slope to bent away from the surface normal making it look steeper.

Decompress

To compress normal, it is common to copy the X value to the A channel of the image, and calculate the Z value from X and Y
In short it is to take advantage of the uncompressed A channel in DXT5 and to save an additional channel data by calculating Z during runtime.

float3 normal;
normal.xy = tex2D(normalMap, uv).ag;

normal.z = sqrt(1 – normal.x * normal.x – normal.y * normal.y);
which can be optimise to
normal.z = sqrt(1 – dot(normal.xy, normal.xy));

Power

A Power function raised x to specific power.

It has an interesting property when x is between 0 – 1, the result will be always between 0 – 1. Do take note Power function will not preserve the sign of x, it may be necessary to calculate the sign beforehand.

What Power does is to change the curve of x to favor certain values.

This is the curve for using a joystick to aim at something. Typically when gamers want to have a fast movement on analog stick, They would push the analog stick to max. Using the Power function we can allocate 80% of the analog controls to slower movement, so gamers would have more control over aiming, yet allow the quick movement by pushing the analog stick to max.

Power is also very commonly used in graphics, We could use it to change color curves, contrast of images, fade off of lighting equation/fog/glows and many more.

Light Attenuation

Point Light Distance Attenuation

This is the commonly used formula to attenuate light by a constant, linear and quadric factor.

1 / (constantFactor + (linearFactor * disFactor) + (quadricFactor * disFactor * disFactor));

personally I dislike this formula as it is very difficult to isolate light to a particular area only.

 Spherical Light Attenuation

This is something I use more frequently.

saturate(1.0 – smoothstep(lightAttMinRange, lightMaxRange, lightDis));

This will only start attenuate at a specific distance and will have a smooth fall off to the lightMaxRange. It really make lights placement in room simpler.

Spot Light Attenuation

float spot = dot(spotlightDir, lightDir);

float outerAngleFactor = cos(outerAngleFactor / 2);

float innerAngleFactor  = cos(innerAngleFactor / 2);

float attenuation = (spot >= outerAngleFactor) * pow((spot – outerAngleFactor) / (innerAngleFactor – outerAngleFactor), falloff);

if you are using ogre, outerAngleFactor and attenuation  is calculated for you. if you are using lightVec from NdotL, do remember to inverse the direction.

Create a simple mini-map using Ogre3D and CEGUI

Objective: Creating a mini-map display using Ogre 3D and CEGUI.

Difficulty: Beginner
Code Length: Short
 
This tutorial explains how to render the whole scene from a top down view and render it to a CEGUI StaticImage.
 
//calculate the height of camera to view the whole scene
 
Ogre::Radian fieldOfView = _camera->getFOVy();
float height = _sceneLength/2 / Ogre::Math::Sin(fieldOfView / 2);
 
Vector3 cameraPosition = Vector3(_sceneLength/2,_sceneWidth/2, height);
Vector3 lookAt = Vector3(_sceneLength/2,_sceneWidth/2, 0);
 
//create a texture for the mini-map to render on
 
TexturePtr texture = TextureManager::getSingleton().createManual(“mmTex”,
   ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D,
   512, 512, 0, PF_R8G8B8, TU_RENDERTARGET );
 
RenderTarget rt = texture->getBuffer()->getRenderTarget();
{
   //create a minimap camera, use the same settings as main camera
   _mmCamera = _sceneManager->createCamera(“minimapCamera”);
   _mmCamera->setNearClipDistance(_camera->getNearClipDistance());
   _mmCamera->setFarClipDistance(_camera->getFarClipDistance());
 
   //AspectRatio(1) is for a square minimap
   _mmCamera->setAspectRatio(1);
   _mmCamera->setPosition(cameraPosition);
   _mmCamera->lookAt(lookAt);
   Viewport *vp = _rt->addViewport(_mmCamera);
   vp->setClearEveryFrame(true);
   vp->setOverlaysEnabled(false);
   vp->setBackgroundColour(ColourValue::Black);
}
 
//Create a CEGUI texture from mmTex
CEGUI::Texture* cetex = GUIManager::instance()->_guiRenderer->createTexture((CEGUI::utf8*) “mmTex”);
 
//CEGUI require an imageset to store the texture
CEGUI::Imageset* imageset = CEGUI::ImagesetManager::getSingleton().createImageset((CEGUI::utf8*)”minimapTexImageset”, cetex);
 
imageset->defineImage((CEGUI::utf8*)”minimapImage”,
CEGUI::Point(0.0f, 0.0f),
CEGUI::Size(cetex->getWidth(), cetex->getHeight()),
CEGUI::Point(0.0f,0.0f));
 
//retrieve the CEGUI StaticImage(window) used to render the minimap
CEGUI::Window* si = CEGUI::WindowManager::getSingleton().getWindow((CEGUI::utf8*)”MiniMap”);
si->setProperty(“Image”, CEGUI::PropertyHelper::imageToString(
   &imageset->getImage((CEGUI::utf8*)|CEGUI::utf8*)”minimapImage”)));