Common per-pixel lighting effects are done by passing a world-position to the pixel shader. Here's an example pipeline:
struct VSInput
{
float4 Position : SV_Position;
float3 Normal : NORMAL;
float2 TexCoord : TEXCOORD0;
};
struct VSOutput
{
float2 TexCoord : TEXCOORD0;
float4 PositionWS : TEXCOORD1;
float3 NormalWS : TEXCOORD2;
float4 Diffuse : COLOR0;
float4 PositionPS : SV_Position;
};
// Vertex shader
VSOutput VertexShader(VSInput vin)
{
VSOutput vout;
vout.TexCoord = vin.TexCoord;
vout.PositionPS = mul(position, WorldViewProj);
vout.PositionWS = mul(position, World).xyz;
vout.NormalWS = normalize(mul(normal, WorldInverseTranspose));
vout.Diffuse = float4(1, 1, 1, DiffuseColor.a);
return vout;
}
struct PSInput
{
float2 TexCoord : TEXCOORD0;
float4 PositionWS : TEXCOORD1;
float3 NormalWS : TEXCOORD2;
float4 Diffuse : COLOR0;
};
float4 PixelShader(PSInput pin) : SV_Target0
{
float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse;
float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz);
float3 worldNormal = normalize(pin.NormalWS);
ColorPair lightResult = ComputeLights(eyeVector, worldNormal, 3);
color.rgb *= lightResult.Diffuse;
AddSpecular(color, lightResult.Specular);
ApplyFog(color, pin.PositionWS.w);
return color;
}
This is cribbed from the DirectX Tool Kit shaders.