Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Introduction to 3D Game Programming with DirectX.9.0 - F. D. Luna

.pdf
Скачиваний:
239
Добавлен:
24.05.2014
Размер:
6.94 Mб
Скачать

104 Chapter 5

renormalize all of your normals after the transformation stages by enabling the D3DRS_NORMALIZENORMALS render state:

Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);

5.4 Light Sources

Direct3D supports three types of light sources.

 

 

 

 

Y

Point lights—This light source has a position in world space and

 

emits light in all directions.

 

 

 

 

 

 

L

 

 

F

 

M

 

 

EA

 

Figure 5.4: A point light

 

 

 

 

Directional lights—This light source has no position but shoots parallel rays of light in the specified direction.

Figure 5.5: Directional light

Spot lights—This type of light source is similar to a flashlight; it

has a position and shines light through a conical shape in a particular direction. The cone is characterized by two angles, and . The angle describes an inner cone, and describes the outer cone.

Figure 5.6: A spotlight

Team-Fly®

Lighting 105

In code a light source is represented with the D3DLIGHT9 structure.

typedef struct _D3DLIGHT9 { D3DLIGHTTYPE Type; D3DCOLORVALUE Diffuse; D3DCOLORVALUE Specular; D3DCOLORVALUE Ambient; D3DVECTOR Position; D3DVECTOR Direction; float Range;

float Falloff; float Attenuation0; float Attenuation1; float Attenuation2; float Theta;

float Phi;

} D3DLIGHT9;

 

Type—Defines the type of light that we are making and can be one

II

art

of the following three types: D3DLIGHT_POINT, D3DLIGHT_

SPOT, D3DLIGHT_DIRECTIONAL

P

 

Diffuse—The color of diffuse light that this source emits

 

 

Specular—The color of specular light that this source emits

 

Ambient—The color of ambient light that this source emits

 

Position—A vector describing the world position of the light

 

source. This value is meaningless for directional lights.

 

Direction—A vector describing the world direction that the light

 

is traveling. This value is not used for point lights.

 

Range—The maximum range that the light can travel before it

 

“dies.” This value cannot be greater than FLT _ MAX and has no

 

effect on directional lights.

 

Falloff—This value is used only for spotlights. It defines how the light’s intensity weakens from the inner cone to the outer cone. This value is generally set to 1.0f.

Attenuation0, Attenuation1, Attenuation2—The attenuation variables are used to define how the intensity of light weakens over distance. These variables are only used for point and spot lights. The attenuation0 variable defines a constant falloff, attenuation1 defines the linear falloff, and attenuation2 defines the quadratic falloff. Calculated using this formula, where D

is distance from light source and

Attenuation =

 

 

1

A0, A1, A2 correspond to Attenu-

A A D A D2

 

0

1

2

ation 0, 1, and 2.

Theta—Used for spotlights only; specifies inner cone angle in radians.

Phi—Used for spotlights only; specifies outer cone angle in radians.

106 Chapter 5

Like initializing a D3DMATERIAL9 structure, initializing a D3DLIGHT9 structure can also be tedious when you only want a simple light. We add the following functions to the d3dUtility.h/cpp files to initialize simple lights:

namespace d3d

{

.

.

.

D3DLIGHT9 InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color);

D3DLIGHT9 InitPointLight(D3DXVECTOR3* position,

D3DXCOLOR* color);

D3DLIGHT9 InitSpotLight(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color);

}

The implementation of these functions is straightforward. We will only show the implementation of InitDirectionalLight. The others are similar:

D3DLIGHT9 d3d::InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color)

{

D3DLIGHT9 light; ::ZeroMemory(&light, sizeof(light));

light.Type

= D3DLIGHT_DIRECTIONAL;

light.Ambient

= *color * 0.4f;

light.Diffuse

= *color;

light.Specular

= *color * 0.6f;

light.Direction = *direction;

return light;

}

Then to create a directional light that runs parallel with the x-axis in the positive direction and emits white light, we would write:

D3DXVECTOR3 dir(1.0f, 0.0f, 0.0f); D3DXCOLOR c = d3d::WHITE;

D3DLIGHT9 dirLight = d3d::InitDirectionalLight(&dir, &c);

After we have initialized a D3DLIGHT9 instance, we need to register with an internal list of lights that Direct3D maintains. We do this like so:

