godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.14k stars 93 forks source link

Add more random number generation options #8884

Open tavurth opened 9 months ago

tavurth commented 9 months ago

Describe the project you are working on

Any project that has procedural generation.

Describe the problem or limitation you are having in your project

Please see the blogpost: When Random Numbers Are Too Random

Sometimes randomness is too random, it would be nice to be able to sample in a grid from 0..1 instead of sampling randomly in that number space.

Here's another blog post on the subject of placing vegetation

image

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Images borrowed from the blog link

Common random number generation:

image

Ideally more uniformly distributed random number generation:

image

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Perhaps RandomNumberGenerator could have a new function: randf_grid

I see that we already have randfn which is a good step in this direction.

If this enhancement will not be used often, can it be worked around with a few lines of script?

It could not, but perhaps it could be a module or addon

Is there a reason why this should be core and not an add-on in the asset library?

This would be really nice to have as part of the Godot RandomNumberGenerator module. There is C++ source code in the above blog post, although I'm not sure of the licensing thereof.

Calinou commented 9 months ago

Random sampling based on the placement of other existing random samples is expensive, and not always suited for real-time usage due to its performance cost.

You can already greatly improve the uniformity of randomness by dividing your area into grid cells and using rand_range() with a small range within this grid cell. This isn't as uniform as methods like Poisson disk sampling, but it essentially has no performance cost.

tavurth commented 9 months ago

You can already greatly improve the uniformity of randomness by dividing your area into grid cells and using rand_range() with a small range within this grid cell. This isn't as uniform as methods like Poisson disk sampling, but it essentially has no performance cost.

Yes I completely agree!

However, my thoughts on using something like this in a game jam or regular production game are:

  1. I don't have time
  2. I don't want to reinvent the wheel
  3. I don't mind if this takes time during level generation
  4. I would rather have this as standard in godot (than as a plugin)

I feel like these options could be available with a note about the performance hit.

Just relying on the built in randomness sometimes leads to less than the desired result.

This is especially true to new entrants to gamedev. It would be really nice to have the official tools pointing out that there are many ways to do random.

lostminds commented 9 months ago

I've worked with a similar issue in a graphics stippling generation tool and I think for a game engine like Godot a good option could be to provide some sort of pre-generated "blue noise" arrays with different densities. Basically fixed pre-generated arrays of points that are placed at random with similar distance between them. The engine could then provide access to these points using similar syntax as a rand_range(min, max) like blue_noise_point_range(min, max, density) and blue_noise_value_range(min, max, density) by then picking a suitable (approx) density array from the pregenerated set based on the density parameter, return a point scaled to fit the desired range and advance an index so the next request returns the next point in the array. I think this could be useful since these types of points are very useful for placing things, but difficult to generate.

Technically this would of course make it repeat the same patterns, but due to the "patternless" like nature of blue noise this will be difficult to see in any real use case, and randomize() could shuffle the array making the points appear in a different order, which will give different placement patterns as long as you don't use all points in an array. Another drawback is of course that the number of points are finite, so if you have an array to 100 points and get 101, the last one will loop around to the same as the first one. But this can be documented, and the solution is just that if you want 101 unique points you need to use a higher density (=more points) array. An option could be to have the interface be a request for an array of points instead like blue_noise_points_range(min, max, density, count) which could give an opportunity to warn if the number of requested points is too many for the density and use a higher density one.

A simpler but similar option that you can more easily implement yourself @tavurth is to make a distributed_points_in_area(point_min,point_max,count) that takes a two-step approach: First based on the area and count find a number of grid rows/columns to distribute roughly count points in a same spacing grid. Make an array of all the points in this grid. Then add a random offset +/- these array points, up to 2/5 (or something less than 1/2) of the grid spacing. Finally return the points, culling at random a couple to return the desired number of points. This will give you a nicely distributed "random" point set, but it won't be as nicely even as blue noise generated with more expensive methods.

tavurth commented 9 months ago

A simpler but similar option that you can more easily implement yourself @tavurth

I don't actually find this to be a current issue for myself, I just use random until I find the need to do it differently. Then I can easily throw together something specific for the required task.

However they aren't open source, and they are not community tested etc as they have pretty much always been built for clients.

I guess I am using this proposal as a personal note to add this to Godot later, and get community feedback on it, rather than a current need or difficulty I have implementing this myself. 😅

Would be awesome if Godot could actually provide this types of common game dev tooling out of the box.