Tutorial 4: creating and working with lights

Posted by ineedhelp512 on Sat, 12 Feb 2022 04:27:17 +0100

Direct3D lights add more realism to 3D objects. When used, each geometric object in the scene will be illuminated according to the position and type of light used. The sample code in this tutorial introduces the topics of lights and materials.

This tutorial contains the following steps to create materials and lights.

pace
Step 1 - initialize scene geometry

Step 2 - set materials and lights

Note

The path of the Lights sample project is:
(SDK root) \ Samples\C++\Direct3D\Tutorials\Tut04_Lights

The sample code in the Lights project is almost the same as that in the Matrices project. The create and use Lights tutorial focuses only on the unique code for creating and using Lights, and does not include setting up Direct3D, processing Windows messages, rendering, or turning off. For information about these tasks, see tutorial 1: creating devices.

This tutorial uses custom vertices and vertex buffers to display geometry. For more information on selecting custom vertex types and implementing vertex buffers, see tutorial 2: rendering vertices.

This tutorial uses matrices to transform geometry. For more information about matrices and transformations, see tutorial 3: working with matrices.

Step 1 - initialize scene geometry

One of the requirements for using Lights is that each surface has a normal. For this purpose, the Lights sample project uses different custom vertex types. The new custom vertex format has 3D positions and surface normals. Surface normals are used internally by Direct3D for lighting calculations.

struct CUSTOMVERTEX
{
    D3DXVECTOR3 position; // The 3D position for the vertex.
    D3DXVECTOR3 normal;   // The surface normal for the vertex.
};
// Custom flexible vertex format (FVF).
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL)

Now that the correct vector format is defined, the Lights sample project calls InitGeometry, an application definition function that creates a cylinder. The first step is to create a vertex buffer to store the points of the cylinder, as shown in the following example code.

// Create the vertex buffer.
if( FAILED( g_pd3dDevice->CreateVertexBuffer( 50*2*sizeof(CUSTOMVERTEX),
                                           0 /*Usage*/, D3DFVF_CUSTOMVERTEX,
                                           D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
    return E_FAIL;

The next step is to fill the vertex buffer with the points of the cylinder. Note that in the following example code, each point is defined by position and normal.

CUSTOMVERTEX* pVertices;
if( FAILED( g_pVB->Lock( 0, 0, (void**)&pVertices, 0 ) ) ) return E_FAIL;

for( DWORD i=0; i<50; i++ )
{
    FLOAT theta = (2*D3DX_PI*i)/(50-1);
    pVertices[2*i+0].position = D3DXVECTOR3( sinf(theta),-1.0f, cosf(theta) );
    pVertices[2*i+0].normal   = D3DXVECTOR3( sinf(theta), 0.0f, cosf(theta) );
    pVertices[2*i+1].position = D3DXVECTOR3( sinf(theta), 1.0f, cosf(theta) );
    pVertices[2*i+1].normal   = D3DXVECTOR3( sinf(theta), 0.0f, cosf(theta) );
}

After the previous example code fills the vertex buffer with the vertices of the cylinder, the vertex buffer can be rendered. But first, you must set the materials and lights for this scene before rendering the cylinder. This is described in step 2 - setting up materials and lights.

Step 2 - set materials and lights

To use lighting in Direct3D, you must create one or more Lights. In order to determine the color reflected by geometric objects, a material for rendering geometric objects was created. Before rendering the scene, the Lights sample project calls SetupLights, an application defined function that sets up a material and an directional light.

  • Create material
  • Create light

1. Create materials

Materials define the color that light reflects when it hits the surface of a geometric object. The following code snippet uses the D3DMATERIAL9 structure to create a yellow material.

D3DMATERIAL9 mtrl;
ZeroMemory( &mtrl, sizeof(mtrl) );
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;
g_pd3dDevice->SetMaterial( &mtrl );

The material's diffuse and ambient colors are set to yellow. The call to the IDirect3DDevice9::SetMaterial method applies the material to the Direct3D device used to render the scene. Idirect3ddevice9:: the only parameter accepted by setmaterial is the address of the material to be set. After this call, each element is rendered with this material until another call is made to IDirect3DDevice9::SetMaterial assigned a different material.

Now that you have applied materials to the scene, the next step is to create lights.

2. Create lights

There are three types of lights available in Direct3D:

  • Light up
  • Directional lamp
  • Spotlight

The sample code creates a directional light, which is a light traveling in one direction. The code also oscillates the direction of the light.

The following code snippet uses the D3DLIGHT9 structure to create directional light.

D3DXVECTOR3 vecDir;
D3DLight9 light;
ZeroMemory( &light, sizeof(light) );
light.Type = D3DLIGHT_DIRECTIONAL;

The following code snippet sets the diffuse color of this light to white.

light.Diffuse.r = 1.0f;
light.Diffuse.g = 1.0f;
light.Diffuse.b = 1.0f;

The following code snippet rotates the direction of the light by one turn.

vecDir = D3DXVECTOR3(cosf(timeGetTime()/360.0f),
                     0.0f,
                     sinf(timeGetTime()/360.0f) );
D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &vecDir );

The call to D3DXVec3Normalize normalizes the direction vector used to determine the light direction.

You can specify a range to tell Direct3D how far the light will produce an effect. This member does not affect directional lights. The following code snippet assigns a range of 1000 units to the lamp.

light.Range = 1000.0f;

The following code snippet assigns lights to Direct3D devices by calling IDirect3DDevice9::SetLight.

g_pd3dDevice->SetLight( 0, &light );

Idirect3ddevice9:: the first parameter accepted by SetLight is the index to which the light will be assigned. Note that if a light already exists at that location, it will be overwritten by the new light. The second parameter is a pointer to the light structure that defines the light. The light sample project places this light at index 0.

The following code snippet enables the light by calling IDirect3DDevice9::LightEnable.

g_pd3dDevice->LightEnable( 0, TRUE);

Idirect3ddevice9:: the first parameter accepted by lightenable is the index of the light to be enabled. The second parameter is a boolean indicating whether the light is on (TRUE) or off (FALSE). In the example code above, the light at index 0 is turned on.

The following code snippet tells Direct3D to render lights by calling IDirect3DDevice9::SetRenderState.

g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );

The first two parameters accepted by IDirect3DDevice9::SetRenderState are which device state variable to modify and what value to set. This code example will use d3drs_ The lighting device variable is set to TRUE, which has the effect of enabling light rendering.

The final step in this code example is to turn on ambient lighting by calling IDirect3DDevice9::SetRenderState again.

g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0x00202020 );

The previous code snippet will be d3drs_ The ambient device variable is set to light gray (0x00202020). All objects are illuminated by a given ambient color.

For more information about lighting and materials, see lights and materials (Direct3D 9).

This tutorial shows you how to use lights and materials. Tutorial 5: using texture maps shows you how to add textures to surfaces.

Topics: DirectX