286 Character Animation with Direct3D//Calculating the binormal and setting //the tangent binormal and normal matrix float3x3 TBNMatrix = float3x3tangent, binormal, normal; //Setting the
Trang 1286 Character Animation with Direct3D
//Calculating the binormal and setting //the tangent binormal and normal matrix float3x3 TBNMatrix = float3x3(tangent, binormal, normal);
//Setting the lightVector OUT.lightVec = mul(TBNMatrix, lightDir);
OUT.lightHalf = mul(TBNMatrix, vHalf);
OUT.tex0 = IN.tex0;
return OUT;
}
//Pixel Shader float4 morphNormalMapPS(VS_OUTPUT IN) : COLOR0 {
//Calculate the color and the normal float4 color = tex2D(DiffuseSampler, IN.tex0);
//This is how you uncompress a normal map float3 normal = 2.0f * tex2D(NormalSampler, IN.tex0).rgb - 1.0f;
//Get specular float4 specularColor = tex2D(SpecularSampler, IN.tex0);
//Set the output color float diffuse = max(saturate(
dot(normal, normalize(IN.lightVec))), 0.2f);
float specular = max(saturate(
dot(normal, normalize(IN.lightHalf))), 0.0f); specular = pow(specular, 85.0f) * 0.4f;
return color * diffuse + specularColor * specular;
}
Trang 2Chapter 12 Wrinkle Maps 287
Figure 12.13 shows another screenshot of the Soldier’s face using somewhat
“exaggerated” highlights Note that this isn’t the kind of result you’d actually want for skin The examples and images in this chapter are a bit exaggerated to emphasize the effect of the specular highlights
EXAMPLE 12.2
Example 12.2 contains all the code for implementing specular highlights Play around with the shininess value in the pixel shader, and if you have good image-editing software, play around with the specular map as well This example also implements specular highlights for the old diffuse lighting model (used for the eyes in the example, which are not normal mapped)
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 3288 Character Animation with Direct3D
WRINKLE MAPS
You’ve now arrived at the goal of this chapter: the wrinkle maps These maps are basically an animated or weighted normal map that is connected to the move-ment of the face For example, smiling may reveal the dimples in the cheeks of the characters These small deformations occur as a result of the underlying muscles
in the face moving Another example of this phenomenon is wrinkles that appear (or disappear) on the forehead as a person raises or lowers his or her eyebrows
FIGURE 12.13
Exaggerated highlights.
Trang 4Chapter 12 Wrinkle Maps 289
The use of wrinkle maps in games is still a recent addition, and most games today don’t bother with it unless the characters are shown in close-up Figure 12.14 shows a grayscale image of a normal map containing wrinkles for the forehead and dimples
Note that the wrinkles in Figure 12.14 have been made somewhat extreme to stand out a bit better (for educational purposes) Normally, wrinkle maps are something you don’t want sticking out like a sore thumb Rather, they should be
a background effect that doesn’t steal too much of the focus Next, you need to encode which regions of the wrinkle map should be affected by which facial movements In the upcoming wrinkle map example, I’ve used a separate texture
to store masking of the wrinkle map regions You could, however, also store this data in the vertex color, for example Figure 12.15 shows the mask used to define the three different regions of the wrinkle map
FIGURE 12.14
Wrinkle normal map.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 5290 Character Animation with Direct3D
In Figure 12.15, three different regions are defined The Red channel defines the part of the face that isn’t affected by animated wrinkles The Green channel defines the forehead wrinkles, and the Blue channel defines the two dimple areas at either side of the mouth To the shader we will upload two blend values (depending on what emotion or shape the character’s face has) These two values are called fore-headWeightand cheekWeight At each pixel, we sample the blend map, multiply the Green channel with the foreheadWeightand the Blue channel with the cheekWeight The resulting value is used to fade the normal in/out from the wrinkle normal map The following code snippet shows how this is done in the pixel shader:
//Pixel Shader float4 morphNormalMapPS(VS_OUTPUT IN) : COLOR0 {
//Sample color float4 color = tex2D(DiffuseSampler, IN.tex0);
//Sample blend from wrinkle mask texture float4 blend = tex2D(BlendSampler, IN.tex0);
//Sample normal and decompress float3 normal = 2.0f * tex2D(NormalSampler, IN.tex0).rgb - 1.0f;
//Calculate final normal weight float w = blend.r + foreheadWeight * blend.g + cheekWeight * blend.b;
FIGURE 12.15
Wrinkle map mask.
Trang 6Chapter 12 Wrinkle Maps 291
w = min( w, 1.0f );
normal.x *= w;
normal.y *= w;
//Re-normalize normal = normalize( normal );
//Normalize the light float3 light = normalize(IN.lightVec);
//Set the output color float diffuse = max(saturate(dot(normal, light)), 0.2f);
return color * diffuse;
}
EXAMPLE 12.3
Example 12.3 has the full implementation for the wrinkle maps You can find how the weights for the forehead and dimples are set in the render method of the Face class.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 7292 Character Animation with Direct3D
Figure 12.16 shows the wrinkle maps in action
Thanks to Henrik Enqvist at Remedy Entertainment for the idea of covering wrinkle maps He also graciously supplied the example code for the wrinkle map example.
CONCLUSIONS
This chapter covered all aspects of normal mapping, from the theory of normal maps to how to create them and how to apply them on a real-time character This base knowledge then allows you to implement the more advanced wrinkle maps as
an animated extension to normal maps I hope you managed to understand all the steps of this somewhat complex process so that you’ll be able to use it in your own projects The DirectX SDK also has skinned characters with high-quality normal maps, which are excellent to play around with
I also touched briefly on implementing a specular lighting model—something that, together with normal maps, really makes your character “shine.” After the slight sidetrack this chapter has taken, I’ll return to more mainstream character animation again Next up is how to create crowd simulations
CHAPTER 12 EXERCISES
Implement normal mapping for the SkinnedMesh class using the code in the
Faceclass as a base
FIGURE 12.16
Wrinkle maps in action.
Trang 8Chapter 12 Wrinkle Maps 293
Implement normal mapping without supplying the binormal from the mesh, but calculate it on-the-fly in the vertex shader
Implement support for multiple lights (for both normal mapping and specular highlights)
FURTHER READING
[Cloward] Cloward, Ben, “Creating and Using Normal Maps.” Available online at http://www.bencloward.com/tutorials_normal_maps1.shtml
[Gath06] Gath, Jakob, “Derivation of the Tangent Space Matrix.” Available online at http://www.blacksmith-studios.dk/projects/downloads/tangent_ matrix_derivation.php, 2006
[Green07] Green, Chris, “Efficient Self-Shadowed Radiosity Normal Mapping.” Available online at http://www.valvesoftware.com/publications/2007/SIGGRAPH 2007_EfficientSelfShadowedRadiosityNormalMapping.pdf, 2007
[Hess02] Hess, Josh, “Object Space Normal Mapping with Skeletal Animation Tutorial.” Available online at: http://www.3dkingdoms.com/tutorial.htm, 2002 [Lengyel01] Lengyel, Eric “Computing Tangent Space Basis Vectors for an Arbitrary Mesh.” Terathon Software 3D Graphics Library Available online at http://www.terathon.com/code/tangent.html, 2001
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 9This page intentionally left blank
Trang 10Crowd Simulation
13
This chapter introduces the concept of crowd simulation and how it can be used in
games to control large groups of NPCs You will first get familiar with the ancestor
of crowd simulation—namely, flocking behaviors With flocking behaviors it is
possible to control a large group of entities, giving them a seemingly complex group behavior using only a few simple rules This idea is then carried over to crowd simulation and extended to create some even more complex behaviors Here’s what will be covered in this chapter:
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 11296 Character Animation with Direct3D
Flocking behaviors
“Boids” implementation Basic crowd simulation Crowd simulation and obstacle avoidance
FLOCKING BEHAVIORS
Let’s start from the beginning Like many other algorithms in computer science, flocking behaviors (aka swarm behaviors or swarm intelligence) try to emulate what already occurs in nature Birds, fish, insects, and many other groups of animals seem
to exhibit something called emergent behavior.
In philosophy, systems theory, and science, emergence is the way complex systems and patterns arise out of a multiplicity of relatively simple interactions Emergence
is central to the theories of integrative levels and of complex systems.
-Wikipedia Flocking algorithms have been around a while now, and in theory they are simple to both understand and implement Flocking algorithms are also lightweight (i.e., not very CPU intensive), which is also a huge plus (especially since some of the flocks can have quite a few entities in them) The theory is to have a set of rules that are evaluated for each entity per frame, to determine where this entity should move The result from the different individual rules are summed up (and often weighted)
to produce the final move direction
One example of this is how ants navigate If an ant is out on a random hike and finds a source of food, it will leave a trail of pheromones on its way back to the stack, carrying with it a piece of the food Another ant on its way out from the stack will encounter this trail and then be more likely to follow it to the food source On its way back, the second ant will lay its own trail of pheromones (reinforcing the first one), making it even more likely that a third ant will follow the first two Once the food source has been depleted, the ants will stop laying the trail and the pheromones will evaporate with time, stopping ants from wasting time down that trail So with these seemingly simple rules that each individual follows, the community as a whole still runs a pretty complex operation This specific example has even spawned its own algorithm called Ant Colony Optimization (ACO), which is used to find good paths through a graph/search space ACO is an adaptable algorithm, which makes it perfect for changing environments For example, if a certain ant trail is blocked, the pheromones will evaporate and the ants will start using other trails instead This tech-nique has been successfully applied to packet routing in networks
Trang 12Chapter 13 Crowd Simulation 297
B OIDS
In 1986, Craig Reynolds made a computer simulation of three simple steering behaviors for a group of creatures he called “Boids.” With only three simple steer-ing rules he managed to show some surprissteer-ingly complex emergent behavior Each Boid just considers a local area around itself and then bases its steering on whatever objects or other Boids are in this area
Separation
The first rule is to make the Boids avoid colliding with other Boids and to avoid
crowding each other This rule is named Separation It calculates a force pointing
away from local neighbors, as shown in Figure 13.1
Alignment
The second rule makes Boids keep the same heading as other Boids This rule is
called Alignment, and it states that a Boid should steer toward the average heading
of its local neighbors This rule is shown in Figure 13.2
FIGURE 13.1
Separation.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 13298 Character Animation with Direct3D
Cohesion
The third and last rule of the Boid steering behaviors is called Cohesion It keeps
the flock together by making a Boid steer toward the center location of its local neighbors This last rule is shown in Figure 13.3
Summing Up
For each frame, these three rules each produce a force vector according to the location of a Boid’s local neighbors For now, let’s just consider these three simple rules (later you’ll see how you can add your own rules to create your own custom behaviors) Figure 13.4 shows the three steering behaviors summed up to produce
FIGURE 13.2
Alignment.
FIGURE 13.3
Cohesion.
Trang 14In Figure 13.4, Fs, Fa, and Fc stand for the forces of the Separation, Alignment, and Cohesion rules, respectively The resulting force F is the force that will be used
to update the velocity and position of the Boid In the upcoming example I’ll use the Boidclass to control an individual entity:
class Boid {
friend class Flock;
public:
Boid(Flock *pFlock);
~Boid();
void Update(float deltaTime);
void Render(bool shadow);
private:
static Mesh* sm_pBoidMesh;
Flock* m_pFlock;
D3DXVECTOR3 m_position;
D3DXVECTOR3 m_velocity;
};
The Boidclass contains a pointer to the flock it belongs to as well as a position and a velocity In the Boid’s Update()function the different steering behaviors and their resulting forces are calculated and used to update the velocity and position of the Boid To manage a flock of Boids, I’ve created the Flockclass like this:
Chapter 13 Crowd Simulation 299
FIGURE 13.4
Summing up the forces.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 15class Flock {
public:
Flock(int numBoids);
~Flock();
void Update(float deltaTime);
void Render(bool shadow);
void GetNeighbors(Boid* pBoid,
float radius, vector<Boid*> &neighbors);
private:
vector<Boid*> m_boids;
};
The only special thing about the Flock class is the GetNeighbors()function, which just fills a list of Boids within a radius of the querying Boid:
void Flock::GetNeighbors(Boid* pBoid,
float radius, vector<Boid*> &neighbors) {
for(int i=0; i<(int)m_boids.size(); i++) {
if(m_boids[i] != pBoid) {
D3DXVECTOR3 toNeighbor;
toNeighbor = pBoid->m_position - m_boids[i]->m_position;
if(D3DXVec3Length(&(toNeighbor)) < radius) {
neighbors.push_back(m_boids[i]);
} } } }
Note that the GetNeighbors()function has a rather nạve implementation in this example For very large flocks it would be unnecessary to loop through the entire flock to find the closest neighbors (especially since we need to do this for each of the entities in the flock) A better way of getting the nearest neighbors would be to use
a space partitioning tree, such as a KD-tree See the following URL for a good introduction to KD-trees:
http://en.wikipedia.org/wiki/Kd-tree
300 Character Animation with Direct3D
Trang 16Chapter 13 Crowd Simulation 301
Since each Boidobject contains a pointer to its flock, it can use the GetNeighbors()
function to find any neighboring Boids Then it is easy to calculate the three rather simple steering behaviors as covered earlier, sum these up, and apply the resulting force to the velocity and position of the Boid The Boid::Update()function shows you how:
void Boid::Update(float deltaTime) {
//Tweakable values const float NEIGHBORHOOD_SIZE = 3.5f;
const float SEPARATION_LIMIT = 2.5f;
const float SEPARATION_FORCE = 15.0f;
const float BOID_SPEED = 3.0f;
//Get neighboring Boids vector<Boid*> neighbors;
m_pFlock->GetNeighbors(this, NEIGHBORHOOD_SIZE, neighbors);
//Forces D3DXVECTOR3 acceleration(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 separationForce(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 alignmentForce(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 cohesionForce(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 toPointForce(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 floorForce(0.0f, 0.0f, 0.0f);
if(!neighbors.empty()) {
//Calculate neighborhood center D3DXVECTOR3 center(0.0f, 0.0f, 0.0f);
for(int i=0; i<(int)neighbors.size(); i++) {
center += neighbors[i]->m_position;
} center /= (float)neighbors.size();
//RULE 1: Separation for(int i=0; i<(int)neighbors.size(); i++) {
D3DXVECTOR3 vToNeighbor = neighbors[i]->m_position -
m_position;
float distToNeightbor = D3DXVec3Length(&vToNeighbor);
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.