- Three.js - Discussion
- Three.js - Useful Resources
- Three.js - Quick Guide
- Three.js - Libraries and Plugins
- Three.js - Loading 3D Models
- Three.js - Creating Text
- Three.js - Animations
- Three.js - Drawing Lines
- Three.js - Textures
- Three.js - Materials
- Three.js - Geometries
- Three.js - Lights & Shadows
- Three.js - Controls
- Three.js - Cameras
- Three.js - Debug and Stats
- Three.js - Responsive Design
- Three.js - Renderer and Responsiveness
- Three.js - Hello Cube App
- Three.js - Installation
- Three.js - Introduction
- Three.js - Home
Selected Reading
- Who is Who
- Computer Glossary
- HR Interview Questions
- Effective Resume Writing
- Questions and Answers
- UPSC IAS Exams Notes
Three.js - Textures
The texture is an image or color added to the material to give more detail or beauty. The texture is an essential topic in Three.js. In this section, we ll see how to apply a basic texture to our material.
Basic Texture
First, you should create a loader. Three.js has a built-in function TextureLoader() to load textures into your Three.js project. Then you can load any texture or image by specifying its path in the load() function.
const loader = new THREE.TextureLoader() texture.load( /path/to/the/image )
Then, set the map property of the material to this texture. That s it; you appped a texture to the plane geometry.
Textures have settings for repeating, offsetting, and rotating a texture. By default, textures in three.js do not repeat. There are two properties, wrapS for horizontal wrapping and wrapT for vertical wrapping to set whether a texture repeats. And set the repeating mode to THREE.ReaptWrapping.
texture.wrapS = THREE.RepeatWrapping texture.wrapT = THREE.RepeatWrapping texture.magFilter = THREE.NearestFilter
In Three.js, you can choose what happens both when the texture is drawn larger than its original size and what happens when it s drawn smaller than its original size.
For setting the filter, when the texture is larger than its original size, you set texture.magFilter property to either THREE.NearestFilter or THREE.LinearFilter.
NearestFilter − This filter uses the color of the nearest texel that it can find.
LinearFilter − This filter is more advanced and uses the color values of the four neighboring texels to determine the correct color.
And, you can add how many times to repeat the texture.
const timesToRepeatHorizontally = 4 const timesToRepeatVertically = 2 texture.repeat.set(timesToRepeatHorizontally, timesToRepeatVertically)
Example
Check out the following example.
texture.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Three.js - Checker Board</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: -applesystem, BpnkMacSystemFont, Segoe UI , Roboto, Oxygen, Ubuntu, Cantarell, Open Sans , Helvetica Neue , sans-serif; } html, body { height: 100vh; width: 100vw; } #threejs-container { position: block; width: 100%; height: 100%; } </style> <script src="https://cdnjs.cloudflare.com/ajax/pbs/three.js/r128/three.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/pbs/dat-gui/0.7.7/dat.gui.js"></script> </head> <body> <span id="threejs-container"></span> <script type="module"> // Creating a checker-board using Textures // applying the texture to 2d plane geometry // GUI const gui = new dat.GUI() // sizes let width = window.innerWidth let height = window.innerHeight // scene const scene = new THREE.Scene() scene.background = new THREE.Color(0x262626) // camera const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 100) camera.position.set(0, 0, 10) const camFolder = gui.addFolder( Camera ) camFolder.add(camera.position, z ).min(10).max(60).step(10) camFolder.open() // Light const ambientLight = new THREE.AmbientLight(0xffffff, 1) scene.add(ambientLight) // texture const planeSize = 10 const loader = new THREE.TextureLoader() const texture = loader.load( https://cloud-nfpbfxp6x-hack-clubbot.vercel.app/0height.png ) texture.wrapS = THREE.RepeatWrapping texture.wrapT = THREE.RepeatWrapping texture.magFilter = THREE.NearestFilter const repeats = planeSize / 2 texture.repeat.set(repeats, repeats) class StringToNumberHelper { constructor(obj, prop) { this.obj = obj this.prop = prop } get value() { return this.obj[this.prop] } set value(v) { this.obj[this.prop] = parseFloat(v) } } const wrapModes = { ClampToEdgeWrapping: THREE.ClampToEdgeWrapping, RepeatWrapping: THREE.RepeatWrapping, MirroredRepeatWrapping: THREE.MirroredRepeatWrapping } function updateTexture() { texture.needsUpdate = true } gui .add(new StringToNumberHelper(texture, wrapS ), value , wrapModes) .name( texture.wrapS ) .onChange(updateTexture) gui .add(new StringToNumberHelper(texture, wrapT ), value , wrapModes) .name( texture.wrapT ) .onChange(updateTexture) gui.add(texture.repeat, x , 0, 5, 0.01).name( texture.repeat.x ) gui.add(texture.repeat, y , 0, 5, 0.01).name( texture.repeat.y ) // plane for board const geometry = new THREE.PlaneGeometry(planeSize, planeSize) const material = new THREE.MeshPhongMaterial({ map: texture, side: THREE.DoubleSide }) const board = new THREE.Mesh(geometry, material) board.position.set(0, 0, 0) scene.add(board) // responsiveness window.addEventListener( resize , () => { width = window.innerWidth height = window.innerHeight camera.aspect = width / height camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight) renderer.render(scene, camera) }) // renderer const renderer = new THREE.WebGL1Renderer() renderer.setSize(width, height) renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) // animation function animate() { requestAnimationFrame(animate) renderer.render(scene, camera) } // rendering the scene const container = document.querySelector( #threejs-container ) container.append(renderer.domElement) renderer.render(scene, camera) console.log(scene.children) animate() </script> </body> </html>
Output
Texture Mapping
base color map
It is the basic colored image you add to the object to the texture. With a base color map we add colors to the surface.
const textureMap = new THREE.TextureLoader().load( /path/to/texture-map ) material.map = textureMap
You can add the effect of depth using a bump map or normal map or distance map.
bump map
A bump map is a grayscale image, where the intensity of each pixel determines the height. You can just set the material bumpMap property to the texture. It adds fine details to the texture.
const textureBumpMap = new THREE.TextureLoader().load( /path/to/bump-map ) material.bumpMap = textureBumpMap
Normal Maps
A normal map describes the normal vector for each pixel, which should be used to calculate how pght affects the material used in the geometry. It creates an illusion of depthness to the flat surface.
const textureNormalMap = new THREE.TextureLoader().load( /path/to/normal-map ) material.normalMap = textureNormalMap
Displacement Map
While the normal map gives an illusion of depth, we change the model s shape, with a displacement map based on the information from the texture.
const textureDisplacementMap = new THREE.TextureLoader().load( /path/to/displacement-map ) material.displacemetMap = textureDisplacementMap
Roughness Map
The roughness map defines which areas are rough and that affects the reflection sharpness from the surface.
const textureRoughnessMap = new THREE.TextureLoader().load( /path/to/roughness-map ) material.roughnessMap = textureRoughnessMap
Ambient Occlusion Map
It highpghts the shadow areas of the object. It requires a second set of UVs.
const textureAmbientOcclusionMap = new THREE.TextureLoader().load( /path/to/AmbientOcclusion-map ) material.aoMap = textureAmbientOcclusionMap // second UV mesh.geometry.attributes.uv2 = mesh.geometry.attributes.uv
If you compare the objects with roughness map and ambient occlusion map, you can observe that The shadows are more highpghted after using aoMap.
Metalness Map
It defines how much the material is pke a metal.
const textureMetalnessMap = new THREE.TextureLoader().load( /path/to/metalness-map ) material.metalnessMap = textureMetalnessMap
Example
Now, check out the following example
texture-maps.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Three.js - Texture Mapping</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: -applesystem, BpnkMacSystemFont, Segoe UI , Roboto, Oxygen, Ubuntu, Cantarell, Open Sans , Helvetica Neue , sans-serif; } html, body { height: 100vh; width: 100vw; } #threejs-container { position: block; width: 100%; height: 100%; } </style> <script src="https://cdnjs.cloudflare.com/ajax/pbs/three.js/r128/three.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/pbs/dat-gui/0.7.7/dat.gui.js"></script> </head> <body> <span id="threejs-container"></span> <script type="module"> // Using different types of texture maps import { OrbitControls } from "https://threejs.org/examples/jsm/controls/OrbitControls.js" // sizes let width = window.innerWidth let height = window.innerHeight // scene const scene = new THREE.Scene() scene.background = new THREE.Color(0xffffff) // pghts const ambientLight = new THREE.AmbientLight(0xffffff, 0.5) scene.add(ambientLight) const pght = new THREE.DirectionalLight(0xffffff, 4.0) pght.position.set(0, 10, 20) pght.castShadow = true pght.shadow.mapSize.width = 512 pght.shadow.mapSize.height = 512 pght.shadow.camera.near = 0.5 pght.shadow.camera.far = 100 scene.add(pght) // camera const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100) camera.position.set(0, 0, 10) // textures const loader = new THREE.TextureLoader() const texture = loader.load( https://cloud-nfpbfxp6x-hack-clubbot.vercel.app/5basecolor.jpg ) const normalmap = loader.load( https://cloud-nfpbfxp6x-hack-clubbot.vercel.app/2normal.jpg ) const heightmap = loader.load( https://cloud-nfpbfxp6x-hack-clubbot.vercel.app/0height.png ) const roughmap = loader.load( https://cloud-nfpbfxp6x-hack-clubbot.vercel.app/3roughness.jpg ) const ambientOcclusionmap = loader.load( https://cloud-nfpbfxp6x-hackclub-bot.vercel.app/4ambientocclusion.jpg ) const metalpcmap = loader.load( https://cloud-nfpbfxp6x-hack-clubbot.vercel.app/1metalpc.jpg ) // plane const planeGeometry = new THREE.PlaneGeometry(100, 100) const plane = new THREE.Mesh( planeGeometry, new THREE.MeshPhongMaterial({ color: 0xffffff, side: THREE.DoubleSide }) ) plane.rotateX(-Math.PI / 2) plane.position.y = -2.75 plane.receiveShadow = true scene.add(plane) // object const geometry = new THREE.SphereGeometry(1, 64, 64) const material1 = new THREE.MeshStandardMaterial({ map: texture, side: THREE.DoubleSide }) const object1 = new THREE.Mesh(geometry, material1) object1.position.set(-2.5, 1.5, 0) object1.castShadow = true scene.add(object1) // normal map const material2 = new THREE.MeshStandardMaterial({ color: 0xffffff, map: texture, side: THREE.DoubleSide, normalMap: normalmap }) const object2 = new THREE.Mesh(geometry, material2) object2.position.set(0, 1.5, 0) object2.castShadow = true scene.add(object2) // displacement map const material3 = new THREE.MeshStandardMaterial({ color: 0xffffff, map: texture, side: THREE.DoubleSide, normalMap: normalmap, displacementMap: heightmap, displacementScale: 0.05 }) const object3 = new THREE.Mesh(geometry, material3) object3.position.set(2.5, 1.5, 0) object3.castShadow = true scene.add(object3) console.log(object3) // roughness map const material4 = new THREE.MeshStandardMaterial({ color: 0xffffff, map: texture, side: THREE.DoubleSide, normalMap: normalmap, displacementMap: heightmap, displacementScale: 0.05, roughnessMap: roughmap, roughness: 0.5 }) const object4 = new THREE.Mesh(geometry, material4) object4.position.set(-2.5, -1.5, 0) object4.castShadow = true scene.add(object4) console.log(object4) // ambient occlusion map const material5 = new THREE.MeshStandardMaterial({ color: 0xffffff, map: texture, side: THREE.DoubleSide, normalMap: normalmap, displacementMap: heightmap, displacementScale: 0.05, roughnessMap: roughmap, roughness: 0.1, aoMap: ambientOcclusionmap }) const object5 = new THREE.Mesh(geometry, material5) object5.position.set(0, -1.5, 0) object5.geometry.attributes.uv2 = object5.geometry.attributes.uv object5.castShadow = true scene.add(object5) console.log(object5) // for env maps const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(128, { format: THREE.RGBFormat, generateMipMaps: true, minFilter: THREE.LinearMipmapLinearFilter, encoding: THREE.sRGBEncoding }) const cubeCamera = new THREE.CubeCamera(1, 10000, cubeRenderTarget) cubeCamera.position.set(0, 100, 0) scene.add(cubeCamera) // metalpc map const material6 = new THREE.MeshStandardMaterial({ color: 0xffffff, map: texture, side: THREE.DoubleSide, normalMap: normalmap, displacementMap: heightmap, displacementScale: 0.15, roughnessMap: roughmap, roughness: 0.1, aoMap: ambientOcclusionmap, metalnessMap: metalpcmap, metalness: 1, envMap: cubeRenderTarget.texture }) const object6 = new THREE.Mesh(geometry, material6) object6.position.set(2.5, -1.5, 0) object6.geometry.attributes.uv2 = object6.geometry.attributes.uv object6.castShadow = true scene.add(object6) console.log(object6) cubeCamera.position.copy(object6.position) // responsiveness window.addEventListener( resize , () => { width = window.innerWidth height = window.innerHeight camera.aspect = width / height camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight) renderer.render(scene, camera) }) // renderer - anti-apasing const renderer = new THREE.WebGLRenderer({ antiapas: true }) renderer.physicallyCorrectLights = true renderer.setSize(width, height) renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) const controls = new OrbitControls(camera, renderer.domElement) // animation function animate() { requestAnimationFrame(animate) let objects = [object1, object2, object3, object4, object5, object6] objects.forEach((i) => { //i.rotation.x += 0.005 i.rotation.y += 0.01 }) controls.update() cubeCamera.update(renderer, scene) renderer.render(scene, camera) } // rendering the scene const container = document.querySelector( #threejs-container ) container.append(renderer.domElement) renderer.render(scene, camera) animate() </script> </body> </html>
Output
There are some other maps for creating a real-world model in computer graphics. You can learn more
. Advertisements