Lighting Textures

In the previous chapter, simple directional lighting was discussed for a cylinder. The cylinder that was used for that example wasn't textured, and so far the lighting calculations have only been performed in the vertex programs. You can just as easily perform these lighting operations in your pixel shader code as well.

Like the previous example, we will use the simple mesh rendering example from the previous chapter as our starting point. In this section you will take that existing sample and add a colored light to the scene in our pixel shader.

The first step to making this update will be to add the following declarations to your shader code:

 float4x4 WorldMatrix : WORLD; float4 DiffuseDirection; Texture meshTexture; sampler TextureSampler = sampler_state { texture = <meshTexture>; mipfilter = LINEAR; }; struct VS_OUTPUT {     float4 Pos : POSITION;     float2 TexCoord : TEXCOORD0;     float3 Light : TEXCOORD1;     float3 Normal : TEXCOORD2; }; 

You'll notice that not only are you storing the combined world, view, and projection matrices, but there is another variable to store the world matrix itself. You'll understand why this is necessary when the shader code itself is discussed. There is also a variable to hold the direction of the light. Much like the previous example, there is a texture and a sampler state stored for reading the texture.

The output structure is quite different than anything seen yet. Not only are the position and texture coordinates passed on, but there are two new texture coordinate members that will be passed on as well. In reality, these members will not really be texture coordinates at all; they are just what will be used to transfer the necessary data from the vertex shader to the pixel shader.

You'll also need to actually update the values of these members. In your mesh drawing routing, you are already setting the WorldViewProj variable, so that is the perfect place for the following:

 effect.SetValue("WorldMatrix", worldMatrix); effect.SetValue("DiffuseDirection", new Vector4(0, 0, 1, 1)); 

The texture variable will also need to be set. In your initialization routine after your mesh (and texture) has been loaded, add the following:

 effect.SetValue("meshTexture", meshTexture); 

With those simple items out of the way, you're ready to update the shader code to perform the lighting. You should pick a color other than white for this light so that it's obvious the work is being done. Before looking at the pixel shader code, you should examine the vertex shader:

 // Transform our coordinates into world space VS_OUTPUT Transform(     float4 inputPosition : POSITION,     float3 inputNormal : NORMAL,     float2 inputTexCoord : TEXCOORD0     ) {     //Declare our output structure     VS_OUTPUT Out = (VS_OUTPUT)0;     // Transform our position     Out.Pos = mul(inputPosition, WorldViewProj);     // Store our texture coordinates     Out.TexCoord = inputTexCoord;     // Store our light direction     Out.Light = DiffuseDirection;     // Transform the normals into the world matrix and normalize them     Out.Normal = normalize(mul(inputNormal, WorldMatrix));     return Out; } 

At first, this shader begins much like ones that have been used previously, albeit with more inputs. First, the position is transformed and the input texture coordinates stored. Next, however, the light direction is placed into the second texture coordinate set. You may notice that the light direction is a float4, while the texture coordinate is a float3. This implicitly implies the xyz swizzle on the larger component. Given that the diffuse direction is essentially a 3D vector and the w component is ignored anyway, this is perfect.

The normals will also need to be stored in the third set of texture coordinates. Before this can be done, though, the normals themselves must be transformed into the correct coordinate system, in this case the world's system. Without this step, the lighting calculations would be way off. You cannot perform math operations on objects in different coordinate systems. The newly transformed normals are normalized and stored in the third texture coordinate set.

With all the data needed to correctly light the model and its textures, you can add the following pixel shader code:

 float4 TextureColor(  float2 textureCoords : TEXCOORD0,  float3 lightDirection : TEXCOORD1,  float3 normal : TEXCOORD2) : COLOR0 {     // Get the texture color     float4 textureColor = tex2D(TextureSampler, textureCoords);     // Make our diffuse color purple for now     float4 diffuseColor = {1.0f, 0.0f, 1.0f, 1.0f};     // Return the combined color after calculating the effect of     // the diffuse directional light     return textureColor * (diffuseColor * saturate(dot(lightDirection, normal))); }; 

As you see, the pixel shader takes as inputs all of the sets of texture coordinates returned from the vertex shader and will return the color for this pixel. The first step is to naturally sample the current texture. Next, a light color is set (in this case a purple color). To calculate the intensity of this light at the pixel, you take the dot product of the light's direction with the normal, much like what was done in the vertex shader case earlier. The saturate intrinsic method clamps the return value to the range of zero to one, so there won't be any overly bright or overly dark colors. Finally, the final color is calculated by multiplying the current texture color by the diffuse lighting component.

While an extremely simple example, this is a much more common occurrence. Running the application, you should see the normal model rendered with a purple tint to it.



Managed DirectX 9 Graphics and Game Programming, Kick Start
Managed DirectX 9 Kick Start: Graphics and Game Programming
ISBN: B003D7JUW6
EAN: N/A
Year: 2002
Pages: 180
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net