Search code examples
three.jsreact-three-fiber

Buildings in three.js


I am working on a web game and using three.js to do the 3d of the game. I am building a city and I want the buildings to look exactly like the picture below.

enter image description here

I want them to be a base material, without textures or colors because I will add colors later depending on the building status (like the game below).

This is what I have right now:

enter image description here

As you can see, I just have a bunch of different-sized boxes, with no details at all. How would I achieve the details seen in the game?

This is my current code:

export default function Map() {
  const RenderBuildings = () => {
    const buildings = []
    for (let i = 0; i < 10; i++) {
      buildings.push(<Box color="#dad3cb" width={1} height={1} depth={1} />)
    }

    return buildings
  }

  return (
    <Canvas>
      <CameraController />
      <ambientLight intensity={1} />
      <Ground color="#b0aa9d" width={40} height={1} depth={40} />
      {RenderBuildings()}
    </Canvas>
  )
}

export const Box = (props: Props) => {
  const { color, width, height, depth, ...rest } = props

  const mesh = useRef<THREE.Mesh>()
  const boxRef = useRef<THREE.Mesh>()

  useEffect(() => {
    if (!mesh.current) return

    const _mesh = mesh.current

    _mesh.position.x = Math.floor(Math.random() * 20)
    _mesh.position.z = Math.floor(Math.random() * 20)
    _mesh.scale.x =
      Math.random() * Math.random() * Math.random() * Math.random() * 5 + 3
    _mesh.scale.z = _mesh.scale.x
    _mesh.scale.y =
      Math.random() * Math.random() * Math.random() * _mesh.scale.x * 5 + 3
  }, [mesh])

  useEffect(() => {
    if (!boxRef.current) return

    const _boxRef = boxRef.current

    _boxRef.applyMatrix4(new Matrix4().makeTranslation(0, 0.5, 0))
  }, [boxRef])

  return (
    <mesh {...rest} ref={mesh}>
      <boxGeometry args={[width, height, depth]} ref={boxRef} />
      <meshToonMaterial color={color} />
    </mesh>
  )
}

Solution

  • Your first step should be to look at lighting your scene. This will allow for better depth to your buildings. A hemisphere light may be your best bet: https://threejs.org/examples/#webgl_lights_hemisphere. You can learn more about lights here: https://threejs.org/manual/#en/lights.

    Next, take a look at geometry. You want those buildings to have some level of detail at the geometry level. You can use primitives like these: https://threejs.org/manual/#en/primitives or build a model in any modelling software and import it in. At that point, maybe make a few models and then instance them randomly.

    Depending on how you do geometry, you will need to pick an appropriate material: https://threejs.org/manual/#en/materials. This should give you plenty of options.

    I would also add in a bit of animation to keep things lively: https://threejs.org/examples/#webgl_animation_keyframes

    Finally, a bit of fog always helps for ambiance: https://threejs.org/manual/#en/fog

    Some more inspiration: https://demos.littleworkshop.fr/infinitown

    Ultimately, building a scene like this is going to be a labour of love. There are no easy shortcuts. Keep at it!