Game Development Community

dev|Pro Game Development Curriculum

Shader tech behind material accumulation for cliffs

by Konrad Kiss · 11/20/2008 (1:37 pm) · 4 comments


Bitgap Games' Cliff Construction Kit seems to have caused quite an echo, which I am extremely happy about. I've been asked about releasing just the tech needed for material accumulation. Well, I won't do that exactly, I'm not going to give you just the entire thing on a plate, but I am going to talk about how the vertex and pixel shaders work for accumulation.

To understand what I mean by material accumulation, watch this video (especially when the cliff is being rotated):



Granted, this is not real material accumulation, but I find that it looks better than procedural real-time "real" accumulation, and it's a lot faster too.

First of all, let's take the vertex shader.

This is just your everyday TGEA vertex shader with fog and light, that needs to additionally return a vector TEXCOORD that always places texture on top of the shape.

The whole thing is just simple texture projection based on a bump map. That makes it sound easy, and it is, too.

First of all, this needs to be in world space, and needs to be done the same way as if we'd use a light source above the shape. Wherever light would fall (think of it as checking as if there were no other triangles in the shape) would be the place where we should be putting the accumulated material.

But I've run ahead. First we need to see how lights are handled in a vertex shader - how the light vector is created:

float3x3 objToTangentSpace;
   objToTangentSpace[0] = IN.T;
   objToTangentSpace[1] = IN.B;
   objToTangentSpace[2] = normalize(IN.normal);
   
   OUT.lightVec.xyz = -inLightVec;
   OUT.lightVec.xyz = mul(objToTangentSpace, OUT.lightVec);

The secret is that inLightVec is updated at every render, and is presented in object space to the shader. What we know is that we need a vector pointing down, say VectorF(0,0,-1). But that must be in world space, so we need to convert this into object space:

float4 tAccuVec;
   tAccuVec.x = 0;
   tAccuVec.y = 0;
   tAccuVec.z = -1;
   tAccuVec.a = 0;
   
   tAccuVec = mul(objTrans, -tAccuVec);

When you do that, you will realize, that the matrix orders used all around the engine are mixed up. So it won't quite give your pixel shader what it expects. You can't just find it out by looking at it, no. You spend an afternoon sweating and trying to figure out what's borked.

Once it occurred to me, that I saw a comment somewhere abut the matrix order used in Torque - and another saying the very opposite right below it - I figured that the same fun must be happening here. It only took one single command to fix the objTrans matrix to be what we need it to be - before our vector is pushed through it:

objTrans = transpose(objTrans);

That shall do justice. The last step will be multiplying the resulting vector with the objToTangentSpace matrix - just the same way as seen with lights.

On to the pixel shader!

Now, we have a TEXCOORD that shows where we can place accumulated materials. If we call it accuVec, the following will issue plc a color in which we could use ie. the alpha channel to lerp the accumulated texture onto the cliff surface. Since we put the accumulated material on top of the object material based on the object material's bump map, we use bumpNormal here:

float4 plc = saturate( dot(bumpNormal.xyz * 2.0 - 1.0, IN.accuVec.xyz) );

After this, all you need to do is lerp any textures based on plc - diffuse, normal and specular maps are used in the BG Cliff Construction Kit.

That'd be the explanation. As you can see, it's something simple, and that makes it fast.

If you don't feel like reinventing the whole thing (which you could probably do through this explanation if you wanted to), this blog tells you where to buy the Kit.

Thank you for reading through this.

@konradkiss
KonradKiss @ GitHub
konradkiss.com

#1
11/20/2008 (4:03 pm)
Cool, thanks for the info, man! When I first saw the video I started thinking about how it could be accomplished and figured it was something along these lines. =)
#2
11/23/2008 (2:54 am)
Really impressive Konrad! :)
#3
02/02/2009 (2:04 am)
Thanks for posting this. I was able to reinvent the wheel in less than an hour! ;D
#4
02/02/2009 (2:06 am)
Congrats! :) I'm glad it saved you some time, one hour sounds pretty cool!