razaekel / noise-rs Goto Github PK
View Code? Open in Web Editor NEWProcedural noise generation library for Rust.
License: Apache License 2.0
Procedural noise generation library for Rust.
License: Apache License 2.0
Need to Implement the following transformer modules:
I'll be happy to implement this if it's wanted.
Not certain exactly how to implement this, but it'd be really nice if the fractal modules were combinators instead of generators, so that you could make Open Simplex fBm and so on.
In commit #92 (41c0df5) @amaranth added a TODO to permutationtable.rs (was seed.rs). It looks like this TODO was partially completed by @arturoc in commit #94 but never actually finished.
The intent of the code is to extract the lowest byte from the input value and use that as the index into the table. This can be done with just the NumCast trait, so I don't see the purpose of the Signed and PrimInt traits.
@amaranth, would you be able to explain your thinking at the time?
This code compiles:
let no = noise::Brownian2::new(noise::perlin2, 4).wavelength(32.0);
This doesn't.
let no = noise::Brownian2::new(noise::perlin2, 4);
Here's the error:
lib/lib.rs:86:14: 86:35 error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
lib/lib.rs:86 let no = noise::Brownian2::new(noise::perlin2, 4);
^~~~~~~~~~~~~~~~~~~~~
lib/lib.rs:86:14: 86:35 help: run `rustc --explain E0282` to see a detailed explanation
error: aborting due to previous error
Being a beginner at Rust I have no idea how to solve the problem other than calling the .wavelength
function or other...
All of the gradient noise functions have begun producing patterns. To see the patterns, increase the image size in the examples to 1024 and reduce the wavelength to 16. From bisecting I was able to determine that this problem began with this commit: ce8e06f
Currently, OpenSimplex exists as a free function. It should be converted to a Noise module so that it can be composed with the rest of the modules.
// brownian.rs
#[cfg(test)]
mod tests {
use super::*;
fn test_brownian2_closure() {
let _ = Brownian2::new(|s, p| 23f32, 8);
}
}
src/brownian.rs:269:32: 269:44 error: can't infer the "kind" of the closure, explicitly annotate it. e.g. `|&:| {}`
src/brownian.rs:269 let _ = Brownian2::new(|s, p| 23f32, 8);
^~~~~~~~~~~~
src/brownian.rs:269:17: 269:31 error: the trait `for<'r,'r> core::ops::Fn(&'r seed::Seed, &'r [_; 2]) -> _` is not implemented for the type `closure[src/brownian.rs:269:32: 269:44]`
src/brownian.rs:269 let _ = Brownian2::new(|s, p| 23f32, 8);
// brownian.rs
#[cfg(test)]
mod tests {
use super::*;
fn test_brownian2_closure() {
let _ = Brownian2::new(|&: s, p| 23f32, 8);
}
}
src/brownian.rs:269:17: 269:31 error: type mismatch: the type `closure[src/brownian.rs:269:32: 269:47]` implements the trait `core::ops::Fn(_, _) -> f32`, but the trait `for<'r,'r> core::ops::Fn(&'r seed::Seed, &'r [_; 2]) -> _` is required (expected concrete lifetime, found bound lifetime parameter )
src/brownian.rs:269 let _ = Brownian2::new(|&: s, p| 23f32, 8);
^~~~~~~~~~~~~~
src/brownian.rs:269:17: 269:31 note: required by `brownian::Brownian2<T, F>::new`
src/brownian.rs:269 let _ = Brownian2::new(|&: s, p| 23f32, 8);
^~~~~~~~~~~~~~
There is little documentation in the library...
Composable noise modules as initially proposed by @Aatch in #100.
This is the tracking issue for the implementation of composable noise modules and examples of their use:
I propose adding a new, higher-level API for constructing complex noise functions built from simpler parts. There are three parts to this: mutators that alter either the input value or the output value, composition functions that allow multiple value sources to be combined in interesting ways and "utility" value generators.
Mutators are probably the simplest part. They wrap a noise source and can choose to alter either the input point, the generated value or both. This means mutators can be used for a variety of tasks, from rotating a value source to scaling output values.
Composition functions can be used combine value sources. The general pattern here is that they are constructed with multiple value sources and can combine them in interesting ways. For example, blending between two value sources dependent on a third. This could be used for biome generation where one layer determines the biome and different biomes have different terrain generation functions.
These are special value sources that produce some specific result. The simplest one is a constant-value generator that always returns the same value no matter what. More interesting is checkerboard, striped or similar patterns. The main reason for having these is to supply values to be manipulated using noise in some way.
The general design I envision is something similar to Iterator
. Having a trait for each dimension that just takes a point and returns a value. This allows for users to do whatever they want. The library would then provide some implementers of the trait that do some common tasks like mapping inputs, mapping outputs and the utility generators mentioned.
This is not intended to replace the existing API, just build upon it.
Value noise needs to be ported to a module, just like perlin or open simplex was.
OpenSimplex 4D isn't implemented yet.
Currently trying to port it from the original Java code, minus the cruft and if-statement hell.
Update
3/5ths of the way through; I think I'm starting to see in four dimensions.
Update 2
80% done. I feel bad for the person who's gonna "optimize" this.
In the future I see that these structs could simply implement Fn(Input) -> Output, but we need to wait for these to be stabilized first.
I also think that we need an impl for Fn.
From #121.
I'd like to see a representative example first to give some guidance on implementation.
The perlin example produces a image in the first render, then overwrites it with the second render because the name is the same. The second example should be renamed so it doesn't overwrite the first example.
A lot of the code in each multifractal NoiseModule is common across PointN. Should extract this out in order to follow DRY.
The MIT license requires reproducing countless copies of the same copyright
header with different names in the copyright field, for every MIT library in
use. The Apache license does not have this drawback, and has protections from
patent trolls and an explicit contribution licensing clause. However, the
Apache license is incompatible with GPLv2. This is why Rust is dual-licensed as
MIT/Apache (the "primary" license being Apache, MIT only for GPLv2 compat), and
doing so would be wise for this project. This also makes this crate suitable
for inclusion in the Rust standard distribution and other project using dual
MIT/Apache.
To do this, get explicit approval from each contributor of copyrightable work
(as not all contributions qualify for copyright) and then add the following to
your README:
## License
Licensed under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you shall be dual licensed as above, without any
additional terms or conditions.
and in your license headers, use the following boilerplate (based on that used in Rust):
// Copyright (c) 2015 t developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
And don't forget to update the license
metadata in your Cargo.toml
!
The name Seed for this module had been bugging me for a while, and I couldn't put my finger on why, until I sat down, read through earlier commits for it, and realized that it really just contains the permutation tables needed for gradient noise generators like Perlin or OpenSimplex.
I think this module should be renamed to either PermTable or PermutationTable, to be more explicit about what it is. It would still take a seed value for input, but it's not really a seed itself. it just uses the inputted seed value to randomized the permutation table that it contains. Additionally, I think that the perlin and opensimplex noise generators should be changed to take a uint value for their seed, and internally use it to get the permutation table, instead of providing the table itself to those generators. Essentially, Seed/PermTable should be a internal implementation detail, not something that's exposed as part of the API.
I think it would be very useful to have a function or an example of how to use open_simplex to generate a tileable image. Possibly tileable on the horizontal, vertical or both sides
Would be nice to have Value noise as a "poor man's coherent noise" function. It actually looks pretty decent on multi-octave fBm noise and should work when faster computation time is necessary.
Edit: currently implementing this.
Hi there!
I notice that PermutationTable is going to become private so I shouldn't use it anymore, but it doesn't mention what I should do instead. E.g. is the PermutationTable
now automatically cached in, e.g., Fbm
, or is there some other thing I should persist for efficiency if I want to sample many points of noise?
Thanks!
Need both seamless and non-seamless texture maps
Need a point/value cache module to temporarily store the result of modules earlier in the chain to reduce the amount of repetitive computation.
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.
open_simplex2
shows some triangular artifacts, see the following image:
The noise function is not smooth at the edges of the open simplex triangle grid.
The shown surface is a regular grid, its height is given by a function call similar to
noise::open_simplex2(&seed, coord) * 0.5
Maybe there are some rounding issues at the triangle borders? These artifacts do not occur when using perlin2
. The artifacts remain even when I switched to other randomly generated seeds, so this should be reproducible.
Implement a Blend selection module for blending two modules together based on the value from a third module.
Does anyone know how to compute the analytic derivative of SuperSimplex noise? The algorithm is so poorly documented I couldn’t find any information anywhere.
How do I change the aplitude of a BrownianX noise generator. I am running a Brownian2 with a open_simplex2 noise generator. After generating 65,536 values, I found these statistics:
min: -1.0851247
avg: -0.005162108
max: 0.96951514
Am I to assume the aplitude here was 1? Is the fact that the min is slightly below -1 just a rounding error? It it always 1?
The Seed type does not implement the Clone/Copy/Debug traits, thereby preventing any structs that include Seed from having those traits as well.
src/brownian.rs:201:51: 201:52 error: associated type bindings are not allowed here [E0229]
src/brownian.rs:201 impl<'a, 'b, T, F> Fn(&'a Seed, &'b Point2<T>) -> T for Brownian2<T, F> where
^
src/brownian.rs:221:51: 221:52 error: associated type bindings are not allowed here [E0229]
src/brownian.rs:221 impl<'a, 'b, T, F> Fn(&'a Seed, &'b Point3<T>) -> T for Brownian3<T, F> where
^
src/brownian.rs:242:53: 242:54 error: associated type bindings are not allowed here [E0229]
src/brownian.rs:242 impl<'a, 'b, T, F> Fn(&'a Seed, &'b ::Point4<T>) -> T for Brownian4<T, F> where
No blog post as of yet, but it's from the same author as Open Simplex noise. It looks fairly simple to implement, and it's always nice to have more noise functions.
As per https://www.reddit.com/r/proceduralgeneration/comments/68kpwt/most_important_algorithms_to_know/dhm8h81/?context=3 it has better results in higher dimensions.
Functions such as perlin5
would be very welcome.
Preliminary list of examples:
I propose to use f64
everywhere instead of the generic Float
type. This would provide one immediate benefit in that all results could be expected to be the same, due to the required use of f64
, instead of the possible use of either f32
or f64
, which could result in differences in the result due to floating point errors.
This would also simplify the API in that there would be no requirement to cast the result of the get()
fn to f32/f64
, and eliminate the need for the associated Output
type, as the output for get()
could now be f64
everywhere.
Voronoi noise is a subset of Worley noise, so maybe implement Voronoi for now, and mark Worley for later implementation.
So, this a two part issue (but the second part is just an awareness issue).
I ported the perlin2 code to Java, using 64-bit floating point (doubles), and did plots of the raw output. I noticed three interesting aspects of the data (third aspect is second part of the issue).
My observations are best explained by a few histograms:
http://imgur.com/a/Izngg (note, last image is random gaussian values as a reference for histogram behavior)
The line of issue: https://github.com/bjz/noise-rs/blob/d46df3fd9e37000a45fa76aad3fcdb85f4967318/src/perlin.rs#L43
The two constants there should be removed; the data's expected value (peak) at 0 without adding a constant, and the limits are at -1 and 1 without multiplying. After removing these two operations, the data begins to behave as expected. I tested with a data set of 613416960 (0xC00 width * 0xC00 height * 0x40 seeds), and my range was (-0.9999978883709002, 0.9999978883709002). I can provide some of the resulting histograms, but I'd just like to know that they're needed before I upload them.
Third thing I noticed, the data has mysterious small-scale biases (where there is a dramatically higher incidence of values on the other side of a threshold). They're noticeable as cliffs in the images. I don't have any suggestion for addressing it, or insight to how expected they are. I just thought that it should be noted by a maintainer as a possible issue.
I'm opening this as an issue instead of a PR because I suspect that perlin3 and perlin4 should receive similar analysis. If I have time, I can attempt to analyse those as well (or make my code available for another's use).
it's rights now 2 months out of date D:
Would it be possible to document the expected output ranges of the different noise functions?
worley-quadratic_2d
worley-quadratic_3d
worley-quadratic_4d
worley-quadratic-range_2d
worley-quadratic-range_3d
worley-quadratic-range_4d
Unless I'm mistaken, that looks very different from what it should.
For comparison, here are the linear equivalents to those images.
worley-linear_2d
worley-linear_3d
worley-linear_4d
worley-linear-range_2d
worley-linear-range_3d
worley-linear-range_4d
The ones that are particularly bad are worley-linear-range, worley-manhattan-range, and worley-chebyshev-range. The ones that fall within [-1,1] but don't hit it exactly (such as worley-linear-squared_4d) are probably just inadequate sampling sizes. worley-quadratic-range has its own issues, but that should wait to be resolved until after #175 is resolved.
Generating 1048576 points for worley-linear_2d.png
Processing 1048576 of 1048576
Finished generating worley-linear_2d.png
MaxValue: 1
MinValue: -1
Generating 1048576 points for worley-linear_3d.png
Processing 1048576 of 1048576
Finished generating worley-linear_3d.png
MaxValue: 1
MinValue: -1
Generating 1048576 points for worley-linear_4d.png
Processing 1048576 of 1048576
Finished generating worley-linear_4d.png
MaxValue: 1
MinValue: -0.9921568627450981
Generating 1048576 points for worley-linear-range_2d.png
Processing 1048576 of 1048576
Finished generating worley-linear-range_2d.png
MaxValue: 1.0087909383089562
MinValue: -1
Generating 1048576 points for worley-linear-range_3d.png
Processing 1048576 of 1048576
Finished generating worley-linear-range_3d.png
MaxValue: 0.9667198923830767
MinValue: -1
Generating 1048576 points for worley-linear-range_4d.png
Processing 1048576 of 1048576
Finished generating worley-linear-range_4d.png
MaxValue: 0.88204510902794
MinValue: -1
Generating 1048576 points for worley-linear-squared_2d.png
Processing 1048576 of 1048576
Finished generating worley-linear-squared_2d.png
MaxValue: 1
MinValue: -1
Generating 1048576 points for worley-linear-squared_3d.png
Processing 1048576 of 1048576
Finished generating worley-linear-squared_3d.png
MaxValue: 1
MinValue: -1
Generating 1048576 points for worley-linear-squared_4d.png
Processing 1048576 of 1048576
Finished generating worley-linear-squared_4d.png
MaxValue: 1
MinValue: -0.9921568627450981
Generating 1048576 points for worley-manhattan_2d.png
Processing 1048576 of 1048576
Finished generating worley-manhattan_2d.png
MaxValue: 1
MinValue: -1
Generating 1048576 points for worley-manhattan_3d.png
Processing 1048576 of 1048576
Finished generating worley-manhattan_3d.png
MaxValue: 1
MinValue: -1
Generating 1048576 points for worley-manhattan_4d.png
Processing 1048576 of 1048576
Finished generating worley-manhattan_4d.png
MaxValue: 1
MinValue: -0.9921568627450981
Generating 1048576 points for worley-manhattan-range_2d.png
Processing 1048576 of 1048576
Finished generating worley-manhattan-range_2d.png
MaxValue: 1.7180156390269357
MinValue: -1
Generating 1048576 points for worley-manhattan-range_3d.png
Processing 1048576 of 1048576
Finished generating worley-manhattan-range_3d.png
MaxValue: 2.000000000000001
MinValue: -1
Generating 1048576 points for worley-manhattan-range_4d.png
Processing 1048576 of 1048576
Finished generating worley-manhattan-range_4d.png
MaxValue: 2.102307488958573
MinValue: -1
Generating 1048576 points for worley-chebyshev_2d.png
Processing 1048576 of 1048576
Finished generating worley-chebyshev_2d.png
MaxValue: 1
MinValue: -1
Generating 1048576 points for worley-chebyshev_3d.png
Processing 1048576 of 1048576
Finished generating worley-chebyshev_3d.png
MaxValue: 1
MinValue: -1
Generating 1048576 points for worley-chebyshev_4d.png
Processing 1048576 of 1048576
Finished generating worley-chebyshev_4d.png
MaxValue: 1
MinValue: -0.9921568627450981
Generating 1048576 points for worley-chebyshev-range_2d.png
Processing 1048576 of 1048576
Finished generating worley-chebyshev-range_2d.png
MaxValue: 0.7187096774193531
MinValue: -1
Generating 1048576 points for worley-chebyshev-range_3d.png
Processing 1048576 of 1048576
Finished generating worley-chebyshev-range_3d.png
MaxValue: 0.7071067811865479
MinValue: -1
Generating 1048576 points for worley-chebyshev-range_4d.png
Processing 1048576 of 1048576
Finished generating worley-chebyshev-range_4d.png
MaxValue: 0.5773502691900001
MinValue: -1
Generating 1048576 points for worley-quadratic_2d.png
Processing 1048576 of 1048576
Finished generating worley-quadratic_2d.png
MaxValue: 1
MinValue: -1
Generating 1048576 points for worley-quadratic_3d.png
Processing 1048576 of 1048576
Finished generating worley-quadratic_3d.png
MaxValue: 1
MinValue: -1
Generating 1048576 points for worley-quadratic_4d.png
Processing 1048576 of 1048576
Finished generating worley-quadratic_4d.png
MaxValue: 1
MinValue: -1
Generating 1048576 points for worley-quadratic-range_2d.png
Processing 1048576 of 1048576
Finished generating worley-quadratic-range_2d.png
MaxValue: 0.979408116545269
MinValue: -1.0000000000000004
Generating 1048576 points for worley-quadratic-range_3d.png
Processing 1048576 of 1048576
Finished generating worley-quadratic-range_3d.png
MaxValue: 0.6510040816326557
MinValue: -1.0000000000000009
Generating 1048576 points for worley-quadratic-range_4d.png
Processing 1048576 of 1048576
Finished generating worley-quadratic-range_4d.png
MaxValue: 0.6417640646075435
MinValue: -1.0000000000000002
As mentioned in #102 the output range of OpenSimplex needs to be fixed.
for example:
Ridgedmulti:
pub fn lacunarity(&self) -> f64 {
self.lacunarity
}
pub fn random_seed(&mut self) {
let mut rng = std::rand::weak_rng();
self.seed = rng.gen();
}
Perlin:
#[inline]
pub fn lacunarity(self, lacunarity: f64) -> Perlin {
Perlin { lacunarity: lacunarity, ..self }
}
/// Randomize the seed
#[inline]
pub fn random_seed(self) -> Perlin {
self.seed(rand::weak_rng().gen())
}
Interface is not unity. Ridgedmulti must mut, I think Perlin's way is better.
I'm not sure what I'm doing wrong but I always seem to get the same value for perlin noise.
extern crate noise;
fn main() {
let seed = noise::Seed::new(5);
for x in 0..10{
let noise = noise::perlin2(&seed, &[x as f32, 0.0]);
println!("{:?}: {}", x, noise);
}
}
gives:
0: 0.056165796
1: 0.056165796
2: 0.056165796
3: 0.056165796
4: 0.056165796
5: 0.056165796
6: 0.056165796
7: 0.056165796
8: 0.056165796
9: 0.056165796
Changing let seed = noise::Seed::new(5);
to let seed = noise::Seed::new(6);
gives the exact same output. What am I doing wrong?
Currently, seed can be modified in Seedable
generators without actually causing an update of the PermutationTable
in the module.
seed
should be made private, and Seedable
should have a seed(&self)
getter to retrieve the seed from the module.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.