Three.js Material which lets you do Texture Projection on a 3d Model.
This method supports TypeScript
.
If you're familiar with Node.js and managing dependencies
with npm
, and have a build setup in place or know how to serve ES modules to a
browser, then install three
and @lume/three-projected-material
from NPM,
npm install three @lume/three-projected-material
then import ProjectedMaterial
and you'll be on your way. If you're writing ES
modules, import like this:
import {ProjectedMaterial} from '@lume/three-projected-material/dist/ProjectedMaterial.js'
const mat = new ProjectedMaterial(/*...*/)
// ...
If you're writing old-school CommonJS modules (consider migrating already!) you
can use dynamic import()
as long as your version of Node or your build tools
are new enough to support it:
import('@lume/three-projected-material/dist/ProjectedMaterial.js').then(({ProjectedMaterial}) => {
const mat = new ProjectedMaterial(/*...*/)
// ...
})
These methods do not support TypeScript
, only plain JavaScript
.
If you don't have a build setup or you are only familiar with plain HTML files,
import @lume/three-projected-material
from a
CDN such as
https://unpkg.com, like so:
<script type="module">
import {ProjectedMaterial} from 'https://unpkg.com/@lume/three-projected-material@^3.0.0/dist/ProjectedMaterial.js'
const mat = new ProjectedMaterial(/*...*/)
// ...
</script>
If you're still loading libraries as global variables using old-school script
tags (f.e. you have a global THREE
object), you can use the .global.js
file,
although we recommend using ES Modules as per above. Make sure to put the global
script after the three.js script:
<script src="..../three.js"></script>
<script src="https://unpkg.com/@lume/three-projected-material@^3.0.0/dist/ProjectedMaterial.global.js"></script>
<script>
const {ProjectedMaterial} = window.projectedMaterial
const mat = new ProjectedMaterial(/*...*/)
// ...
</script>
Use ProjectedMaterial
like so:
// ... get the ProjectedMaterial class as per above ...
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new ProjectedMaterial({
camera, // the camera that acts as a projector
texture, // the texture being projected
textureScale: 0.8, // scale down the texture a bit
textureOffset: new THREE.Vector2(0.1, 0.1), // you can translate the texture if you want
cover: true, // enable background-size: cover behaviour, by default it's like background-size: contain
color: '#ccc', // the color of the object if it's not projected on
roughness: 0.3, // you can pass any other option that belongs to MeshPhysicalMaterial
})
const box = new THREE.Mesh(geometry, material)
webgl.scene.add(box)
// move the mesh any way you want!
box.rotation.y = -Math.PI / 4
// and when you're ready project the texture on the box!
material.project(box)
ProjectedMaterial
also supports instanced meshes via three.js' InstancedMesh, and even multiple projections. Check out the examples below for a detailed guide!
Create a new material to later use for a mesh.
Option | Default | Description |
---|---|---|
camera |
The PerspectiveCamera the texture will be projected from. Any time you change this after the initial value, remember to set material.needsUpdate = true . |
|
texture |
The Texture being projected. | |
textureScale |
1 | Make the texture bigger or smaller. |
textureOffset |
new THREE.Vector2() |
Offset the texture in a x or y direction. The unit system goes from 0 to 1, from the bottom left corner to the top right corner of the projector camera frustum. |
fitment |
'contain' |
Possible values: 'cover' , 'contain' . Whether the texture should fit like CSS object-fit: cover or object-fit: contain within the projector camera frustum. By default it fits like object-fit: contain . See the descriptions of those in the MDN docs. |
frontFacesOnly |
true |
A boolean. If true , the texture is projected only onto faces that face towards the projector much like a real life projector, otherwise the projected texture will "pass through" the whole object and will also paint the faces facing away. |
...options |
Other options you pass to any three.js material like color , opacity , envMap and so on. The material is built from a MeshPhysicalMaterial, so you can pass any property of that material and of its parent MeshStandardMaterial. |
These properties are exposed as properties of the material, so you can change them later. For example, to update the material texture and change its scale:
material.texture = newTexture
material.textureScale = 0.8
Project the texture from the camera on the mesh. With this method we "take a snaphot" of the current mesh and camera position in space. The After calling this method, you can move the mesh or the camera freely.
Option | Description |
---|---|
mesh |
The THREE.Mesh that has a ProjectedMaterial as material. |
updateWorldMatrices |
Optional, defaults to true . A boolean indicating whether or not to update world matrices of the projection camera and of the mesh that has the ProjectedMaterial . Setting it to false is useful if you will be updating world matrices externally first. |
Call this any time the camera passed into ProjectedMaterial
has had its parameters updated.
const camera = new PerspectiveCamera(...)
const mat = new ProjectedMaterial({camera})
// then later if you modify the camera:
camera.fov = 40
camera.updateProjectionMatrix()
mat.updateFromCamera() // don't forget to update the material too
Allocate the data that will be used when projecting on an InstancedMesh. Use this on the geometry that will be used in pair with a ProjectedMaterial
when initializing InstancedMesh
.
This needs to be called before .projectInstanceAt()
.
Option | Description |
---|---|
geometry |
The geometry that will be passed to the InstancedMesh . |
instancesCount |
The number of instances, the same that will be passed to the InstancedMesh . |
Do the projection for an InstancedMesh. Don't forget to call updateMatrix()
like you do before calling InstancedMesh.setMatrixAt()
.
To do projection on an instanced mesh, the geometry needs to be prepared with allocateProjectionData()
beforehand.
dummy.updateMatrix()
material.projectInstanceAt(i, instancedMesh, dummy.matrix)
Link to the full example about instancing.
Option | Description |
---|---|
index |
The index of the instanced element to project. |
instancedMesh |
The InstancedMesh with a projected material. |
matrix |
The matrix of the dummy you used to position the instanced mesh element. Be sure to call .updateMatrix() beforehand. |