How to change the textures of each instance dynamically?

I’m creating a game which is full of characters that are just a basic spheres. Each sphere has it’s own color, and a texture applied. Since there will be 100s of “characters” with around 20 textures that need to dynamically change, I wish to instance them.

How can this be accomplished, if possible? Would I need to create a separate instance for each texture?

Using mesh.setColorAt( i, color ); works for color, but is there an offset option for the texture, or something similar?

Creating the characters as a separate object, I achieve this via offsetting the texture

// my object
this.face = Math.floor( Math.random( ) * 16 );
this.texture = textures['faces-atlas'].clone( ); // 2048x2048 texture atlas comprised of 512x512 textures (4x4)
this.texture.center.x = 0;
this.texture.center.y = 0;
this.texture.offset.x = 0.25 * ( this.face % 4 );
this.texture.offset.y = 0.25 * Math.floor( this.face/4 );
this.geometry = new THREE.SphereGeometry( this.size/2, 32, 16 );
this.material = new THREE.MeshToonMaterial( {
	color: this.color,
	map: this.texture,
	gradientMap: this.toonTexture
} );
	
this.material.onBeforeCompile = ( shader ) => {
	const custom_map_fragment = THREE.ShaderChunk.map_fragment.replace(
		`diffuseColor *= sampledDiffuseColor;`,
		`diffuseColor = vec4( mix( diffuse, sampledDiffuseColor.rgb, sampledDiffuseColor.a ), opacity );`
	);
		
	shader.fragmentShader = shader.fragmentShader.replace( '#include <map_fragment>', custom_map_fragment );
};

this.object = new THREE.Mesh( this.geometry, this.material ); // need this 'object' instanced & change each texture dynamically

The point of InstancedMeshes is to have duplicates of exactly the same material-geometry combinations - you cannot swap material parameters between the instances (InstancedMesh does allow for some customization in terms of color passed independently, but that won’t help much for textures.)

If that’s a viable option, you can create a texture atlas (ie. all character texture options bundled up into a single image), and then pass that as the texture for instanced meshes - but you’ll need a custom shader for that, so that you can pick specific subregion of that texture atlas based on gl_InstanceID.

3 Likes

Hi mjurczyk, thanks for the reply!

Yep I’m using a single image for all the textures, I got that far reading upon it, I just don’t know the method used of picking the specific subregion

You can use an instanced buffer attribute with an indices of texture chunks, or with pre-computed UVs per instance.

For example, I used the first approach (with texture chunk indices)


Demo: JSFiddle

3 Likes

Hey prisoner, perfect thank you!

1 Like