Angie the Anglerfish is hungry, but the fish around her are too fast to eat! Concentrate on the screen to light up her lure and attract the fish to her instead!

Help Angie eat as much sashimi as she can in one minute: Nom nom nom!

Angie the Anglerfish is hungry, but the fish around her are too fast to eat! Concentrate on the screen to light up her lure and attract the fish to her instead!

Help Angie eat as much sashimi as she can in one minute: Nom nom nom!

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.

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);

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));