Problem
Until now, you have used unidirectional light to illuminate the scene, which is very useful for adding sunlight to the 3D world. But in many cases, you also need a light from a point, such as a searchlight or explosion. Such a light source is called a point light source.
Solution
Transmits the 3D position of the Point Light source from the XNA project to the XNA effect. For each vertex, calculate the direction of the light source pointing to the vertex and use this direction as the direction of light. You can continue with the light direction as before.
Working Principle
In the. fx file, use the following code to replace the xLightDirection parameter so that you can pass the 3D position of the light source from the XNA project to the HLSL effect:
float3 xLightPosition;
Then, for each vertex, You need to calculate the direction from the light source to the vertex. The direction from A to B can be obtained through B minus. Remember that you need the final 3D position of the vertex, which means that the initial 3D position must be transformed by a world matrix:
float3 final3DPosition = mul(inPos, xWorld);float3 lightDirection = final3DPosition - xLightPosition; lightDirection = normalize(lightDirection); Output.LightFactor = dot(rotNormal, -lightDirection);
Because the direction of the light source pointing to the vertex may be much greater than 1, make sure that this direction is normalized. With the light direction, you can continue to process it as in the previous tutorial.
When you run the code, you need to calculate the direction from the light source to the vertex for each vertex, so this direction is often different.
Distance Attenuation
To make the effect more realistic, you want to make the light from the Point Light Source weaker when the distance between the light source and the vertex increases. To do this, you can use the length function to get the distance before normalization of lightDirection, and then divide the LightFactor by the distance:
float3 final3DPosition = mul(inPos, xWorld); float3 lightDirection = final3DPosition - xLightPosition; float distance = length(lightDirection); lightDirection = normalize(lightDirection); Output.LightFactor = dot(rotNormal, -lightDirection); Output.LightFactor /= distance;
Code
In the XNA project, you can place a point light source at any position you want:
effect.CurrentTechnique = effect.Techniques["VertexShading"]; effect.Parameters["xView"].SetValue(fpsCam.ViewMatrix); effect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix); effect.Parameters["xLightPosition"].SetValue(new Vector3(1, 5, 0));effect.Parameters["xAmbient"].SetValue(0.0f);
Below is the complete vertex shader:
VSVertexToPixel VSVertexShader(float4 inPos: POSITION0, float3 inNormal: NORMAL0) { VSVertexToPixel Output = (VSVertexToPixel)0; float4x4 preViewProjection = mul(xView, xProjection); float4x4 preWorldViewProjection = mul(xWorld, preViewProjection); Output.Position = mul(inPos, preWorldViewProjection); float3 normal = normalize(inNormal); float3x3 rotMatrix = (float3x3)xWorld; float3 rotNormal = mul(normal, rotMatrix); float3 final3DPosition = mul(inPos, xWorld); float3 lightDirection = final3DPosition - xLightPosition; lightDirection = normalize(lightDirection); Output.LightFactor = dot(rotNormal, -lightDirection); return Output; }