- name
- dev-noise-texture
- description
- Add procedural noise texture generation to the asset pipeline — C tool, Python plugin, web UI with live preview
- user_invokable
- true
# Noise Texture Generation
Add procedural noise texture generation to the forge-gpu asset pipeline.
A C tool samples noise functions from `forge_math.h` across a pixel grid
and writes grayscale BMP heightmaps. A Python pipeline plugin invokes the
tool as a subprocess, and the web UI provides interactive parameter tuning
with live preview.
## When to use
- Generating procedural heightmaps for terrain
- Creating tileable noise textures (fBm, Worley, ridged multi-fractal)
- Deriving normal maps from heightmaps
- Adding procedural texture generation to the pipeline
## Key API calls
### C noise functions (forge_math.h)
| Function | Output | Use case |
|----------|--------|----------|
| `forge_noise_perlin2d` | [-1, 1] | Basic gradient noise |
| `forge_noise_simplex2d` | [-1, 1] | Less axis-aligned than Perlin |
| `forge_noise_fbm2d` | [-1, 1] | Multi-scale terrain, clouds |
| `forge_noise_ridged_fbm2d` | [0, 1] | Mountain ridges, rivers |
| `forge_noise_worley2d` | F1, F2 distances | Voronoi cells, edges |
| `forge_noise_domain_warp2d` | [-1, 1] | Organic distortion |
### Pipeline plugin (pipeline/plugins/noise.py)
| Function | Purpose |
|----------|---------|
| `find_noise_tool()` | Locate `forge_noise_tool` binary |
| `run_noise_tool()` | Invoke tool as subprocess with CLI args |
| `generate_noise_textures()` | Batch generation from pipeline.toml config |
| `NoisePlugin.process()` | Plugin entry point for pipeline integration |
### Server endpoints (pipeline/server.py)
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/api/generate/noise` | POST | Generate full-resolution texture |
| `/api/generate/noise/preview` | GET | Stream a 256×256 BMP preview |
## Correct order
### Adding a new noise type
1. Implement the noise function in `common/math/forge_math.h` with full
documentation (parameters, output range, usage example)
2. Add tests in `tests/math/test_math.c` (determinism, range, edge cases)
3. Add a `NOISE_*` enum value and `parse_type` case in `tools/noise/main.c`
4. Add the type to `VALID_TYPES` in `pipeline/plugins/noise.py`
5. Add it to the `NOISE_TYPES` array in `pipeline/web/src/routes/generate/noise.tsx`
6. Add it to the `NoiseGenerateRequest` type dropdown and server endpoint
### Setting up noise generation in a project
1. Build the tool: `cmake --build build --target forge_noise_tool`
2. Add `[noise]` config to `pipeline.toml`:
```toml
[noise]
noise_enabled = true
output_dir = "generated"
[[noise.textures]]
name = "terrain_height"
type = "fbm"
width = 1024
height = 1024
seed = 42
tiling = true
```
3. Generate: `uv run python -m pipeline generate-noise`
4. Or use the web UI: `uv run python -m pipeline serve --reload`
### Seamless tiling
Map 2D coordinates onto a 4D torus — each axis traces a circle in a
separate 2D plane:
```c
float angle_u = u * 2.0f * FORGE_PI;
float angle_v = v * 2.0f * FORGE_PI;
float r = frequency / (2.0f * FORGE_PI);
/* Noise sees (r*cos(u) + r*cos(v), r*sin(u) + r*sin(v)) — continuous
* across the tile boundary because each circle closes on itself. */
```
### Normal map derivation
Central differences on the heightmap with wrapping:
```c
float dx = (heightmap[y*w + xp] - heightmap[y*w + xm]) * strength;
float dy = (heightmap[yp*w + x] - heightmap[ym*w + x]) * strength;
/* Normal = normalize(-dx, -dy, 1), mapped to [0, 255] RGB */
```
## Common mistakes
- **Forgetting to normalize output.** Perlin/Simplex/fBm return [-1, 1],
ridged returns [0, 1], Worley returns [0, ~1.5]. The tool normalizes via
min/max scan before writing BMP pixels.
- **Tool not found on MSVC.** Multi-config generators put binaries in
`build/tools/noise/Debug/`. The `tool_finder.py` searches config
subdirectories automatically.
- **Normal map source ordering.** In pipeline.toml, the heightmap entry
must appear before the normal map entry that references it (the plugin
processes entries in order).
- **Tiling with Worley noise.** The 4D torus approximation works well for
gradient-based noise but can produce visible seams with Worley at low
frequencies. Increase frequency or use fBm/ridged for seamless tiles.
## Ready-to-use template
### C tool invocation
```bash
forge_noise_tool heightmap.bmp fbm \
--width 1024 --height 1024 --seed 42 \
--octaves 6 --frequency 4.0 --persistence 0.5 --tiling
forge_noise_tool normals.bmp fbm \
--width 1024 --height 1024 --seed 42 \
--octaves 6 --frequency 4.0 --normal-map --normal-strength 2.0
```
### Python one-off generation
```python
from pathlib import Path
from pipeline.plugins.noise import find_noise_tool, run_noise_tool
tool = find_noise_tool()
if tool is None:
raise RuntimeError("forge_noise_tool not found — build it first")
run_noise_tool(
Path("output.bmp"), "ridged",
width=512, height=512, seed=42,
frequency=4.0, octaves=6, tiling=True,
tool=tool,
)
```
### Web UI preview URL
```typescript
import { noisePreviewUrl } from "@/lib/api"
const url = noisePreviewUrl({
noise_type: "worley-edge",
width: 256, height: 256,
seed: 42, frequency: 8.0,
})
// -> "/api/generate/noise/preview?noise_type=worley-edge&..."
```
## Reference
- [Asset Lesson 21](../../../lessons/assets/21-noise-texture-generation/) —
full walkthrough
- [Math Lesson 13](../../../lessons/math/13-gradient-noise/) — Perlin and
Simplex theory
- [forge-shader-noise](../forge-shader-noise/SKILL.md) — GPU-side noise
(same algorithms in HLSL)
Auf GitHub ansehen