 The idea of physically-based rendering is that we want to model light in a way that's more realistic. There's no authoritative definition of what counts as physically-based rendering and what doesn't, but the most essential criteria is that our lighting calculation, which in physically-based rendering will call the bi-directional reflectance distribution function, this calculation should account first for energy conservation such that when light energy strikes a surface, we don't reflect off that surface more light energy than what is coming in. Second, we want to account for the microfacets of surfaces such that the light interacts differently for smooth surfaces versus rough surfaces. Thirdly, we should account for how light interacts differently with metal versus non-metal surfaces, a.k.a. dielectric surfaces. And fourthly, we should account for the fanelle effect, which is a phenomenon where light striking a surface at a shear angle, more of that light is going to bounce off at the reflected angle rather than get absorbed. If you look straight down at a wooden floor, it's not going to look super glossy, but when the floor is at a shear angle to your eye, then it'll look more glossy because more of the light is being reflected at the inverse angle. So now, if we look at the first PBR example, lighting, what you see here are a bunch of spheres being lit by four lights. The four lights in the scene are these small little balls just for reference, that's showing you where they are. And left to right, the spheres are increasing in roughness. It's the minimum roughness in the left column and the maximum on the right. And bottom to top, the spheres are increasing in metalness. The ones at the bottom are not metal at all, and the ones at the top are full metal. As you can see, with increasing roughness, the specular highlights get bigger but fainter. Metalness increases the intensity of the specular highlight a bit, but more noticeable about the metalness is that they have less diffuse reflection. So outside the specular highlight, they're just getting darker, they're not as intensely red. Because that's how metals behave. The light energy that absorbs into the metal, unlike with non-metal materials, tends not to escape. Before looking at the code, there are a few key formulas we're going to employ. The first is an approximation of the Fresnel effect called Fresnel-Schlick. It takes in three parameters. H is the bisecting vector between V and the vector to the light. And then F sub-zero, we can call that the base reflectivity. This is actually a VEC-3, RGB values between zero and one. And for dielectric materials, like say plastic, glass, and water, these values are going to be very low. For water, the RGBs are all 0.02. But then for metals, these values are much higher. At the low end, you have say iron, which is 0.56, 0.57, 0.58. And then at the high end, you have metals like silver, which is 0.95, 0.93, and 0.88. Putting F sub-zero aside, though, what do H and V do in this formula? Well, we get their dot product, such that when they point in the same direction, they get a value of one. And as they diverge, you get a value closer to zero. Well, we invert that by subtracting from one. And so this rightmost term here that's being raised to the power of five, the larger H and V diverge, the larger this value gets. As for F sub-zero, let's consider first say water, for which again, the F sub-zero value is 0.02, 0.02, 0.02. Well, one minus a very small value gets us a value close to one. In contrast for a metal like, say, silver, where the R component is 0.95, well, regardless of the H and V value, the R component output of this formula is going to be 0.95 plus a little something. The larger the base reflectivity, the less H and V is going to matter. So you could say that for dialectics like water and glass, there's a stronger Fresnel effect. When we look down at our wood floor, it's a non-metallic surface, and so it has a strong Fresnel effect. It doesn't look glossy straight down, but it looks glossy side-on. Anyway, this gets us a value for Fresnel, which effectively is going to determine for us how much of the energy striking a surface is reflecting in a specular fashion. And when we take into account energy conservation, it's only the remainder of the energy that can contribute to any diffuse reflections. Another formula we need is a normal distribution function. In this case, the Trowbridge writes GGX, normal distribution function. I don't know what the GGX stands for, but what this gets us is an estimation of what proportion of the micro facets of a surface, how many of them are aligned with our halfway vector, our bisecting vector, such that the light hitting those micro facets is going to bounce off at an angle towards their camera. And here's the normal, h is our halfway vector, and alpha is a value between 0 and 1, denoting roughness. The larger the value, the rougher the surface. And as you can see, the rougher the surface, the larger, but less intense, the specular highlight. So that full roughness for our sphere, it doesn't look like a highlight at all, the whole sphere just looks gray. This makes sense because when our normal is aligned with a halfway vector, then for a smooth surface, most of the micro facets should also be aligned with the halfway vector. The rougher our surface gets, the less this is the case. One more formula we'll need is an estimation of how many of the micro facets in our surface are self-shadowing. If you zoom way in to a point on the surface, some micro facets stick out, such that for light coming in from sheer angles, that micro facet is actually blocking light reaching other micro facets. And so when light strikes the surface, the more that light is coming in at an angle and the rougher the surface, the more we get self-shadowing amongst the micro facets. The formula we want here is called Smith's Method, which employs the Schlick GGX geometry function, and K here is our measure of roughness. Don't ask me why it's not called alpha. But anyway, as you can see in the diagram, the points on the surface that are angled from the light have a stronger self-shadowing effect, and so they're going to be a bit darker. Looking finally at the shader code, for each of the spheres we use the same albedo color, but we're using different roughness and metallic parameters. This function here is our normal distribution function. It's just the formula translated into code. Here's the Smith's Method formula translated into code, and here's the Fresnel Schlick approximation. We'll call these functions from main, which starts by computing the normal and the view vector. We also get a base reflectivity, which we're going to feed into the Fresnel function. The built-in mix function interpolates between two values, a start and end value here, Vect3 of .04 and albedo, another Vect3, and when metallic is zero, we get the start value. When it's one, we get the end value, and then for values between zero and one, we get interpolated values in between. So as our metallic parameter gets closer to one, the more our base reflectivity is going to match the albedo. This loop is where we're doing the calculation for each of the four lights. We accumulate the output luminance in this variable, LO. And we'll come back to this loop, but looking past it first, having computed LO, we simply add it to our ambient light computed right here to get our color. Then we do our tone mapping, gamma correction, and that's our output color. Looking back at the loop, we're going to need the light vector L, the halfway bisecting vector. We compute the distance to the light, use that for our attenuation, and that gets us an input radiance value, a measure of light energy coming in from this light source. This all then gets fed into our bidirectional reflectance distribution function, which in this case is the correct torrents, BRDF. We're going to need the dot products between N and V and an L, H and V and N and H. And note that for N dot V and N dot L, to prevent divide by zero errors down the line, we can't have these values be zero, so make sure there's something at least a little greater than zero. We then compute our normal distribution value, we'll call D, the result of the geometry function, the self-shadowing estimation we call that G, and the result from our funnel function we'll call F. In cooked torrents, we then get our measure of specular light by multiplying these all together and dividing by four times N dot V and N dot L. Because we're dividing by N dot V and N dot L, that's why they can't be zero here. Now, I can't explain all the logic here, but understand that D is going to be a value between zero and one that gets larger closer to one the more the micro facets are aligned to H. G is going to be a value between zero and one that gets smaller closer to zero the more the micro facets are shadowed by other micro facets. And F is going to be our GB values, also between zero and one, which are going to get closer to one the more the light vector and the view vector are at a sheer angle to the surface. N dot V and N dot L are also going to be in between zero and one, and because we're dividing the specular by them, the closer these values are to zero the larger the specular value is going to get. Again, I can't explain exactly the logic here, but it does make sense that as the view vector and the light vector diverge from the normal, the more intense the specular light should get. Now, as for the diffuse light, that's denoted here as KD, and this is computed as simply one minus F because thanks to energy conservation whatever is bounced off as specular light, our diffuse light can only be made up of the energy left over. But then the larger a metallic parameter, the more KD is diminished. If metallic is one, then the diffuse component goes all the way to zero because metals actually have no diffuse light. Now, in reality, a material is either metallic or not, it's basically binary zero or one but because metallic surfaces can have dust and dirt on them, they can get scuffed up we want those surfaces to render as partially metallic even though in reality something is either metal or not. Having computed KD, we can finally get our output luminance for this one light by multiplying the KD times the albedo divided by pi, for reasons I don't really understand other than the specular term is also divided by pi in the distribution function up here it's effectively divided by pi, I think that's the rationale basically to even out the diffuse and specular components, but I can't say for sure we then add the diffuse and the specular together, multiplied by the incoming radiance but just like in fog lighting, the incoming light is diminished when it hits the surface at an angle we only get the full radiance energy when it's hitting the surface perpendicular straight on and note two differences from fog shading here for one, the specular is effectively also being multiplied by n.l, not just our diffuse light and also note that only the diffuse is multiplied by the albedo the specular light isn't being mixed with the albedo color at all and this better matches physical reality it's the diffuse light, the light that partially absorbs into the surface and bounces around that picks up the color property of that material the specular component just bounces off the surface anyway, this is how we get the output luminance for each light add them all together and then add them together with the ambient getting us our output color to which we apply the tone mapping and gamma correction now in our example, each sphere was rendered with its own metallic and roughness value but it was the same metallic and roughness value applied across the whole sphere that may be fine for some objects we want to render but in many cases, the metalness and the roughness might vary across a surface a metal surface for example might have some parts that are dirtier, rustier and more scuffed than others so to have metalness and roughness that varies across the surface we can get those values per point by sampling from textures for extra detail, we can also bring in an ambient occlusion map which effectively denotes the subtle self-shadowing from a complex rough surface if say you have small creases and grooves and dents on the surface a little less ambient light is going to reach into points on those surfaces be clear though that these AO maps do not replace other ambient occlusion we might still apply screen space ambient occlusion which is calculated based on our geometry and so isn't going to capture any detail within a single surface that's what the AO maps are for so if we want to use these textures instead of the uniforms like we had before what that looks like in code is very straightforward we just sample from the appropriate textures so what this looks like in action is we have all of these maps being applied onto these spheres and note in this scene we just have one light instead of the four like we did before but anyway if we zoom in you can see there's quite a bit of lighting detail going on here thanks to all those textures