# Bush

> A soft, rounded low-poly shrub built from overlapping jittered foliage blobs in three leaf tones, with optional clusters of shiny berries dotted over its surface. Scale it and recolor leaves or berries for gardens, hedgerows, forest undergrowth and cozy farm scenes.

![Bush preview](https://codels.download/m/bush.png)

Multi-view sheet (3/4, front, side, top): https://codels.download/m/bush.sheet.png

## Facts

- Category: Nature › Plants & Flowers
- Size (W × H × D): 1.27 × 0.97 × 1.31 m — units are meters, +Y up, front faces +Z
- Origin: center of the base (y = 0)
- Triangles: 760 · Meshes: 20 · Materials: 4
- License: CC0-1.0 (Codels built-in)
- Tags: berry-bush, bush, low-poly, stylized, farm, forest, garden, grassland, village, farming-sim, rpg, survival, background, decoration, vegetation, foliage-material, round, instancing-friendly, mobile-friendly, parametric, color-green, size-medium, poly-low
- Page: https://codels.download/models/bush

## Files

| Format | URL |
|---|---|
| three.js ES module (`createBush()`) | https://codels.download/m/bush.js |
| React Three Fiber component (`<Bush />`) | https://codels.download/m/bush.jsx |
| Instancing module (`createBushInstances(count, place)`, one draw call per material) | https://codels.download/m/bush.instanced.js |
| Standalone HTML demo | https://codels.download/m/bush.html |
| glTF binary | https://codels.download/m/bush.glb |
| glTF binary, Meshopt-compressed (2–3× smaller; GLTFLoader needs `setMeshoptDecoder(MeshoptDecoder)`) | https://codels.download/m/bush.glb?compress=meshopt |
| glTF JSON | https://codels.download/m/bush.gltf |
| OBJ (vertex colors) + MTL | https://codels.download/m/bush.obj · https://codels.download/m/bush.mtl |
| STL | https://codels.download/m/bush.stl |
| CodelSpec JSON | https://codels.download/m/bush.json |

## Parameters

Append them as query parameters to any URL above to get a customized variant (e.g. `?size=...`).

| Param | Type | Range / options | Default | Current |
|---|---|---|---|---|
| `size` | number | 0.4 – 2.5 | 1 | 1 |
| `leaves` | color | #rrggbb | #4f8f3e | #4f8f3e |
| `berries` | boolean | true \| false | true | true |
| `berryColor` | color | #rrggbb | #d93448 | #d93448 |

## Parts

- bush (group)
  - foliage1 (icosahedron · leaf)
  - foliage2 (icosahedron · leafDark)
  - foliage3 (icosahedron · leafDark)
  - foliage4 (icosahedron · leaf)
  - foliage5 (icosahedron · leafLight)
  - foliage6 (icosahedron · leafLight)
  - berry1 (icosahedron · berry)
  - berry2 (icosahedron · berry)
  - berry3 (icosahedron · berry)
  - berry4 (icosahedron · berry)
  - berry5 (icosahedron · berry)
  - berry6 (icosahedron · berry)
  - berry7 (icosahedron · berry)
  - berry8 (icosahedron · berry)
  - berry9 (icosahedron · berry)
  - berry10 (icosahedron · berry)
  - berry11 (icosahedron · berry)
  - berry12 (icosahedron · berry)
  - berry13 (icosahedron · berry)
  - berry14 (icosahedron · berry)

## How to use

Save the code below as `bush.js`, then:

```js
import { createBush } from './bush.js';
const model = createBush();
scene.add(model);
```

## Code

```js
/**
 * Bush — Codels 3D model library (https://codels.download/models/bush)
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 760 · Size (W × H × D): 1.27 × 0.97 × 1.31 m
 *
 * Usage:
 *   import { createBush } from './bush.js';
 *   
 *   const model = createBush();
 *   model.position.set(0, 0, 0);
 *   scene.add(model);
 */
import * as THREE from 'three';

const deg = THREE.MathUtils.degToRad;

// Deterministic vertex displacement (organic low-poly look).
function jitter(geometry, amount, seed) {
  const pos = geometry.attributes.position;
  const h = (a, b, c, s) => {
    let x = Math.imul(a, 73856093) ^ Math.imul(b, 19349663) ^ Math.imul(c, 83492791) ^ Math.imul(s, 2654435761);
    x = Math.imul(x ^ (x >>> 16), 2246822507);
    x = Math.imul(x ^ (x >>> 13), 3266489909);
    return ((x ^ (x >>> 16)) >>> 0) / 4294967295 - 0.5;
  };
  for (let i = 0; i < pos.count; i++) {
    const x = pos.getX(i), y = pos.getY(i), z = pos.getZ(i);
    const qx = Math.round(x * 1000), qy = Math.round(y * 1000), qz = Math.round(z * 1000);
    pos.setXYZ(i, x + h(qx, qy, qz, seed) * 2 * amount, y + h(qx, qy, qz, seed + 1) * 2 * amount, z + h(qx, qy, qz, seed + 2) * 2 * amount);
  }
  pos.needsUpdate = true;
  geometry.computeVertexNormals();
  return geometry;
}

export function createBush() {
  const root = new THREE.Group();
  root.name = 'Bush';

  // Materials
  const leafMat = new THREE.MeshStandardMaterial({ name: 'leaf', color: 0x4f8f3e, roughness: 0.85, flatShading: true });
  const leafLightMat = new THREE.MeshStandardMaterial({ name: 'leafLight', color: 0x519848, roughness: 0.85, flatShading: true });
  const leafDarkMat = new THREE.MeshStandardMaterial({ name: 'leafDark', color: 0x447232, roughness: 0.9, flatShading: true });
  const berryMat = new THREE.MeshStandardMaterial({ name: 'berry', color: 0xd93448, roughness: 0.35, flatShading: true });

  // Shared geometries
  const icosahedronGeo = new THREE.IcosahedronGeometry(0.042, 0);

  // Parts
  const bush = new THREE.Group();
  bush.name = 'bush';
  bush.position.set(0, 0.0691, 0);
  root.add(bush);
  const foliage1 = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.4347, 1), 0.0565, 30), leafMat);
  foliage1.name = 'foliage1';
  foliage1.position.set(0, 0.42, 0);
  foliage1.rotation.set(deg(54.8202), deg(60.0912), 0);
  foliage1.scale.set(1, 0.82, 1);
  bush.add(foliage1);
  const foliage2 = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.3507, 1), 0.0456, 31), leafDarkMat);
  foliage2.name = 'foliage2';
  foliage2.position.set(0.3304, 0.3, 0.143);
  foliage2.rotation.set(deg(59.872), deg(330.3399), 0);
  foliage2.scale.set(1, 0.82, 1);
  bush.add(foliage2);
  const foliage3 = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.2979, 1), 0.0387, 32), leafDarkMat);
  foliage3.name = 'foliage3';
  foliage3.position.set(-0.3062, 0.28, 0.225);
  foliage3.rotation.set(deg(33.3982), deg(322.7924), 0);
  foliage3.scale.set(1, 0.82, 1);
  bush.add(foliage3);
  const foliage4 = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.3418, 1), 0.0444, 33), leafMat);
  foliage4.name = 'foliage4';
  foliage4.position.set(-0.0078, 0.3, -0.3499);
  foliage4.rotation.set(deg(7.7251), deg(65.4064), 0);
  foliage4.scale.set(1, 0.82, 1);
  bush.add(foliage4);
  const foliage5 = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.3069, 1), 0.0399, 34), leafLightMat);
  foliage5.name = 'foliage5';
  foliage5.position.set(0.0977, 0.4, 0.2836);
  foliage5.rotation.set(deg(6.7732), deg(139.3173), 0);
  foliage5.scale.set(1, 0.82, 1);
  bush.add(foliage5);
  const foliage6 = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.2787, 1), 0.0362, 35), leafLightMat);
  foliage6.name = 'foliage6';
  foliage6.position.set(-0.1875, 0.62, -0.0697);
  foliage6.rotation.set(deg(44.9884), deg(44.365), 0);
  foliage6.scale.set(1, 0.82, 1);
  bush.add(foliage6);
  const berry1 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry1.name = 'berry1';
  berry1.position.set(0.5758, 0.4724, -0.0034);
  bush.add(berry1);
  const berry2 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry2.name = 'berry2';
  berry2.position.set(-0.37, 0.5033, 0.3439);
  bush.add(berry2);
  const berry3 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry3.name = 'berry3';
  berry3.position.set(0.041, 0.5464, -0.4877);
  bush.add(berry3);
  const berry4 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry4.name = 'berry4';
  berry4.position.set(0.3068, 0.5774, 0.3847);
  bush.add(berry4);
  const berry5 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry5.name = 'berry5';
  berry5.position.set(-0.4628, 0.5985, -0.0878);
  bush.add(berry5);
  const berry6 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry6.name = 'berry6';
  berry6.position.set(0.3318, 0.6127, -0.2138);
  bush.add(berry6);
  const berry7 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry7.name = 'berry7';
  berry7.position.set(-0.0908, 0.6109, 0.332);
  bush.add(berry7);
  const berry8 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry8.name = 'berry8';
  berry8.position.set(-0.1536, 0.6507, -0.3161);
  bush.add(berry8);
  const berry9 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry9.name = 'berry9';
  berry9.position.set(0.313, 0.6829, 0.118);
  bush.add(berry9);
  const berry10 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry10.name = 'berry10';
  berry10.position.set(-0.275, 0.7026, 0.1221);
  bush.add(berry10);
  const berry11 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry11.name = 'berry11';
  berry11.position.set(0.1142, 0.7139, -0.2438);
  bush.add(berry11);
  const berry12 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry12.name = 'berry12';
  berry12.position.set(0.068, 0.7038, 0.2105);
  bush.add(berry12);
  const berry13 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry13.name = 'berry13';
  berry13.position.set(-0.2328, 0.8625, -0.1382);
  bush.add(berry13);
  const berry14 = new THREE.Mesh(icosahedronGeo, berryMat);
  berry14.name = 'berry14';
  berry14.position.set(0.1989, 0.8042, -0.0491);
  bush.add(berry14);

  root.traverse((o) => {
    if (o.isMesh) {
      o.castShadow = true;
      o.receiveShadow = true;
    }
  });
  return root;
}

export default createBush;
```

---
Codels — 3D models as code for three.js. Search more: https://codels.download/api/v1/models?q=<query> · Guide: https://codels.download/llms.txt
