Shader Programming: Lighting Basics
A month between blogs! Not great, but still posting something! This will be a shorter one as the topic, while just as "complex" as noise I think it's a bit easier to condense.
I will be providing most examples in this blog with Unity's Shader Graph for ease, but it's pretty human readable and easy to convert to other node based shaders or your preferred shader language.
Shading
Once you want to display a 3D scene, you're probably going to want some shading. For shading, you need light, but how does your scene know what to even do with this light? How does it react to surfaces?
This is where we use reflectance models. Reflectance has a big long definition with words I don't understand, but in this case you just need to consider it as "How light reacts to surfaces". There are different types of reflectance models, all doing vaguely the same thing, but with different formulas achieving different levels of realism & style.
All reflectance models rely on light vectors and surface vectors. The light vector is pretty self explanatory, and the surface vector usually defines the Normal vector of a surface. A normal vector is a vector that is perpendicular to a surface, usually on a face-by-face basis. My using the light vector and surface vector, we can then use this data to calculate how bright a surface is.
Image Source: Wikimedia Commons
Diffuse Reflectance
The most basic form is "Diffuse reflectance", this is just the general lighting of an object. Diffuse Reflectance is not a reflectance model, but something a reflectance model achieves. Diffuse is used for rough surfaces, where each light ray hits a point on a surface with a different angle. This is dependent on light and normal direction. It is not altered by view direction.
Specular Reflectance
The second form of lighting we look at is "Specular reflectance". This is used for smooth surfaces, and also requires the View Vector and changes based on the location of the camera. It outputs in one direction, and the outgoing light ray must hit our "eye" to be visible.
How we calculate Reflectance
Dot product. Plus some fancy frills to make it look better. This is especially true for diffuse reflectance. If you don't know what the dot product of two vectors is, I recommend taking a look online for an answer, as I'm not exactly a maths guy and probably wont be able to explain it correctly, but if you're lazy, in this context the dot product of two vectors returns us our light value for a given pixel.
Calculating Diffuse
With Lambert reflectance, we calculate it by simply using:
dot(lightVector, normalVector)
You can add more to this to make it more reliable, such as clamping the output, etc, but really this is all that is being done.

In games before, and sometimes still seen today, a modification to this model known as Half-Lambert was often used. This was done to offset the harsh shadow of the lambert model and allow for more visibility. This can be seen in games from TF2 to Valorant.
dot(light, normal) * .5 + .5

There have since been a lot of improvements in diffuse reflectance, such as Oren-Nayar and burley reflectance models have largely replaced lambert, but you can usually still find it in game engines.
Calculating Specular
The very basics of Specular reflectance is Phong reflectance. This uses the normal, light, and view vector. We get the reflected vector of the light and normal vectors, and then gets the dot product of the reflected vector and the view vector. That's a lot of words. The formula may be easier to read:
dot(reflect(light, normal), view)
And then we have basic specular reflectance! this can be altered to your liking by putting it to the power of whatever intensity you would like.

It's not great though, and a better looking model for essentially the same complexity is Blinn-Phong. The big difference is that instead of a reflected vector, we instead use a normalised vector. This normalised vector is called the Half-Vector.
normalize(light - view)
Then just pop it back into the same formula as before:
dot(halfVector, normal)
It's usually best to keep this about 4x as bright as you would using regular Phong specular.

As with diffuse reflectance, there are more complex and more accurate ways to calculate this, but for a basic introduction, it will look just fine.
Here's what is looks like when we display these together!

A little Extra: Fresnel
Personally, I love the Fresnel effect and I think it's cool. The fresnel effect essentially describes how light reacts differently at grazing angles.
It can be used for a ton of things, and can make your lighting models look a little better, or adding more stylistic effects. Fresnel can be calculated as:
dot(normalVector, viewVector)
And is usually just a node in your node-based shader editor. There's a ton of writing online about Fresnel that go more in depth, I recommend reading them!
