Razaekel / noise-rs

Procedural noise generation library for Rust.
Apache License 2.0
859 stars 119 forks source link

Proposed new API #31

Closed Cifram closed 7 years ago

Cifram commented 9 years ago

Based on a discussion on IRC #rust-gamedev, I want to propose a new API, as so:

struct Seed { ... }
Seed {
    fn new(seed: u32) -> Seed;
}

fn perlin2d(seed: Seed, point: (f32, f32)) -> f32;
fn perlin3d(seed: Seed, point: (f32, f32, f32)) -> f32;
fn perlin3d(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn open_simplex2d(seed: Seed, point: (f32, f32)) -> f32;
fn open_simplex3d(seed: Seed, point: (f32, f32, f32)) -> f32;
fn open_simplex4d(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn worley2d_points(seed: Seed, point: (f32, f32)) -> [(f32, f32), ..9];
fn worley3d_points(seed: Seed, point: (f32, f32, f32)) -> [(f32, f32, f32), ..27];
fn worley4d_points(seed: Seed, point: (f32, f32, f32, f32)) -> [(f32, f32, f32, f32), ..81];

fn worley2d_nearest_point(seed: Seed, point: (f32, f32)) -> f32;
fn worley3d_nearest_point(seed: Seed, point: (f32, f32, f32)) -> f32;
fn worley4d_nearest_point(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn worley2d_nearest_edge(seed: Seed, point: (f32, f32)) -> f32;
fn worley3d_nearest_edge(seed: Seed, point: (f32, f32, f32)) -> f32;
fn worley4d_nearest_edge(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn worley2d_manhattan_point(seed: Seed, point: (f32, f32)) -> f32;
fn worley3d_manhattan_point(seed: Seed, point: (f32, f32, f32)) -> f32;
fn worley4d_manhattan_point(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn worley2d_manhattan_edge(seed: Seed, point: (f32, f32)) -> f32;
fn worley3d_manhattan_edge(seed: Seed, point: (f32, f32, f32)) -> f32;
fn worley4d_manhattan_edge(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn brownian2d<F>(seed: Seed, point: (f32, f32), noise_func: F, wavelength: f32, octaves: u32) -> f32
    where F: Fn(Seed, (f32, f32)) -> f32;
fn brownian3d<F>(seed: Seed, point: (f32, f32, f32), noise_func: F, wavelength: f32, octaves: u32) -> f32
    where F: Fn(Seed, (f32, f32, f32)) -> f32;
fn brownian4d<F>(seed: Seed, point: (f32, f32, f32, f32), noise_func: F, wavelength: f32, octaves: u32) -> f32
    where F: Fn(Seed, (f32, f32, f32, f32)) -> f32;

This API is simple and composable. To explain the different pieces of it:

The Seed struct just contains a shuffled array of bytes, useful for generating consistent, location-based random numbers.

Perlin noise is one of the most standard noise equations around.

Simplex noise is an improved version of Perlin noise, and also very standard. However, it was pointed out to me by bjz that Simplex noise is under patent, but OpenSimplex is almost as good.

Worley noise, also called Cell noise or Voronoi noise, is unusual. It involves dividing space into a set of cells by placing a point randomly in each hypercubic region of space, and using proximity to that point, and the points in all neighboring hypercubic regions, to determine which cell the current point occupies. There are a lot of ways of calculating this proximity, which produce a variety of different effects. To support any kind of Worley noise the user may want, the basic Worley noise function just returns the complete set of points from this and all neighboring regions. Then we have an additional set of functions built on top that for the most common types of Worley noise:

The brownian functions do fractal brownian motion on whatever noise function you hand it, with the specified wavelength (the size of the first iteration) and octaves (the number of iterations). Each iteration has half the wavelength and half the amplitude of the previous iteration.

brendanzab commented 9 years ago

I like this. Any reason why you are using functions, not methods on Seed?

mpowell-imvu commented 9 years ago

Because it hadn't occurred to me. :-)

My first draft didn't include Seed. Then I realized I needed a seed structure, so added it to all the functions. So I didn't think of turning them into member functions.

That said, I think making them into member functions would make them less composable. If I want to call brownian2d on perlin noise now, this is how I'd do it:

let noise = brownian2d(seed, point, perlin2d, 16, 3);

But if we made these member functions of Seed, it would have to be:

let noise = seed.brownian2d(point, |seed: Seed, point: (f32, f32)| seed.perlin2d(point), 16, 3);

So I think I'd argue to keep them as free functions. That said, Seed should really be passed by borrow. I'll go through and fix that.

Also, I left of lacunarity and persistence in the brownian motion, even though your current implementation includes them. They should probably be added back in. However, I also feel they should have defaults of 2.0 and 0.5, as that's what you want like 99% of the time.

mpowell-imvu commented 9 years ago

Oh, apparently I can't edit the issue once it's been responded to. Oh well. The proposed changes are still captured in my comment.

Also thinking about a way to better generalize the worley noise into two parts. One generates a set of nearby cell points, and the other analyzes those points to produce a value. Sort of like what's already in this proposal, except with the ability to swap out for a different point generation algorithm.

I don't think I'd include any other point generation algorithms in the noise-rs library, for now... But I could see consumers of this library wanting to implement some alternate algorithms.

brendanzab commented 9 years ago

Regarding the composability concerns, note that UFCS you could do:

seed.brownian2d((x, y), Seed::perlin2d, ..)

Have you seen http://libnoise.sourceforge.net/ by the way? Here are some Haskell libraries based on it:

Might be abit complex though, I like the simplicity of your approach.

Cifram commented 9 years ago

I had not seen this... I'll dig into it's structure a bit, and see if there's anything that seems worth pulling out.

Razaekel commented 7 years ago

@Cifram do you feel a need to expand on this any further?

Cifram commented 7 years ago

No, not really... This post was made before I started contributing to this library. This API was pretty much implemented, and refined, before my attention turned elsewhere. I haven't been actively involved in any development here for close to 2 years.

Razaekel commented 7 years ago

Thanks for the followup.