Open Ralith opened 6 years ago
The purpose of having seeds is to provide reproducible results by using the same seed while giving users the ability to specify the seed as part of their generation process. Having a hard-coded seed would defeat the purpose of this library.
Sorry, I must have been unclear. The library already contains many hard-coded seeds. This proposal contains two parts: improving the hard-coded seeds, and adjusting, not removing, the interface by which the user controls dynamic seeding. As outlined in the patch above, instead of supplying a u32
, the user would provide a &mut T
where T: Rng
which is then used to initialize the noise function's internal state as appropriate. The user can seed an RNG in whatever manner suits them (from a constant, from storage, from entropy, ...).
Some discussion with Ralith indicated that I may have been misinterpreting the proposal, so I'm re-opening it for further discussion and expanding the details.
Seeds in noise-rs are used to initialize internal RNGs, which are in turn used to construct noise function internal states (currently just permutation tables, maybe other things in the future?). The heart of this proposal is to instead allow the user to pass in a reference to a user-constructed RNG and use that directly, via the standard rand
API. To construct a non-default instance of a noise function, a user would first construct some RNG, then call gen
on it. For example:
let mut rng = XorShiftRng::from_seed(seed);
let noise = rng.gen::<SuperSimplex>();
noise.get(...);
This has a number of benefits:
XorShiftRng
is designed to take 128-bit seeds. This proposal allows the user to use whatever RNG suits them, and seed it in its preferred manner. No future API changes would be necessary to support new RNGs with different seeding requirements.
While quality of randomness used may not currently be the dominant factor in the quality of noise functions, the existing interface nonetheless imposes an unnecessary and easily-corrected bottleneck.gen
. Users who want irreproducible results can use rand::thread_rng()
. Users who don't care about any of this can still call Default::default()
for high-quality deterministic output.The proposed interface is not mutually exclusive with the existing Seedable
trait. If desired, it could be retained, although the seed
getter could not return meaningful results for functions generated via the proposed method. For the above reasons I think it should at least be marked deprecated.
What about a combined approach?
I was thinking of providing some default method such as Seedable::new_seed()
that generates a high quality seed using the OsRng
. This would be called by default when constructing the structs, but could be modified afterwards.
The noise generators like noise::Perlin
already have private fields, so this should not be a breaking change, but it should significantly increase the quality of generators since they won't have a fixed default seed all the time.
I still support @Ralith with this one though, I think adding RNGs would be a good idea and I would personally like it if it were implemented.
32-bit seeds aren't great, and pervasive use of mostly-0 seeds may be causing odd results already. I propose that
impl Seedable for T
be replaced withimpl Distribution<T> for Standard
per rand 0.5 convention, withDefault
impls andnew
using anXorShiftRng
with a hardcoded seed for reproducibility and convenience.This would save users and implementers the effort of having to come up with a good seed (particularly when unpredictable results are desired) and improves the quality of randomness used at no cost. I'm interested in implementing this if people are on board with the idea. The changes involved might look like this: