DirectX 10 Game Programming : Shaders and Effects - Pixel Shaders, Lighting (part 1) - Generating Normals

11/21/2013 7:47:03 PM

1. Pixel Shaders

Pixel shaders give you access to every pixel being put through the pipeline. Before anything is drawn, you’re given the chance to make changes to the color of each pixel. In some cases you’ll simply return the pixel color passed in from the vertex or geometry shaders, but in most cases you’ll apply lighting or textures that affect the color of the resulting pixel. The code shown next demonstrates the simplest form of pixel shader, which just passes the input color along to be drawn.

// Pixel Shader
float4 PS(PS_INPUT psInput) : SV_Target
return psInput.Color;

Changing the Color

Just to give you an idea of one manipulation you can perform on the input color, the following shader divides the color by two. This causes the color to dim as compared to the input color.

// Pixel Shader
float4 PS(PS_INPUT psInput) : SV_Target
/* psInput.Color / 2 is equivalent to
psInput.Color.r = psInput.Color.r / 2;
psInput.Color.g = psInput.Color.g / 2;
psInput.Color.b = psInput.Color.b / 2;
psInput.Color.a = psInput.Color.a / 2;

return (psInput.Color / 2);

2. Lighting

If you look around, chances are there’s a light source somewhere in your vicinity. It could be a lamp, the sun, or even just your monitor. Every bit of light, no matter how small, affects how we see the world around us. Some objects are dull and seem to absorb light while others are shiny causing light to be readily reflected. Everything you’ve done up to this point has assumed that your scene is taking place in a world where there is an all-powerful ambient light source causing everything to be lit from all directions. While it helps to be able to see what you’ve created, it isn’t very realistic.

In this section you’re going to learn a little about how lights affect a scene and how to get lighting up and running. The first step down the lighting path is the generating of normals.

2.1 Generating Normals

A normal is a vector that is perpendicular to the plane of a polygon and is used when adding lighting to a scene. The job of the normal is to help in determining the amount of light a polygon may be receiving. Essentially, what this means is you have to calculate what direction the different pieces of your objects are facing so you know how much to light it.

Normals are calculated by obtaining the cross product of two of the polygon’s edges. The vectors representing the edges of the polygon are determined using the polygon’s vertices. The normal calculation is shown below. An example of a normal vector is shown in Figure 1.

Figure 1. A normal vector.

NormalVector = CrossProduct( (vertex2 - vertex0), (vertex1 - vertex0) )

You can think of the returned value from the normal calculation as the amount of light hitting the object from the three axes directions.

Updating the Code to Support Normals

Like texture coordinates and position, normals are stored in the vertex structure and that means updating the custom vertex structure. Normals have an X, Y, and Z component and can be stored in a D3DXVECTOR3 variable. As you can see in the following structure, a variable called Normal has been added. The name of the structure was also changed to more accurately reflect the type of data it’s storing.

struct VertexPosColorNormalStruct

After the vertex structure is updated, the input layout also needs to be changed. This will enable the application to successfully send the vertices to the shader. In the new layout, the information for the normals is added to the end after position and color. This keeps everything in sync with the order in the vertex structure.

// The vertex input layout
D3D10_INPUT_ELEMENT_DESC layout[] = {
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12,
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 28,

The normals themselves are calculated using the array of indices that define all the faces in the object. By looping through the array of indices, the appropriate vertices can be updated for each face. After the normals are calculated, the resulting value is written to the Normal variable for each of the vertices.

// compute normals for each face in the model
for (unsigned int i = 0; i < modelObject->numIndices; i+=3)
D3DXVECTOR3 v0 = vertices[indices[i]].Pos;
D3DXVECTOR3 v1 = vertices[indices[i + 1]].Pos;
D3DXVECTOR3 v2 = vertices[indices[i + 2]].Pos;

D3DXVECTOR3 normal;
D3DXVECTOR3 cross;
D3DXVec3Cross(&cross, &D3DXVECTOR3(v2 - v0), &D3DXVECTOR3(v1 - v0));
D3DXVec3Normalize(&normal, &cross);

// assign the computed normal to each vertex in this face
vertices[indices[i]].Normal = normal;
vertices[indices[i + 1]].Normal = normal;
vertices[indices[i + 2]].Normal = normal;

Now that you have normals defined for all the vertices in your object, lighting can be applied within the pixel shader.

Video tutorials
- How To Install Windows 8

- How To Install Windows Server 2012

- How To Install Windows Server 2012 On VirtualBox

- How To Disable Windows 8 Metro UI

- How To Install Windows Store Apps From Windows 8 Classic Desktop

- How To Disable Windows Update in Windows 8

- How To Disable Windows 8 Metro UI

- How To Add Widgets To Windows 8 Lock Screen

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010
programming4us programming4us