Texturing a sphere with react-three-fiber

Texturing a sphere with react-three-fiber

So far in these posts everything has looked pretty good, but all the objects have just had color and shininess applied. That's great (and fast) but it's not particularly like the real world. Objects have texture, so we need to add textures to our objects.

A texture in Three.JS is a material that with some images applied to it to transform what it looks like. There are a few different ways that the images can be applied. Each is called a map. Some of them are very simple and have a significant impact on the material, and some of them are more subtle and only affect how an object is drawn very slightly. They're all necessary to render something as it might look in real life though.

The first map is the base color of an object, and it's applied to a material by setting it's map attribute. This takes an object from being a single shaded color to having a real texture. It's the basis for everything else that's applied on top.

The next map that's applied is the bump or height map. This tells Three.JS where the surface height is different on an object, and enables complex surface geometry to be added without increasing the complexity of the geometry. Related to the bump map is the normal map, which controls how light reflects off of the surface of the object by affecting its normals (normals are the perpendicular vectors to the objects faces.)

The next texture map to apply is called "ambient occlusion". This is just a fancy term for how much of the ambient light in the scene affects the object - if the object is dark, non-reflective black then it wouldn't be affected by the ambient light very much. The ambient occlusion map lets us control that.

Lastly in physically based rendering there are three additional maps - metalness, roughness and clearcoat mapping. What these do is a little outside of the scope of this article because it's complicated, but approximately metalness controls how the object changes the color of reflected light, roughness controls how much the reflected light is blurred, and clearcoat controls the specular highlights.

Loading texture images

In Three.JS textures are loaded using the TextureLoader helper. react-three-fiber provides a convenient hook wrapper around this called useLoader. Consequently we can use a simple hook to asynchronously load an image and turn it in to something we can use in a material.

const base = useLoader(THREE.TextureLoader, "/metal/metal1_basecolor.jpg");

With the base texture loaded we just need to use that in the map attribute of the material to apply the texture.

Asynchronous image loading

There's one more important thing to note when using the useLoader hook. As it's asynchronous it means the texture might not be available yet when the renderer tries to draw the object. This would cause the render to fail with an error, but as this is using React it can fall back to Suspense component's fallback. Therefore it's necessary to wrap components that use the useLoader hook in a <Suspense fallback={null}> wrapper. Note that the fallback doesn't need to be null. You could put a simple placeholder object in there instead.

Bringing everything together

By loading all of the different maps in to a material you can create some really interesting textured objects.