Subtle (or I guess just more technical) distinction to make: the red, green, and blue components of a particular pixel in a normal maps are the "x", "y", and "z" components of the normal vector, where "z" is defined as the tangent to the plane the image is taken from, and x and y are relative to the image.
It's basically the same thing, but might help if you're more familiar with vector/matrix math
I'm an "enterprisey" software developer and have tried a few times to make a game by myself. When it comes to putting software and math to work, I've never been more impressed than with graphics rendering pipelines. It's a realm of software I'm not sure I will ever dive into, just marvel from afar. I "get" a lot of the techniques on a conceptual level, but I'm not sure I'll ever grok the code that pulls it off without spending much more time than I'm willing to commit to it.
It's actually not so hard to groc once you emerce yourself in it. The hard part is you don't really experience these same patterns in enterprise work. It'll click eventually.
The main thing for me was realizing the goals of the pipeline. You need to process each pixel on its own in parallel to get the speed you need. All these texture techniques are in service of prebaking data down such that it can be effeciently prefetched before the shader needs it.
At least for me, understanding the why helped me understand the what.
FWIW, it doesn’t take that much time if you start with a good setup. Writing your own game engine in order to write normal maps will take a loooong time, but you could whip up some normal maps on (for example) ShaderToy in an afternoon, easy, and there are tons of great examples there to reference and learn from.
This is a pretty old technique in the demoscene ('called bump mapping there'). There's probably a few hundred demos from the late 90s that use this technique on everything from Amigas to DOS systems of the time.
Normal mapping has a long history. Jim Blinn did the original work in 1978, with "Simulation of Wrinkled Surfaces" [0] which precomputes the normal pertubation into a height texture ("bumpmap") and does some sleight of hand arithmetic to integrate it back, and there was a ton of noise about putting it in hardware around 1998 or so.
The modern normal map was introduced by Kilgard as a rotation on top of the unperturbed surface normal [1] in surface tangent space, allowing it to be encoded with three channels, and compressed to two.
How the hell people like you have links to things like [0] will never cease to amaze me. This is why HN is irreplaceable imho. This place is too damn interesting
Bump maps are subtly different from normal maps. Bump maps are a grayscale map that holds height info (tangential to the face normal). Slightly smaller file, but holds slightly less information.
The hard-transition version on the left shows the outline of the sphere with clear line segments, but the smooth-transition one on the right shows a perfectly circular outline. (The clearest comparison can be seen in the darkest section, the bottom-left edge.)
So smoothing groups are clearly doing something in addition to changing how light reflects and this is not the full story.
Does anyone know how the perfectly smooth, non-polygonal outline (silhouette) is achieved? This post is clearly not providing the full story.
This is interesting, but one thing throughout is that he keeps talking about how normals control how the light "bounces" off the surface, for instance: "Normals are vectors that we use to define how light bounces from a surface."
This makes sense for such things as specular highlights, but most of what we are seeing is simply shading. That is, the brightness of the surface is simply a matter of how much light is hitting the surface, due to the angle of the surface to rays of light from the light source. I would word it "how a surface is illuminated" rather than "how light bounces from a surface."
They're talking about the incoming irradiance vs. the surface reflectance. Both are related to the surface normal, but the former has to do with surface visibility, rather than reflectance. You'll notice that BDRFs don't include the Lambert cos term, since they only describe how they deal with incoming irradiance.
Put simply, a surface can't bounce any light if it doesn't receive any light to begin with. It's a weird, nitpicky statement, but hey, that's what this site is about, right?
> All lighting is just light bouncing off of things
In real-time graphics, bounce lighting is pretty rare. Typically you cache incoming irradiance into some form offline to emulate bounce lighting. Most of our scenes are lit with analytic lighting. A key directional e.g. sunlight for most light, and then punctual sources like point and spotlights for additional lighting tweaks.
I agree that normal maps influence diffuse lighting. Not sure I agree that lighting is always "light bouncing off things", since with a diffuse surface, it scatters it in all directions approximately equally. If you consider that a bounce, ok, but it isn't the same as "mirror type" reflection where the bounce angle works similarly to a ball bouncing off a surface (angle of incidence equals angle of reflection).
So, I don't agree that the normal's effect on diffuse lighting is on the way light bounces off things, instead it is the quantity of light that hits the surface.
I just think he could change the wording (as I suggested) to make it more accurate, so it betters people's understanding of the more general subject. It would be a simple change that would be easy to make, so it's intended as constructive criticism rather than just quibbling.
If you want to quibble, then in reality, there is only specular light reflection. It’s just that rough surfaces bounce light in many directions because the surface is rough.
In theory, if you had a high enough resolution texture and used enough samples you could actually represent all materials with only specular reflection.
Diffuse light is simply an approximation we use. We effectively have a value for “roughness” of a surface and assume that some percentage of the lights scatters in all directions using various approximations of how rough surfaces typically reflect light.
> It’s just that rough surfaces bounce light in many directions because the surface is rough.
Sort of, but not entirely, or you would get micro-flares on the surface, which we don't observe in reality. The other half of the equation is light that bounces around inside the object, and then comes out effectively in a random location. The majority of diffuse light is light that has been subsurface scattered. A huge simplification we make is assuming that that light can be modelled with a uniform constant factor, e.g. Lambertian diffuse. For surfaces with with microgeometry larger than the scattering distance, you need a more robust diffuse model, like Oren-Nayar.
I think you might be getting confused in the different lighting models. IRL there is no hard difference between specular and diffuse surfaces, that's a separation we've made in rendering for convenience. A raytraced pbr scene will use the normal and the roughness to determine the grouping of the resulting light bounces. But unless the surface is also emitting light, all lighting in the scene is from bounces, even of everything in the scene is diffuse.
Pretty sure I'm not confused about it, although it appears I use the word "bounce" a bit differently than you. Specular reflection and diffuse reflection are very different things, at least on typical reflective objects (including water and glass and black things, which don't have significant amounts of diffuse reflection).
> I would word it "how a surface is illuminated" rather than "how light bounces from a surface."
Do recognize that Carlos said at the beginning, “Most explanations I found are usually too technical, incomplete or too hard to understand for my taste, so I decided to give it a try and explain what I gathered. I recognize that these explanations may be incomplete or not 100% accurate, but I want to try anyway.“
FWIW, your suggestion sounds to me more like it’s about the light sources, and not the surface. The normals don’t really affect how the surface is illuminated before the light bounces off (which is what “how the surface is illuminated” sounds like to me), the normals define the orientation of the surface, and so combined with a material and lights, the normals really do (help) define how light bounces off a surface.
There are some established graphics terms for these things, but getting nit picky and accurate to that level would be detrimental to this blog post, I think. We don’t need everything to have the Wikipedia disease of being so accurate that only the experts who know the material and don’t need it can read the article. Carlos already found other explanations of normal maps too technical, so I guess he’s not interested in explaining the difference between radiance and irradiance just to describe the idea that normal maps change the surface orientation and affect shading.
Most of what we're seeing is a non-trivial function of the light entering a local interaction point, and the light leaving it and hitting the camera or our eyes[1].
Since the entry and/or exit angle matters, perturbing the geometrically flat normals (from triangles) makes the surface appear to have curvature.
This is important even for purely diffuse reflections. So I disagree with your wording.
[1]: In some cases, such as skin, it's also important that the entry point and the exit point typically does not coincide. However for most materials one can usually get away with ignoring this aspect.
It would be nice to see object space / tangent space normal maps make an appearance. The article only talks about tangent space normal maps, which are actually more complicated than object space normal maps.
It’s extremely rare to see anything other than tangent-space normal maps used these days, at least in games - as they allow the same map to be used on surfaces of any orientation, or on animated meshes
It’s not that rare. The surfaces can have any orientation, they just can’t be deformed easily.
But the idea here is that it’s easier to understand. If you show an object space normal map, and then show the rendered model, you can say, “aha, it’s really obvious how that works”.
And then you can introduce the tangent space normal map, and describe how you use transformation matrices to convert tangent space to object space. Showing the object-space normal map gives you insight into how tangent space normal maps work.
It's basically the same thing, but might help if you're more familiar with vector/matrix math