![Biplanar](/.github/biplanar-mapping_900x600.png)

# defold-biplanar-mapping

World-space biplanar texture mapping shader for [Defold](https://defold.com). Applies textures based on world position and surface normals — no UV unwrapping required. Ideal for terrain, rocks, and organic geometry.

## How It Works

The shader projects two textures onto geometry using world-space coordinates:

- **`tex0`** (side/rock) — sampled via X and Z axis projections, blended by the surface normal's X/Z weight. Only two projections are used (biplanar) instead of three (triplanar) for better performance.
- **`tex1`** (top/grass) — sampled via a top-down XZ projection.

The fragment shader blends between `tex0` and `tex1` based on `normal.y` (how much the surface faces upward). An optional **mid-band color overlay** can be applied at a configurable normal.y band — useful for transition zones like moss or dirt rings. A **ground color** fades in below a configurable world-Y height.



## Usage

1. Assign `biplanar_instanced.material` to your Model component.
2. Set `tex0` to your side texture (e.g. rock).
3. Set `tex1` to your top texture (e.g. grass).
4. Tune uniforms via the material defaults or at runtime with `go.set()`.

> **Note:** This material uses instanced rendering. The world transform is supplied automatically as a vertex attribute — no extra setup is needed.

### Runtime Uniform Override

All uniforms can be set per-object at runtime:

```lua
-- Set texture tiling
go.set("/go#model", "texture_scale", vmath.vector4(0.2, 0.2, 0.2, 0.05))

-- Adjust side→top blend range
go.set("/go#model", "texture_params", vmath.vector4(0.7, 0.9, -2.5, 0.0))

-- Disable mid-band overlay
go.set("/go#model", "mid_params", vmath.vector4(0.8, 0.18, 0.0, 0.0))

-- Custom ground color (dark soil)
go.set("/go#model", "ground_color", vmath.vector4(0.1, 0.08, 0.05, 1.0))

-- Custom mid-band color (moss)
go.set("/go#model", "mid_color", vmath.vector4(0.3, 0.45, 0.2, 1.0))
```

## Uniforms

### `texture_scale` — `vec4`

Controls UV tiling for all projections.

| Component | Description | Default |
|-----------|-------------|---------|
| `x` | Side texture scale — X axis projection | `0.2` |
| `y` | Side texture scale — Y component of all side UVs | `0.2` |
| `z` | Side texture scale — Z axis projection | `0.2` |
| `w` | Top texture scale (`tex1` top-down projection) | `0.05` |

---

### `texture_params` — `vec4`

Controls blending thresholds and ground darkening.

| Component | Description | Default |
|-----------|-------------|---------|
| `x` | `blend_start` — `normal.y` value where side→top blend begins | `0.7` |
| `y` | `blend_end` — `normal.y` value where surface is fully top | `0.9` |
| `z` | `ground_start` — world Y below which `ground_color` is fully applied | `-2.5` |
| `w` | `ground_end` — world Y above which `ground_color` has no influence | `0.0` |

Blend uses `smoothstep(blend_start, blend_end, normal.y)`.

---

### `mid_params` — `vec4`

Controls an optional mid-band color overlay (e.g. moss, snow line, waterline).

| Component | Description | Default |
|-----------|-------------|---------|
| `x` | `mid_center` — `normal.y` center of the overlay band | `0.8` |
| `y` | `mid_width` — width of the overlay band falloff | `0.18` |
| `z` | `mid_enable` — `0.0` = off, `1.0` = on | `1.0` |

---

### `ground_color` — `vec4`

RGBA color blended into `tex0` at and below `texture_params.z` (world Y ground level).

Default: `(0.0, 0.0, 0.0, 1.0)` (black)

---

### `mid_color` — `vec4`

RGBA color blended into the surface at the mid-band when `mid_params.z == 1.0`.

Default: `(0.639, 0.718, 0.369, 1.0)` (muted green)

## Textures

| Sampler | Role | Wrap | Filter |
|---------|------|------|--------|
| `tex0` | Side texture (rock, wall, bark…) | Repeat | Linear |
| `tex1` | Top texture (grass, snow, mud…) | Repeat | Linear |

## Blend Sharpness

The side projection blend uses `normal^4` by default (standard sharpness). To adjust, edit `biplanar_sample()` in `biplanar.fp`:

```glsl
vec3 a2 = an * an;
float bx = a2.x * a2.x; // ^4 — standard
float bz = a2.z * a2.z;

// Other options (swap bx/bz assignments):
// float bx = an.x * an.x;             // ^2 — softer
// float bx = a2.x * a2.x * a2.x;      // ^8 — sharper
// vec3 a8 = a2 * a2; float bx = a8.x * a8.x; // ^16 — near hard edge
```

## Requirements

- Add `exclude_gles_sm100 = 1` under `[shader]` in `game.project` to disable legacy GLES SM1.0 compilation:

```ini
[shader]
exclude_gles_sm100 = 1
```


## Credits

All Textures by: https://lynocs.itch.io/texture-pack