# Making a point light follow the camera

I'm applying phong shading onto a single giant triangle, and I'd like the light's coordinates to coincide with the camera's coordinates in 3D space. In order to do this, whenever I update the camera's coordinates, I also update the light's coordinates. However, diffuse and specular lighting don't "focus" exactly at the camera position when I bring the camera close to the triangle, instead they do it a few units too far. The triangle is .5 units below the XZ plane and parallel to it. Here is a picture demonstrating this effect.

Also, here are my HLSL shaders; this is where all the transformations happen.

// Vertex Shader ////////////////////////////////////////

float4x4    Matrix; // ViewProjection Matrix
float4x4    Matrix2; // View matrix
float4x4    Matrix3; // View matrix with translation zeroed out

float4    lightPos;

struct VS_INPUT
{
float4  Pos     : POSITION;
float4  Normal  : NORMAL;
float2  Tex0    : TEXCOORD0;
};

struct VS_OUTPUT
{
float4  Pos     : POSITION;
float4  Normal  : TEXCOORD1;
float4  LightDir     : TEXCOORD2;
float2  Tex0    : TEXCOORD0;
};

////////////////////////////////////////

VS_OUTPUT VShade(VS_INPUT In)
{
VS_OUTPUT Out = (VS_OUTPUT) 0;

Out.Tex0      = In.Tex0;

//world space -> clip space transformation
Out.Pos       = mul(In.Pos, Matrix);

// world space -> camera space transformations
Out.Normal    = normalize(mul(In.Normal, Matrix3));
Out.LightDir  = normalize(mul(lightPos - In.Pos, Matrix3));

return Out;
}

// Pixel Shader ////////////////////////////////////////

sampler   baseTex;

struct PS_INPUT
{
float4  LightDir     : TEXCOORD2;
float4  Normal  : TEXCOORD1;
float2  Tex0    : TEXCOORD0;
};

struct PS_OUTPUT
{
float4  Color   : COLOR;
};

////////////////////////////////////////

PS_OUTPUT PShade(PS_INPUT In)
{
PS_OUTPUT Out = (PS_OUTPUT) 0;

float4 Normal = normalize(In.Normal);
float4 LightDir = normalize(In.LightDir);

float di = saturate(dot(Normal, LightDir));

float4 ambient = {0.1, 0.1, 0.1, 1};

float4 Reflect = normalize(2 * di * Normal - LightDir);
float specular = pow(saturate(dot(Reflect, LightDir)), 2);

Out.Color = ambient + tex2D(baseTex,In.Tex0) * di + specular;
return Out;
}


EDIT

In.LightDir is computed for each pixel. It is first computed for each vertex in the vertex shader Out.LightDir = normalize(mul(lightPos - In.Pos, Matrix3));, then it is interpolated across the whole triangle. lightPos is just the camera's position (which coincides with the light):

if(handle = m_pVertexConstants->GetConstantByName(NULL,"lightPos"))
m_pVertexConstants->SetVector(pDevice,handle,(D3DXVECTOR4*)m_camera.GetPosition());


Also, the world transformation matrix is just the identity, so I have not included it anywhere.

• Im not sure what IN.LightDir might contain, but by the looks of the name that might be wrong, since you want the light to follow the camera, you need to calulate a new direction for each pixel from the new lights position. Other than that, it´s hard to tell with litle information. If you could add some info on what you are doing on the cpu side with your lighte it would be helpfull. Besides, in your vertex shader, you dont need to create a new matrix with "translateion zerod out" you can just cast it to a matrix3x3( Matrix2 ) ; and it will work. – Tordin Jan 29 '13 at 20:17
• Worth noting that the bright spot you're seeing isn't going to represent where the light is - it's the specular highlight; that is, it's a shader simulation of the light bouncing off the surface to strike the camera. To get a bright spot around the light, you'd need to have some distance-based falloff for the light's intensity. (The normal of the triangle is the same everywhere, so all points on it will be illuminated the same amount, apart from the specular highlight). Also worth noting that LightDir will not interpolate accurately; try calculating it in the pixel shader to see if it helps. – Trevor Powell Jan 30 '13 at 12:27
• You shouldent multiply with the Matrix3, since you want the position aswell to be stored with the light. so you have to multiplie it with the original View matrix. (Matrix2) – Tordin Jan 30 '13 at 12:55

## 1 Answer

Thanks to everyone for their responses, I have finally figured out the problem. Turns out I had to interpolate the positions, as they are transformed in view space, instead of the light direction. Apparently light direction does not linearly interpolate properly. Here are my modified shaders:

// Vertex Shader ////////////////////////////////////////

float4x4    Matrix; // ViewProjection Matrix
float4x4    Matrix2; // View matrix
float4x4    Matrix3; // View matrix with translation zeroed out
float4x4    Matrix4; // World matrix

//float4    lightPos;

struct VS_INPUT
{
float4  Pos     : POSITION;
float4  Normal  : NORMAL;
float2  Tex0    : TEXCOORD0;
};

struct VS_OUTPUT
{
float4  Pos         : POSITION;

float2  Tex0        : TEXCOORD0;
float4  Normal      : TEXCOORD1;
float4  Pos2        : TEXCOORD2;
};

////////////////////////////////////////

VS_OUTPUT VShade(VS_INPUT In)
{
VS_OUTPUT Out = (VS_OUTPUT) 0;

Out.Tex0      = In.Tex0;

//world space -> clip space transformation
Out.Pos       = mul( In.Pos, mul( Matrix4, Matrix ) );

// world space -> camera space transformations
Out.Normal    = normalize(mul(In.Normal, Matrix3));
Out.Pos2      = mul( In.Pos, mul( Matrix4, Matrix3 ) );

return Out;
}

// Pixel Shader ////////////////////////////////////////

sampler   baseTex;

float4x4    Matrix; // ViewProjection Matrix
float4x4    Matrix2; // View matrix
float4x4    Matrix3; // View matrix with translation zeroed out
float4x4    Matrix4; // World matrix

float4    lightPos;

struct PS_INPUT
{
float2  Tex0        : TEXCOORD0;
float4  Normal      : TEXCOORD1;
float4  Pos2        : TEXCOORD2;
};

struct PS_OUTPUT
{
float4  Color       : COLOR;
};

////////////////////////////////////////

PS_OUTPUT PShade(PS_INPUT In)
{
PS_OUTPUT Out = (PS_OUTPUT) 0;

float4 Normal = normalize(In.Normal);
float4 LightDir = normalize(mul(lightPos, Matrix3) - In.Pos2);

float di = saturate(dot(Normal, LightDir));

float4 ambient = {0.1, 0.1, 0.1, 1};

float4 Reflect = normalize(2 * di * Normal - LightDir);
float specular = pow(saturate(dot(Reflect, LightDir)), 2);

Out.Color = ambient + tex2D(baseTex,In.Tex0) * di + specular;
return Out;
}