DirectX has powerful lighting capabilities, which are especially useful on platforms supporting hardware lighting. We can set the type of light we want to use, its position, color, and several other parameters. Notice, however, that this is per-vertex lighting. We will be illuminating the processed vertices, and interpolation will be used to illuminate the pixels in between successive vertices. If you want to do per-pixel illumination, you need to use techniques such as light mapping, which is covered in detail in Chapter 17, "Shading." Direct3D shading is accessed using very intuitive methods. We only need to define the light sources and their properties, and how each surface material will respond to them. To begin with, the core of Direct3D's lighting capabilities lies in the D3DLIGHT9 structure, which has the following attributes: 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; The first parameter is the type of light source we want to use. Point lights, spotlights, and directional lights are all supported. Then, we have three color values to specify: the ambient, diffuse, and specular components of the light color. We can then specify the position (for point and spotlights) and direction (for directional and spotlight only) vectors. The next parameters are specific to spotlights. Range is used to control how light decays with distance. Points located further than range from the light source will not receive any light. Falloff represents the amount of decay between a spotlight's internal and external cones. Then, the three attenuation parameters are used to describe an envelope of the light's attenuation depending on distance. Basically, attenuation is computed as the sum of a cubic polynomial over the distance from the point being lit to the light source: Attenuation 0 is independent to distance, attenuation 1 is linear to distance, and so on. By using these values, we can implement exotic attenuation curves. As a summary, here is the law that describes how the parameters interact: Atten = 1/( attenuation0 + attenuation1 * d + attenuation2 * d2) Notice that directional lights have attenuation set to one regardless of the distance to the lamp and are thus not attenuated. For all other light types, points beyond the range are attenuated by a value of zero, and thus no light is ever received. Additionally, theta is the angle, in radians, of a spotlight's inner cone that is, the fully illuminated spotlight cone. This value must be in the range from 0 through the value specified by Phi, which is the angle, in radians, that defines the outer edge of the spotlight's outer cone. Points outside this cone are not lit by the spotlight. This value must be between 0 and pi. Take a look at Figure C.2 for a clarifying diagram. Figure C.2. Structure and parameters of a spotlight in DirectX.Let's take a look at a working example in which we define a spotlight: D3DXVECTOR3 vecDir, vecPos; D3DLIGHT9 light; ZeroMemory( &light, sizeof(D3DLIGHT9) ); light.Type = D3DLIGHT_SPOTLIGHT; light.Diffuse.r = 1.0f; light.Diffuse.g = 1.0f; light.Diffuse.b = 1.0f; vecDir = D3DXVECTOR3(0,-1,0); D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &vecDir ); VecPos = D3DXVECTOR3(0,10,0); D3DXVec3Normalize( (D3DXVECTOR3*)&light.Position, &vecPos ); light.Range = 1000.0f; light.Phi=1; light.Theta=0.8; g_pd3dDevice->SetLight( 0, &light ); g_pd3dDevice->LightEnable( 0, TRUE ); g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE ); Notice how we end the sequence by sending the newly created light to the Direct3D device. In addition, we must enable lighting and activate the lighting render state for lights to be taken into consideration. But we also need to set materials: Light interaction with vertices requires the latter to be assigned a material, so we can simulate how light bounces on different kinds of surfaces. This is achieved with the following D3DMATERIAL9 structure: typedef struct _D3DMATERIAL9 { D3DCOLORVALUE Diffuse; D3DCOLORVALUE Ambient; D3DCOLORVALUE Specular; D3DCOLORVALUE Emissive; float Power; } D3DMATERIAL9; As with lights, the first three parameters are used to specify the color of the surface. In case the surface and light colors are different, lighting equations will blend the two. The last parameter is the typical exponent found in all specular highlights equations, which helps simulate different smoothness levels. Lower values can be used for rough materials, whereas higher values represent polished surfaces like metals or plastic. Setting a material is not very different from setting lights, as you can see in the following example: D3DMATERIAL9 mtrl; ZeroMemory( &mtrl, sizeof(D3DMATERIAL9) ); mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f; mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f; mtrl.Diffuse.b = mtrl.Ambient.b = 0.0f; mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f; mtrl.Emissive= 5.0f; g_pd3dDevice->SetMaterial( &mtrl ); A final comment on Direct3D's lighting is that often you will need to know the number of light sources supported by the device you are using. If your light number goes beyond that limit, illumination will be computed on the software driver, with the predictable loss of performance. Then, to detect the number of lights supported by the hardware, we only need to read the MaxActiveLights attribute of the D3DCAPS9 structure. Here is the source code for such a query: D3DCAPS9 caps; GetDeviceCaps(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,&caps); int num=caps.MaxActiveLights; |