Reputation: 577
I am trying to implement a simple noise function that takes two integers and return a random float based on the seed combined with the two parameters.
using std::mt19937
works great, but for some reason when I try to use srand
with rand()
, I get repeated numbers..
Note: Using c++11
seed
member function in a loop is really, really slow.
here are two terrains using both methods (with the same reseeding numbers):
c++11 random:
std::random_device rd;
std::mt19937 g{ rd() };
std::uniform_real_distribution<float> gen{ -1.0, 1.0 };
float getNoise(int x, int z) {
g.seed(x * 523234 + z * 128354 + seed);
return gen(g);
}
c random:
float getNoise(int x, int z) {
std::srand(x * 523234 + z * 128354 + seed);
return static_cast<float>(std::rand()) / RAND_MAX * 2.0f - 1.0f;
}
srand
work as expected?Thanks in advance.
EDIT: Ok sorry for not being clear, and, I know maybe I am wrong but let me try to explain again, I use reseeding because I use the same x and z coordinate's when iterating (not the same iteration).
If I remove the reseeding I will get this result:
Upvotes: 3
Views: 2456
Reputation: 10038
I am trying to implement a simple noise function that takes two integers and return a random float based on the seed combined with the two parameters.
Please don't say I shouldn't reseeding, I want to reseed on purpose.
You are purposely breaking it and asking us why it is broken, with the caveat that we aren't allowed to mention the gorilla in the room.
Don't reseed.
[edit]
Alright, as per comment request, here's an answer:
1) No, there is no faster way to reseed a PRNG, which you shouldn't be doing anyway. Properly, you should be seeding and then “warming up” the PRNG by discarding a few thousand values.
2) The reason rand()
(and, even though you don't believe it, any other PRNG you use) doesn't work is because your getNoise()
function is incorrect.
Your third image is correct. It is the result you should expect from simply returning a clamped random value.
You have attempted to modulate it by messing with the seed and, because of an apparent visual goodness in your first attempt, concluded that it is the correct method. However, what is really happening is you are simply crippling the PRNG and seeing the result of that. (It is more clear in the rand()
attempt because its seed more crudely defines the resulting sequence, which itself has a smaller period than the Mersenne Twister.)
(Attempting to modify it by skewing the (x,z) coordinate is also a red herring. It doesn't affect the actual randomness of the output.)
TL;DR
You're doing it wrong.
If you want to generate terrain maps, you should google around fractal terrain generation. In fact, here's a decent link for you: http://www.gameprogrammer.com/fractal.html
You will find that it takes a little more work to do it, but that the methods are very pleasingly simple and that you can very easily tweak them to modify your results.
Hope this helps.
Upvotes: 6
Reputation: 31457
I don't see why you'd want to continuously re-seed - that seems pointless and slow. But that's not what you are asking, so...
rand
produces very poor quality random numbers. Very low period and usually based on a linear congruential generator (not good). Also, the seed size is very small. Don't use it - <random>
exists for a reason.
The way you seed using srand
seems to depend very much on the x
and z
values you pass in and that you then multiply by large numbers which likely leeds to overflows and truncation when passing to srand
, meaning that (due to the limited number of possible seed values) you'll be reusing the same seed(s) often.
Some relevant links you may want to visit:
http://en.cppreference.com/w/cpp/numeric/random
https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful
Upvotes: 2
Reputation: 9090
The random number generator generates a sequence of random values from an initial seed, and is not meant to be used to generate single random values in function of a seed. So it should be initialized with g.seed(seed)
, and then be called in a fixed order for all (x, y) values, without reseeding each time. This will give random values efficiently, with the expected distribution.
For example:
std::random_device rd;
std::mt19937 g{ rd() };
std::uniform_real_distribution<float> gen{ -1.0, 1.0 };
constexpr std::size_t nx = 100;
constexpr std::size_t nz = 100;
float noise[nx][nz];
void generateNoise() {
g.seed(seed);
for(int x = 0; x < nx; ++x) for(int x = 0; x < nx; ++x)
noise[x][z] = gen(g);
return gen(g);
}
Upvotes: 3