The following topics will be covered in this chapter: Single hairs versus strips of hair Generating hair strips from control splines Animating control splines HAIR REPRESENTATION OK, so
Trang 1Because I’m using a plane (calculated from the hit position and the triangle normal) to calculate the UV coordinates of the decal, there will be some stretching when the decal is applied to curved surfaces You can see this in Figure 14.15 where the positions of the vertices in the texture surface aren’t spread out uniformly (even though the distance on the mesh surface between the vertices is)
This is a problem related to that of using the straight line distance from the hit
to the vertex described earlier If you can calculate the actual distance over the mesh surface from the hit position to the vertex, then you can also improve on the
UV coordinate calculation
CONCLUSIONS
This chapter introduced the concept of adding real-time scratches, bullet holes, etc.,
to your character using decals Decals are a great way of adding extra detail or ran-domness to your characters without having a major impact on texture memory One thing I haven’t covered in this chapter is how to handle a large number
of decals In a first-person shooter game, for example, the number of bullet-hole-induced decals can quickly rise to several thousands At some point you need to start removing the decals from the scene or your frame rate will start to drop Usually, you’ll need to implement some form of decal manager that keeps track
of all the decals in the world and which can remove them according to:
FIGURE 14.15
Decal UV coordinates over a curved surface.
Trang 2Decal position (relative to the player) Decal age
First-in, first-out Once a decal has been flagged for removal, you can either just pop it out of existence, fade it out, or wait until the player is looking the other way and then remove it (you can easily check the decal position against the player view frustum) One big improvement to this decal system would also be to have normal-mapped decals, making it possible to add dents, etc., to the character Hopefully you’ve gotten enough out of this chapter to be able to create your own decal system (with all the possible improvements mentioned in this chapter and more)
CHAPTER 14 EXERCISES
Also create decals for static meshes (pulse rifle, helmet) Note that you need to disregard the skinning information when creating the decal mesh; otherwise, it
is pretty much the same process
Use the D3DXIntersect method to find all intersections of a mesh Use this also
to create exit wounds of the ray
Create a normal-mapped wound decal
Create a particle system and tie it to the decal (e.g., blood pouring from a wound)
Make a more accurate implementation of the “triangle-bounding sphere” test used to determine whether or not a triangle belongs to the decal mesh Find the point on the triangle closest to the bounding sphere
Use different decals for the character’s armor and the face
Trang 4Hair Animation
15
In this chapter I’ll briefly cover the topic of dynamic hair animation When I say
“hair animation” I don’t mean a simple pony tail animated with a few bones, but the generic “any-hair-style” case This topic is closely linked to the topic of cloth animation (they share many similarities)
Hair animation is a component that most games leave out completely The primary reason is because it is quite costly (thousands of small triangles are required, which can, in most cases, give you more bang-for-your-buck somewhere else) The second reason is that it is very hard to get hair animation to look good
So, because of these issues, most developers just opt for leaving out animated hair
Trang 5completely from their game characters Usually, they stick with either a static mesh for the characters’ hair or cover the heads with some form of helmets or other headwear
The following topics will be covered in this chapter:
Single hairs versus strips of hair Generating hair strips from control splines Animating control splines
HAIR REPRESENTATION
OK, so you want to have a dynamically animated haircut for your game character Well, the first issue to solve is how to represent, or render, the hair The two most common ways to represent hair are either as a bunch of splines or as a set of meshes (strips) with an alpha-blended hair texture (see Figure 15.1)
FIGURE 15.1
Individual hair strands versus hair strips.
Trang 6NVIDIA has made a very nice demo of a mermaid, which renders individual hair strands as splines Although this option may be viable in future games, it is, at the time this book was written, a far too costly option (nevertheless, I strongly recommend you check out NVIDIA’s Nalu demo) You can find a really good article about the hair animation in this demo at the following URL:
http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter23.html
It seems rendering/simulating individual hair strands is a bit too expensive for today’s hardware This leaves us with the option of simulating multiple hair strands using a mesh strip and a texture
HAIR MODELING
The problem of creating the hair mesh for the character can be approached in many ways The most straightforward approach to grasp is, of course, to do it by hand Simply model and texture all the hair strips of the character in modeling software like 3D Studio Max, Maya, or Lightwave, etc Then, import this hair mesh into your game somehow and animate and render it This is, of course, a rather expensive process and one that requires considerable artistic talent (something that, not surprisingly, most programmers lack) It also means that for each new haircut you will have to do all the work over again from scratch So, instead, let’s try to make a system for this and “grow” hair for our characters in code
This will be done by importing just a few splines (aka control splines) from some modeling software and then generating the hairs (either strands or strips) between the control splines in code Figure 15.2 shows an example of this
FIGURE 15.2
Example of control hairs and interpolated hairs.
Trang 7Even though Figure 15.2 only shows how this could be used to generate hair strands, you can easily use this technique to generate strips as well In either case, this technique lets you (or your artist) quickly create or shape new haircuts without all the manual polygonal modeling and texturing
THE CONTROL HAIR CLASS
Let’s start by looking at a single control hair The control hair consists of a list of points that define the location of the control hair Since the control hairs will be used only for the growing of the more detailed hair strips and to update the hair simulation, it is good to keep the number of points used in the control hair to a minimum Just because the control hair has a low level of detail (i.e., few points) doesn’t mean the final hair strips will, since these are cubically interpolated from the points in the control hair, as shown in Figure 15.3
This is a great thing, because it gives you complete control over the amount of polygons and detail you have for your haircuts (as opposed to pre-created hair meshes) To represent a control hair, I’ve created the following class:
class ControlHair {
public:
ControlHair();
float GetSegmentPercent(float prc);
pair<int, int> GetBlendIndices(float prc);
D3DXVECTOR3 GetBlendedPoint(float prc);
FIGURE 15.3
Control hair and generated strip.
Trang 8void Render();
public:
vector<D3DXVECTOR3> m_points;
};
As said before, the control hair just consists of a list of control points The
GetSegmentPercent(), GetBlendIndices(), and GetBlendedPoint()helper functions are used when calculating the cubically blended spline These three functions take
a percentage float value in the range 0 to 1, where 0 is the start of the hair and 1 is the end point of the hair Figure 15.4 shows how these three functions are used
FIGURE 15.4
The control hair helper functions.
Trang 9Figure 15.4 shows the result if you pass 55% (or 0.55f) to these three helper func-tions The first helper function, GetSegmentPercent(), converts a percentage value from the whole hair to a percentage value between two points of the hair So first you need to calculate between which two points the value lies and then calculate the new percentage value as shown in code here:
float ControlHair::GetSegmentPercent(float prc) {
//Calculate percentage step between two points in the control hair float step = 1.0f / (float)(m_points.size() - 1);
int numSteps = (int)(prc / step);
//Convert prc to the [0-1] range of the segment return (prc - numSteps * step) / step;
}
Next is the GetBlendIndices()function This function retrieves the two indices
of the segment described by a certain hair percentage value as shown here:
pair<int, int> ControlHair::GetBlendIndices(float prc) {
//Less than zero if(prc <= 0.0f) return pair<int, int>(0, 0);
//Greater than one if(prc >= 1.0f) return pair<int, int>(
(int)m_points.size() - 1, (int)m_points.size() - 1);
//Get first segment index int index1 = (int)(prc * (m_points.size() - 1));
//Get second segment index (no greater than num points - 1) int index2 = min(index1 + 1, (int)m_points.size() - 1);
return pair<int, int>(index1, index2);
}
Trang 10This function simply figures out which two points define the segment on which
a certain percentage value lies This information must be known when you attempt
to blend between two points, which coincidentally is something that the next helper function does To get smooth blending between the control points (as opposed to basic linear interpolation, which would produce very blocky hairs), I use cubic interpolation for the hair strips Consider the points shown in Figure 15.5
A cubically interpolated point (p n) needs to take the four neighboring points
(p 1 , p 2 , p 3 and p 4 ) into consideration The point p ncan be calculated according to
the following formula, where t is the percentage between the points p 1 and p 2:
P = (p 3 – p 2 )–(p 0 – p 1)
Q = (p 0 – p 1 ) – P
R = (p 2 – p 0)
S = p 1
p n = P t 3 + Q t 2 + R t + S
This formula is used in the GetBlendedPoint()function, which makes use of the two previous helper functions to return a cubically blended point along the control hair:
D3DXVECTOR3 ControlHair::GetBlendedPoint(float prc) {
//Get two affected indices pair<int, int> indices = GetBlendIndices(prc);
//Get the four point indices int index0 = max(indices.first - 1, 0);
FIGURE 15.5
Cubic interpolation.
Trang 11int index1 = indices.first;
int index2 = indices.second;
int index3 = min(indices.second + 1, (int)m_points.size() - 1);
//Get segment percentage float t = GetSegmentPercent(prc);
//Perform the cubic interpolation D3DXVECTOR3 P = (m_points[index3] - m_points[index2]) –
(m_points[index0] - m_points[index1]);
D3DXVECTOR3 Q = (m_points[index0] - m_points[index1]) - P;
D3DXVECTOR3 R = m_points[index2] - m_points[index0];
D3DXVECTOR3 S = m_points[index1];
return (P * t * t * t) + (Q * t * t) + (R * t) + S;
}
That about covers the control hair Later on, when you simulate the hair, all you need to do is update the points in your control hairs and the rest of the haircut will follow suit The next step is to actually create the strips from these control hairs After that you’ll have something “hair-ish” that can be rendered onto the screen
THE HAIRPATCH CLASS
The nạve way of implementing the hair strips would be to have a single mesh for each
of the hair strips and perhaps use some form of skinning to implement an animated hair strip However, this is very inefficient! You’re bound to have hundreds of hair strips per haircut, so to improve performance you need to bundle strips together in one mesh to reduce the number of draw calls necessary to render the hair So for this purpose I’ve created the HairPatchclass, which implements a patch of hair defined
as the area between four control hairs Each of the four control hairs marks one corner of the “squarish” area of the hair patch Figure 15.6 shows how the hair patch will be built
Trang 12Note that all the strips in Figure 15.6 belong to the same mesh The idea is that
as one of the control hairs bends or animates, the hair strips close to this control hair will be influenced and bend in a similar manner A strip placed exactly in the middle of the hair patch will be influenced equally by the four control hairs On the other hand, a strip placed in the exact same spot as a control hair will only be influenced by this control hair and no others
class HairPatch {
public:
HairPatch(ControlHair* pCH1,
ControlHair* pCH2, ControlHair* pCH3, ControlHair* pCH4);
~HairPatch();
D3DXVECTOR3 GetBlendedPoint(D3DXVECTOR2 pos, float prc);
HairVertex GetBlendedVertex(D3DXVECTOR2 pos,
float prc, bool oddVertex);
FIGURE 15.6
The hair patch.
Trang 13vector<D3DXVECTOR2> GetStripPlacements(float sizePerHairStrip);
void CreateHairStrips(int numSegments,
float sizePerHairStrip, float stripSize);
void Render();
public:
ID3DXMesh* m_pHairMesh;
ControlHair* m_controlHairs[4];
};
The HairPatchclass keeps four pointers to control hairs (which are set in the constructor) as well as an ID3DXMeshobject that the class is responsible for creating and rendering The GetBlendedPoint()function returns a point anywhere on the patch along a certain percentage (again, zero being the base of the skull and one being the tip of the hairs):
D3DXVECTOR3 HairPatch::GetBlendedPoint(D3DXVECTOR2 pos, float prc) {
//Get blended points along the control hairs (for this prc) D3DXVECTOR3 p1 = m_controlHairs[0]->GetBlendedPoint(prc);
D3DXVECTOR3 p2 = m_controlHairs[1]->GetBlendedPoint(prc);
D3DXVECTOR3 p3 = m_controlHairs[2]->GetBlendedPoint(prc);
D3DXVECTOR3 p4 = m_controlHairs[3]->GetBlendedPoint(prc);
//Perform a linear 2D blend D3DXVECTOR3 blendedX1 = p2 * pos.x + p1 * (1.0f - pos.x);
D3DXVECTOR3 blendedX2 = p3 * pos.x + p4 * (1.0f - pos.x);
return blendedX2 * pos.y + blendedX1 * (1.0f - pos.y);
}
First you retrieve the blended position from each of the four control hairs (for the desired percentage) Then you need to perform a 2D blend depending on the hairs’ position on the patch (i.e., the closer a hair is to one of the control hairs, the more this control hair will influence it) Now that you can calculate a point anywhere on the hair patch it is quite simple to start generating the hair strips
Trang 14G ROWING THE H AIR
Before you actually create the hair strips, there are some questions that need answering First is how detailed you want to make the strips (i.e., how many faces/vertices per strip), and second is how tightly you want to pack the strips Here again is where you enter the gray zone of performance versus what looks good Most likely this will differ from one situation to the next, depending on what performance requirements the game you’re working on has However, with this way of growing the hair dynamically you can even plug in these two values in an LOD system Simply increase the number of strips and the detail of these the closer
to the camera a character is
Let’s start with the simpler of the two problems—namely, where to place the strips One way is, of course, to place them uniformly by creating a uniform grid between the four control hairs and placing a hair strip at each point of this grid This tends to create a bit too-regular-looking hair patch You can easily get a better looking hair patch using fewer strips by simply placing them at random One important restriction is to make sure that no two strips are placed too close to each other The GetStripPlacements()function in the HairPatchclass implements this:
vector<D3DXVECTOR2> HairPatch::GetStripPlacements(
float sizePerHairStrip) {
//Place hair strips at random vector<D3DXVECTOR2> strips;
for(int i=0; i<200; i++) {
//Create random hair position D3DXVECTOR2 hairPos = D3DXVECTOR2((rand()%1000) / 1000.0f,
(rand()%1000) / 1000.0f);
//Check that this hair isn’t too close to another hair bool valid = true;
for(int h=0; h<(int)strips.size() && valid; h++) {
D3DXVECTOR3 diff = hairPos - strips[h];
if(D3DXVec2Length(&diff) < sizePerHairStrip) valid = false;
}