Device->SetLight(

0, // element in the light list to set, range is 0-maxlights &light);// address of the D3DLIGHT9 structure to set

Lighting 107

Once a light is registered, we can turn it on and off using what this next example illustrates:

Device->LightEnable(

0, // the element in the light list to enable/disable true); // true = enable, false = disable

5.5 Sample Application: Lighting

The sample for this chapter creates the scene shown in Figure 5.7. It demonstrates how to specify vertex normals, how to create a material, and how to create and activate a directional light. Note that in this sample program we do not make use of the d3dUtility.h/cpp material and light functionality code because we want to show how it is done manually first. However, the rest of the samples in this book do use the material and light utility code.

P a r t I I

Figure 5.7: Screen shot from the LitPyramid sample

The steps for adding light to a scene are:

1.Enable lighting.

2.Create a material for each object and set the material before rendering the corresponding object.

3.Create one or more light sources, set the light sources, and enable them.

4.Enable any additional lighting states, such as specular highlights.

First we instantiate a global vertex buffer that stores the pyramid’s vertices:

IDirect3DVertexBuffer9* Pyramid = 0;

The Setup function contains all the code relevant to this chapter, so we omit the other functions to save space. It implements the previously discussed steps to add lighting to a scene. The Setup method starts by

108 Chapter 5

enabling lighting, which isn’t necessary because it’s enabled by default (but it doesn’t hurt either).

bool Setup()

{

Device->SetRenderState(D3DRS_LIGHTING, true);

Next, we create the vertex buffer, lock it, and specify the vertices that form triangles of the pyramid. The vertex normals were precomputed using the algorithm covered in section 5.3. Notice that while the triangles share vertices, they do not share normals; thus it is not very advantageous to use an index list for this object. For example, all the triangles share the peak point (0, 1, 0); however, for each triangle, the peak vertex normal points in a different direction.

Device->CreateVertexBuffer(

12 * sizeof(Vertex), D3DUSAGE_WRITEONLY, Vertex::FVF, D3DPOOL_MANAGED, &Pyramid,

0);

//fill the vertex buffer with pyramid data Vertex* v;

Pyramid->Lock(0, 0, (void**)&v, 0);

//front face

v[0] = Vertex(-1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f); v[1] = Vertex( 0.0f, 1.0f, 0.0f, 0.0f, 0.707f, -0.707f); v[2] = Vertex( 1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);

// left face

v[3] = Vertex(-1.0f, 0.0f, 1.0f, -0.707f, 0.707f, 0.0f); v[4] = Vertex( 0.0f, 1.0f, 0.0f, -0.707f, 0.707f, 0.0f); v[5] = Vertex(-1.0f, 0.0f, -1.0f, -0.707f, 0.707f, 0.0f);

// right face

v[6] = Vertex( 1.0f, 0.0f, -1.0f, 0.707f, 0.707f, 0.0f); v[7] = Vertex( 0.0f, 1.0f, 0.0f, 0.707f, 0.707f, 0.0f); v[8] = Vertex( 1.0f, 0.0f, 1.0f, 0.707f, 0.707f, 0.0f);

// back face

v[9] = Vertex( 1.0f, 0.0f, v[10] = Vertex( 0.0f, 1.0f, v[11] = Vertex(-1.0f, 0.0f,

1.0f, 0.0f, 0.707f, 0.707f); 0.0f, 0.0f, 0.707f, 0.707f); 1.0f, 0.0f, 0.707f, 0.707f);

Pyramid->Unlock();

After we have generated the vertex data of our object, we describe how the object interacts with light by describing its materials. In this sample, the pyramid reflects white lights, emits no light, and produces some highlights.

Lighting 109

D3DMATERIAL9 mtrl;

 

mtrl.Ambient

= d3d::WHITE;

mtrl.Diffuse

= d3d::WHITE;

mtrl.Specular

= d3d::WHITE;

mtrl.Emissive

= d3d::BLACK;

mtrl.Power

= 5.0f;

Device->SetMaterial(&mtrl);

Second to last, we create and enable a directional light. The directional light rays run parallel to the x-axis in the positive direction. The light emits strong white diffuse light (dir.Diffuse = WHITE), weak white specular light (dir.Specular = WHITE * 0.3f), and a medium amount of white ambient light (dir.Ambient = WHITE * 0.6f).

D3DLIGHT9 dir;

::ZeroMemory(&dir, sizeof(dir));

dir.Type

= D3DLIGHT_DIRECTIONAL;

dir.Diffuse

= d3d::WHITE;

dir.Specular

= d3d::WHITE * 0.3f;

dir.Ambient

= d3d::WHITE * 0.6f;

dir.Direction

= D3DXVECTOR3(1.0f, 0.0f, 0.0f);

Device->SetLight(0, &dir);

Device->LightEnable(0, true);

Finally, we set the state to renormalize normals and enable specular highlights.

Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);

Device->SetRenderState(D3DRS_SPECULARENABLE, true);

//... code to set up the view matrix and projection matrix

//omitted

return true;

}

5.6 Additional Samples

P a r t I I

Three additional samples are included for this chapter in the companion files. They use the D3DXCreate* functions to create the 3D objects that compose the scene. The D3DXCreate* functions create vertex data with the format D3DFVF_XYZ | D3DFVF_NORMAL. In addition these functions compute the vertex normals of each mesh for us. The additional samples demonstrate how to use directional lights, point lights, and spot lights. Figure 5.8 shows a screen shot from the directional light sample.

110 Chapter 5

Figure 5.8: A screen shot from the DirectionalLight sample

5.7Summary

Direct3D supports three light source models: directional lights, point lights, and spot lights. Light sources emit three types of light: ambient light, diffuse light, and specular light.

The material of a surface defines how light interacts with the surface that it strikes (that is, how much light is reflected and absorbed, thus determining the color of the surface).

Vertex normals are used to define the orientation of a vertex. They are used so that Direct3D can determine the angle at which a ray of light strikes the vertex. In some cases, the vertex normal is equal to the normal of the triangle that it forms, but this is not usually the case when approximating smooth surfaces (e.g., spheres, cylinders).

Chapter 6

Texturing

Texture mapping is a technique that allows us to map image data onto a triangle; this capability allows us to increase the details and realism of our scene significantly. For example, we can build a cube and turn it into a crate by mapping a crate texture to each side (see Figure 6.1).

Figure 6.1:

A cube with a crate texture

In Direct3D a texture is represented with the IDirect3DTexture9 interface. A texture is a matrix of pixels similar to a surface but can be mapped to triangles.

Objectives

To learn how to specify the part of a texture that gets mapped to the triangle

To find out how to create textures

To learn how textures can be filtered to create a smoother image

111

112 Chapter 6

6.1 Texture Coordinates

Direct3D uses a texture coordinate system that consists of a u-axis that runs horizontally and a v-axis that runs vertically. A pair of u, v coordinates identifies an element on the texture called a texel. Notice that the v-axis is positive in the “down” direction (see Figure 6.2).

Figure 6.2: The texture coordinate system, sometimes called texture space

Also, notice the normalized coordinate interval, [0, 1], which is used because it gives Direct3D a fixed range to work with textures of various dimensions.

For each 3D triangle, we want to define a corresponding triangle on the texture that is to be mapped to the 3D triangle (see Figure 6.3).

Figure 6.3: On the left is a triangle in 3D space, and on the right we define a 2D triangle on the texture that is going to be mapped onto the 3D triangle.

Texturing

To do this, we modify our vertex structure once again and add a pair of texture coordinates that identifies a vertex on the texture.

struct Vertex

{

float _x, _y, _z; float _nx, _ny, _nz;

float _u, _v; // texture coordinates

static const DWORD FVF;

};

const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;

Observe that we have added D3DFVF_TEX1 to our vertex format description, which says that our vertex structure contains one pair of texture coordinates.

Now every triangle built from three Vertex objects also defines a corresponding texture triangle from the texture coordinates.

Note: Although we specify a corresponding texture triangle to a 3D triangle, the texture isn’t mapped until the rasterization stage where the 3D triangle has been transformed to screen space.

113

P a r t I I

6.2 Creating and Enabling a Texture

Texture data is usually read from an image file stored on disk and loaded into an IDirect3DTexture9 object. To do this, we can use the following D3DX function:

HRESULT D3DXCreateTextureFromFile(

LPDIRECT3DDEVICE9 pDevice,

//

device to create the

texture

LPCSTR pSrcFile,

//

filename of image to

load

LPDIRECT3DTEXTURE9* ppTexture // ptr to receive the created texture );

This function can load any of the following image formats: BMP, DDS, DIB, JPG, PNG, and TGA.

For example, to create a texture from an image called stonewall.bmp, we would write the following:

IDirect3Dtexture9* _stonewall;

D3DXCreateTextureFromFile(_device, "stonewall.bmp", &_stonewall);

To set the current texture, we use the following method:

HRESULT IDirect3DDevice9::SetTexture(

DWORD Stage, // A value in the range 0-7 identifying the texture // stage – see note on Texture Stages

IDirect3DBaseTexture9* pTexture // ptr to the texture to set );