I'm learning a tutorial from Rastertek about diffuse light with DX 11, here's the shader code:

float4 LightPixelShader(PixelInputType input) : SV_TARGET
    float4 textureColor;
    float3 lightDir;
    float lightIntensity;
    float4 color;

    // Sample the pixel color from the texture using the sampler at this texture coordinate location.
    textureColor = shaderTexture.Sample(SampleType, input.tex);

    // Invert the light direction for calculations.
    lightDir = -lightDirection;

    // Calculate the amount of light on this pixel.
    lightIntensity = saturate(dot(input.normal, lightDir));

    // Determine the final amount of diffuse color based on the diffuse color combined with the light intensity.
    color = saturate(diffuseColor * lightIntensity);

    // Multiply the texture pixel and the final diffuse color to get the final pixel color result.
    color = color * textureColor;

    return color;

And I don't understand why to invert the direction, it says in a comment line "invert the light direction for calculations", why is it necesary?



@alariq is correct, assume you have this scenario:

enter image description here

The light is shining DOWN and LEFT. The normal of the surface is UP and to the RIGHT.

If you just take the dot product:

float lightIntensity = saturate( dot( N, L ) )

The saturate function is going to clamp the value to being between 0 and 1. So, that's going to be

lightIntensity = (0.5, 0.75) • (-0.75, -0.5) = -0.375 - 0.375 = -0.75

But oh no! That will be clamped off to 0, and the point will appear in total darkness. How sad.

So if you turn lightDir around,

float lightIntensity = saturate( dot( N, -L ) )
lightIntensity = 0.375 + 0.375 = 0.75

Oh, that works. Why?

Well the light at the vertex should be most intense when lightDir is directly opposed to N.

enter image description here

But the dot product of two normalized vectors is the cosine of the angle between them

u • v = |u||v| cos(t)

If |u|=|v|=1, then

u • v = cos(t)

But cos(0) = 1, which should correspond with maximal illumination. Here's what we'll do. We'll turn lightDir AROUND, so then when -lightDir lines up perfectly with N, then lightDir is actually head-on with N.

All what we're doing is manipulating the vectors so that the dot product (which is actually the cosine of the angle between lightDir and N) gives a maximal value when lightDir is head-on with N.

| improve this answer | |

This is necessary to receive a correct sign of a dot product. lightIntensity of a point depends on the angle between the two vectors: one is normal in a point of interest and second is a vector which goes from point of interest to light source.

That is, if the normal points at the light - intensity will be maximal. Imagine you are looking at the sun: your view vector is same as sun light direction, but with a different sign. So we need to inverse light direction to get correct dot product.

Your shader is for the case of direct light source (which is the approximation of a light source at infinity - like sun, and so intensity of a point depends only at its direction - not its position). In case of a point light formula might be more intuitive (assume lightPos, pointPos and normal in same space):

posToLightDir = normalize(lightPos - pointPos); // same as inverted lightDir in your case
lightIntensity = saturate(dot(normal, posToLightDir));

In case of directional light pointPos is not needed, so

// here normalize(lightPos) would be same as -lightDir
posToLightDir = normalize(lightPos); 

You can search for Lambertian cosine Law, or Lambertian Reflectance

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.