In threejs there is the normal attribute of a buffer geometry instance which will come into play when it comes to figuring out how light should effect a surface. The normal attribute is also used for things like finding out what side of triangle should be the front side so that one does not have to always render triangles on both sides thus wasting resources. So then this normals attribute is an important part of creating a custom geometry which in my view is what I typically think of next after working out the position attribute of a geometry. The position attribute as the name suggests is the actually points in space of the geometry.
So then in todays post I will be writing about a special kind of texture map that can be added to some materials that can be used to adjust lighting called a normal map. A normal map is just one of many options to work with when it comes to the various kinds of texture maps such as emissive maps, alpha maps, and just plain old color maps. I can not say that I use normal maps that often thus far, but this is still a topic of interest when it comes to threejs in general so I took a moment to work out some examples and write this post.
In order to do anything with a normal map, or any kind of texture map for that matter I am going to want a texture to use for the map. One way to go about getting a texture to work with would be to use the threejs built in texture loader, and another way would be to load images by some other means and then use the texture constructor to create a texture object to be used in a material.
Some times Tangent attribute, UV mapping, and other geometry related concerns will need to be addressed
I might need to create a tangent attribute for a geometry if there is not one to begin with.
In this post I am sticking with built in geometry constructors that have the UV map set up for me to begin with. However even so I might need to adjust the values of the UV attribute to change how the texture is mapped to the 3d object. Getting into uv mapping might prove to be a little to far outside the scope of this post, but I have wrote a main post on this topic of UV mapping.
In this post I am looking into normal maps, but there are many other kinds of maps that you should also be aware of. If you are first starting out with this sort of thing I might be better to start out with the usual simple color map, or maybe a emissive map when it comes to working with a material that will react to one or more light sources.
The normal map is a kind of map that effects lighting, so it does not make sense to use a normal map when it comes to using a material such as the basic material, or any kind of material that does not support normal maps. In the source code examples here I am sticking to using just the standard material which is one options that supports the normal map feature, there are some other options to be aware of so be sure to read up more on materials in general if you have not done so.
The source code for these examples can be found in the for post folder of my test threejs repository along with the source code examples for all my other blog posts on threejs. If for some reason you want to make a pull request with one or the source code examples here that would be the place to do it. There is also the comments section of this blog post that can also be used as a way to bring something to my attention with this content.
When I wrote this post I was using r127 of threejs which was a later version of threejs in early 2021, and the last time I came around to doing a little editing I have found that the examples are still working okay with r146. As time goes by yet even more code breaking changes might be made to the library that will cause the source code examples here to no longer work.
I then have a create normal map helper that will create and return a texture that I want by calling my create canvas texture helper and passing the draw function to use. The next helper function that I worked out just creates and returns a Mesh object that uses the box geometry constructor for the geometry, and the standard materials for a material which of course supports normal maps. When creating the instance of the standard material in this helper I of course call my create normal map helper and just set the resulting texture as the value for the normal map property of the material.
In order to gain a better sense of what kind of effect the normal map has to the surface of a mesh I am going to want to make some kind of animation loop, and change some values over time. I could change things like the position of the point light that I am using, or rotate the cube. However I am thinking that it is the texture that I am using for the normal map that should be changing over time to see what the effect is when I change rgb values used when drawing to the canvas.
So for this example I started out with my basic example, but I made some changes so that I can call the draw method over and over again in a loop rather than just once, and being done with it. So my create canvas texture helper function now returns an object that contains the texture as a property of the object, and also references to the canvas element, and the 2d drawing context.
So far I can not say that I am using normal maps that often, in fact thus far I am not using them at all. However if I really truly get into 3d modeling at some point I am sure that I might want to create these kinds of maps now and then to adjust things a little when it comes to lighting.
For now as of this writing at last I am still sticking to simple color maps, and also I like to play around with emissive maps. Those two kinds of maps just seem to be what comes first naturally when it comes to learning about all the various texture maps that there are to work with materials. Also in just about any project I am typically going to always want to use those two in most cases.