HLSL point light normal map shader

I’m developing a 2D game and I’m very stuck with shaders. To me they are very difficult to understand and to create.

At his moment I’d like to code a pixel shader that draws a normal map around a circular point of light, to achieve an effect like this:

I could be able to apply a normal map effect with a single direction, but I don’t understand how to make it with more directions starting from a point.

I searched over internet but I didn’t find anything that fits what I need (a lot of the examples are related to 3D games).

Do you have any suggestions?

you do it the same way, like you would do it with single directional light, with the only difference, that the “direction” is basically a vector from the light source to pixel, instead of a fixed direction.

yes, that means you have to transport the actual pixels world position to compare with the pointlights world position or any other sort of comparable positions whatever you have available or want to use.

this can be as simple as having another variable passed over to the pixel shader which has the desired position filled in VertexShader (which will get interpolated for the PS)

1 Like

At the end, thanks to reiti suggestions, I did:

// Effect applies normalmapped lighting from i punctual light to a 2D sprite.

float3 LightPosition; // in World Space
float3 LightColor = 1.0;
float3 AmbientColor = 0.35;

float4x4 World;
float4x4 ViewProjection;

Texture2D ScreenTexture;
Texture2D NormalTexture;

SamplerState TextureSampler = sampler_state
    Texture = <ScreenTexture>;

SamplerState NormalSampler = sampler_state
    Texture = <NormalTexture>;

struct VertexShaderInput
    float4 Position: POSITION0;
    float2 TexCoords : TEXCOORD0;
    float4 Color : COLOR0;

struct VertexShaderOutput
    float4 Position: POSITION0;
    float4 PosWorld: POSITION1;
    float2 TexCoords : TEXCOORD0;
    float4 Color : COLOR0;

VertexShaderOutput VS(VertexShaderInput input)
    VertexShaderOutput output;

    float4 pos = mul(input.Position, World);
    output.PosWorld = pos; // handing over WorldSpace Coordinates to PS
    output.Position = mul(pos, ViewProjection);

    // fill other fields of output
    output.TexCoords = input.TexCoords;
    output.Color = input.Color;

    return output;

float4 PS(VertexShaderOutput input) : COLOR0
    // input.PosWorld how has the Position of this Pixel in World Space
    float3 lightdir = normalize(input.PosWorld - LightPosition); // this is now the direction of light for this pixel

    // Look up the texture value
    float4 tex = ScreenTexture.Sample(TextureSampler, input.TexCoords);

    // Look up the normalmap value
    //float4 normal = 2 * NormalTexture.Sample(NormalSampler, input.TexCoords) - 1;
    float3 normal = normalize((2 * NormalTexture.Sample(NormalSampler, input.TexCoords)) - 1);

    // Compute lighting
    float lightAmount = saturate(dot(normal, -lightdir));
    input.Color.rgb *= AmbientColor + (lightAmount * LightColor);
    return input.Color * tex;

technique PointLightNormalMap
    pass Pass1
        VertexShader = compile vs_4_0_level_9_1 VS();
        PixelShader = compile ps_4_0_level_9_1 PS();

This is the result (not very good):

If you are interested, I made a GitHub repository for this.

Hi Francesco,

I think your normal map is the problem.

Look at this image to see what I mean.

Looks like the Y coordinate needs to be reversed.

I did download your demo, but the way you have structured it the solution does not build the shaders and I didn’t have the time to modify it, but I think a couple of simple changes in the shader would make it look a lot better

float lightAmount = saturate(max(0,dot(normal, -lightdir)));

And as a test try

normal.y *= -1;

Or change LightDirection to -LightDirection


Hi Stainless! Thanks a lot for you help!

I added the ContentBuilder to the CircularNormalMap sample, if you want to try it you can just edit the fx file, double click on the builder, build and run.

I inverted the y of the normal, this is the result:

Does it look ok for you now?
Can I do something to improve it?

Thanks a lot!

That looks a lot better
Well done :smiley:

1 Like

Thanks Stainless, and thanks to everyone who helped me!

Your shader calculation helped me greatly write similar shader for my game.