11: textures
Texture types
- Color : a single color
- Alpha : grayscale image, white is visible, black is not visible.
- Height : grayscale image, white is high, black is low, gray is middle, giving the illusion of depth.
- Normal : RGB image, each color represents a direction, used to give the illusion of depth, lots of details and very performant
- Ambient occlusion : grayscale image, puts shadows in crevices, making good constrast
- Metalness : for reflections, white become metallic, black become non-metallic
- Roughness : for reflections, white is rough, black is smooth
Textures are special because they follow the PBR (physically based rendering) guidelines, which means that they are based on real world physics.
Loading textures
Loading textures will only work on basic shapes, not on any custom shapes you created like a BufferGeometry
You create a texture loader from the TextureLoader
class, and then you can use the load()
method to load the specified image. The load()
method takes a path to the image, and returns a Texture
object.
const texture = new THREE.TextureLoader().load("/obama.jpg");
const material = new THREE.MeshBasicMaterial({ map: texture });
const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2);
const texture = new THREE.TextureLoader().load("/obama.jpg");
const material = new THREE.MeshBasicMaterial({ map: texture });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
Callbacks
const textureLoader = new THREE.TextureLoader();
const onLoaded = (texture: THREE.Texture) => {};
const onProgress = (event: ProgressEvent) => {};
const onError = (error: ErrorEvent) => {};
const texture = textureLoader.load("/obama.jpg", onLoaded, onProgress, onError);
The load()
method takes in additional arguments. Here they are in order:
- image source : the path to the image
onLoad
: a callback function that is called when the image is successfully loadedonProgress
: a callback function that is called when the image is loading, and gives you the progress of the loadingonError
: a callback function that is called when the image fails to load
Loading manager
The loading manager is a better way of having event-driven callbacks when loading textures.
This is how you create a loading manager:
- Instantiate it with the
LoadingManager
class - Pass in the loading manager instance as an argument to the texture loader constructor
const loadingManager = new THREE.LoadingManager();
const textureLoader = new THREE.TextureLoader(loadingManager);
The loading manager has 4 properties that you can set callbacks for:
const loadingManager = new THREE.LoadingManager();
const textureLoader = new THREE.TextureLoader(loadingManager);
loadingManager.onStart = (url, loaded, total) => {};
loadingManager.onLoad = () => {};
loadingManager.onProgress = (url, loaded, total) => {};
loadingManager.onError = (url) => {};
onStart
: called when the loading startsurl
: the url of the imageloaded
: the number of images that have been loadedtotal
: the total number of images to load
onLoad
: called when the loading for all the images is finished successfullyonProgress
: called when the loading for an image is finished successfullyurl
: the url of the imageloaded
: the number of images that have been loadedtotal
: the total number of images to load
onError
: called when the loading for an image failsurl
: the url of the image
UV mapping
UV mapping is the principle behind how we apply textures to 3D objects. We take a 2D plane like an image, and wrap it around a 3D object. The 2D plane is called a UV map.
Each vertex also has UV coordinates, that map from 2D to 3D.
The reason why textures don't work on custom shapes is because they don't have UV coordinates for the textures to successfully map onto the geometry. That's why you can't load textures on buffer geometry, as opposed to normal geometries that come with threejs, since they already have their own UV coordinates.
If you want to load textures on custom shapes, you need to provide the UV coordinates for that object.
Modifying the texture
There are many transformations you can make to the texture itself to get it look different, like rotating it, repeating it, and offsetting it.
Rotation
texture.rotation
: the rotation of the texture in radians. Anumber
value.texture.center
: the center of the texture. AVector2
value, where (0.5, 0.5) is the direct center of the texture.
texture.center.set(0.5, 0.5);
texture.rotation = Math.PI / 4;
Repeating
The texture.repeat
is a Vector2
instance, and in there you can define how many times the texture should repeat in the x and y direction.
// repeat 2 times in the x and 2 times in the y direction
texture.repeat.set(2, 2);
By default, the texture will repeat the last pixel on each side of the texture. If you don't want that behavior, and instead you want to repeat the image itself multiple times, you need to modify the wrapS
and wrapT
properties of the texture.
You can set these properties to two different values:
THREE.RepeatWrapping
: repeats the image normally, no flippingTHREE.MirroredRepeatWrapping
: repeats the image, but mirrors it each time, flipping horizontally when repeating on the x and vertically when repeating on the y
const texture = textureLoader.load("/obama.jpg");
texture.repeat.x = 2;
texture.repeat.y = 2;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
Mipmapping : go back and review
Mipmapping is a technique that improves the performance of textures. It's a technique that generates multiple versions of the same texture, but at different resolutions, halving and halving to fill up the material.
When the texture is far away, the lower resolution version is used, and when the texture is close, the higher resolution version is used.
If you don't want to use mipmapping, you can use the minFilter
and magFilter
properties on a texture.
minFilter
: the filter used when the texture is scaled down. Using a very large image as a texturemagFilter
: the filter used when the texture is scaled up. Using a very small image as a texture
const texture = textureLoader.load("/obama.jpg");
texture.magFilter = THREE.NearestFilter;
texture.minFilter = THREE.NearestFilter;
texture.generateMipmaps = false;
When setting the filter to THREE.NearestFilter
, you can disable mipmapping and get a performance boost. It also gives a minecraft texture effect.
Image files
- jpegs: usually lighter file size, lossy compression.
- pngs: usually heavier file size, lossless compression.
Texture files, their width and height must be a power of 2, since mipmapping divides down the image until it becomes 1x1.