Color bottom x% of a BoxGeometry using faces

So I’m working on a 3D warehouse map type thing. It’s made up of a bunch of rows and columns of boxes like so:

boxes

Now I’m trying to visualize how “full” a certain warehouse position is. Basically, if it’s 20% full color the bottom 20%, if it’s 80% full color the bottom 80%, etc. Here’s an amazing Paint illustration:

fill

So I would need to color the bottom side, the top side only if it’s 100% full, and x% of every other side.

Is this possible using faces? My first thought was to pass 50 (or another number) width segments to the constructor:

var boxGeometry = new THREE.BoxGeometry(width, height, depth, 1, 50, 1);

then use vertexColors: THREE.FaceColors in the main beige material, loop through the faces and color the ones I need. But then the faces array is just an array of 404 elements thrown together. How do I divide them by side and then loop through them, starting from the faces at the bottom? Otherwise, is there a better solution that doesn’t involve drawing multiple boxes?

Have you considered to use a custom shader material for the sides of your box? You could use a uniform that controls the coloring according to the texture coordinates. Meaning the bottom vertices have a v value of 0 whereas the top vertices have a v value of 1. If you represent the used space of a warehouse as a value between 0 and 1, you can use it as a threshold in order to switch between colors. The fragment shader looks like so:

varying vec2 vUv;

uniform float threshold;

void main()	{

	float f = step( threshold, vUv.y );
	
	vec3 color1 = vec3( 1.0, 0.0, 0.0 );
	vec3 color2 = vec3( 1.0, 1.0, 1.0 );
	
	vec3 color = mix( color1, color2, f );

	gl_FragColor = vec4( color, 1.0 );

}

Demo: https://jsfiddle.net/5gdcv9ue/

3 Likes

The same idea with shaders.
Just modified MeshLambertMaterial with some antialiasing and visual effect :upside_down_face:

3 Likes

It seems the lines

    const loader = new THREE.TextureLoader();
	const map = loader.load( 'https://threejs.org/examples/textures/uv_grid_opengl.jpg' );

are not necessary here.

2 Likes

Good catch! I have updated the fiddle.

@prisoner849 Fantastic work as usual! :+1:

1 Like

Looks perfect but I have a couple of questions since I’ve indeed never used shaders:

  • How gpu intensive is this for a larger number of meshes? Say, 400+ plus the meshes for their edges

  • I have some raycasting set up to select warehouse positions, and it changes the color and opacity of the position with material.color and material.opacity. Is it possible to maintain this behaviour with ShaderMaterial relatively easily? Maybe even change the color of the filled part dynamically so that they can look solid when selected?

  • I can’t make it transparent and then still have the red part at the bottom, right? Because it’s only one material and opacity would affect the red? I’m not planning to use transparency now but it would be cool to have the option ready in the future