As an addition to above answer, there are several factors can cause to this as you have so many objects and lighting can also affect to this.
I would use LODs too and depending on how far/near you are to those objects you can add/remove details.
For simple objects:
for( let i = 0; i < 3; i++ ) {
const geometry = new THREE.IcosahedronGeometry( 10, 3 - i );
const mesh = new THREE.Mesh( geometry, material );
lod.addLevel( mesh, i * 75 );
}
scene.add( lod );
If you want to implement it with GLTF models then you may try to create different versions (high/medium/low) of the models and add them to LOD.
You might also check your lighting setup as lights taking quite some memory.
Edit
But I don't like the sudden change of levels, it's very noticeable. Don't know how to make a smooth level change?
You may apply some shaders or play with opacity values depending on the requirements. I created updateLOD
function to demonstrate how you can apply opacity
to objects depending on their distance to the camera. This is simple scenario, but you can make it better.
const cursor = {
x: 0,
y: 0,
}
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
60,
window.innerWidth / window.innerHeight,
1,
1000,
)
camera.position.set(0, 0, 200)
const renderer = new THREE.WebGLRenderer({
antialias: true,
})
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setAnimationLoop(animate)
document.body.appendChild(renderer.domElement)
const materials = [
new THREE.MeshBasicMaterial({
color: 0xff4444,
transparent: true,
depthWrite: false,
}),
new THREE.MeshBasicMaterial({
color: 0x44ff44,
transparent: true,
depthWrite: false,
}),
new THREE.MeshBasicMaterial({
color: 0x4444ff,
transparent: true,
depthWrite: false,
}),
]
const lodObjects = []
for (let i = 0; i < 1000; i++) {
const lod = new THREE.Object3D()
const position = new THREE.Vector3(
(Math.random() - 0.5) * 800,
(Math.random() - 0.5) * 800,
(Math.random() - 0.5) * 800,
)
lod.position.copy(position)
const lodLevels = []
for (let l = 0; l < 3; l++) {
const geometry = new THREE.ConeGeometry(5, 20, 32)
const mesh = new THREE.Mesh(geometry, materials[l].clone())
mesh.visible = true
lod.add(mesh)
lodLevels.push(mesh)
}
lod.userData.levels = lodLevels
lodObjects.push(lod)
scene.add(lod)
}
function updateLOD() {
for (const lod of lodObjects) {
const distance = camera.position.distanceTo(lod.position)
const levels = lod.userData.levels
const fadeRange = 60
for (let i = 0; i < levels.length; i++) {
const mesh = levels[i]
const minDist = i * fadeRange
const maxDist = (i + 1) * fadeRange
if (distance >= minDist && distance <= maxDist && i < levels.length - 1) {
// apply crossfade
const t = (distance - minDist) / (maxDist - minDist)
mesh.material.opacity = 1 - t
levels[i + 1].material.opacity = t
mesh.visible = true
levels[i + 1].visible = true
} else if (distance < minDist && i === 0) {
mesh.material.opacity = 1
mesh.visible = true
} else if (distance > maxDist && i === levels.length - 1) {
mesh.material.opacity = 1
mesh.visible = true
} else {
mesh.visible = false
}
}
}
}
function animate() {
updateLOD()
renderer.render(scene, camera)
}
window.addEventListener("mousemove", () => {
cursor.y = -event.clientY / window.innerHeight
camera.position.z = cursor.y * 200
})
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
})
<style>
* {
margin: 0;
padding: 0
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>