2D procedural veins/cumps of weighted ores/rocks

I'm evaluating methods for generating "clumps" or "veins" of rocks in a 2D top-down procedural game. Obviously the most common suggestion is to use various types of noise, but I feel like noise (alone) won't achieve what I'm after.

I've used a combination of voronoi and perlin noise because it's easier to generate "regions" of different noise. I can more easily turn these areas into "veins" of rocks or something than I could be using a "heightmap" alone - heightmaps tend to be more swirly/cloud-like.

With a high threshold and some rounding, I can turn this into a mask that works well for veins:

Changing the spacing and scale is as simple as toying with the math/threshold.

The difficulty lies with choosing which "rock" (or "ore") will fill each vein. Primarily, this is thanks to the fact that my rocks are weighted. Veins of rare rocks should not be common.

I currently map the original voronoi cell value to a specific rock, which forces each vein to be a single type, but can't be weighted. As you can see in picture #1, there's a fairly even distribution of each shaded area.

So either, I need a new way to choose a weighted random rock type for each vein, or I need a new way to place veins.

Minecraft is a good well-known example of what I need, except I work in 2D:

• Regularly distributed ore veins
• Veins of common ores are found more often and are typically larger veins
• Veins of rare ores are harder to find, and are smaller in size

Noise alone just can't seem to produce these values. What else can I try?

Once you've identified a particular voronoi cell that should be a vein, take the coordinates of its generating point and pump them into a hash function. Interpret the result as a number between 0 and 1, and use it to look up into a probability table of your ore types.

This way you can control the probability of each ore type completely independently from the number, size, & distribution of your veins.

You can even pick out these ore types early in the process - when you've seeded your points but not yet generated the voronoi regions from them - and use the ore type to decide on a distance scaler to apply when comparing points against this seed. That way you can make some ores naturally occur in fatter or smaller veins, or even make some ores more narrow and wispy if you use a non-isotropic scale.

• Could you perhaps provide your answer with an example? Code or a link to a more detailed explanation? – Derk Jan Speelman Mar 21 '18 at 21:25
• What bit would you like more detail about, @DerkJanSpeelman? – DMGregory Mar 21 '18 at 23:10
• you say what can be done, but you don't provide your answer with an example. – Derk Jan Speelman Mar 23 '18 at 11:41
• And I'll repeat: what part would you like an example to illustrate? The more clearly you can state your question, generally, the clearer and more useful the answers you'll get. – DMGregory Mar 23 '18 at 16:27

What if you try this:

• Adjust your threshold and rounding logic to increase the number of "levels" or "colors" in your output noise-image
• Instead of one level/color representing one type of rock, assign multiple levels to one type of rock

If you control the "amount" of levels going to each type of rock, does it help give the control you need?

Seeing other answers I'll suggest a little different approach. This will work better if you want have an ability to make the veins (material in them) more (or arbitrarily) dense.

1. Determine an origin of a vein node (by hash of a position being above a threshold for example, just like you generate random numbers but it has have hash semantics, you can vary generation density here). If you have a chunk based generation process then you can sample points uniformly in a chunk (number of points using for example a poisson distribution).
2. Generate a pseudo-random polygon and rasterize it (for small veins you can do a brute with an algorithm to check polygon-point intersection). Before generation you can choose the vein type and parameters for it's appearence.

2.1. When rasterizing you can vary density with noise/hashing function for example.

Note that this method may generate overlapping veins, which may not be desired.

The way you generate a polygon is first you determine number of vertices, and radius (you can do it however you want). Then put the vertices on the circle boundary (I found best results with uniformly distributed points, constant spacings work well too). Then for each point sample a corresponding noise value in range <-1, 1> (it doesn't have to be strictly that place, the only requirement is that the sampled points create a closed curve) and displace them perpendicularly to the circle boundary by a contiuous function (of your choice) of that sampled value. You are free to choose whatever function you want (you have to choose it empirically. Linear scaling will gives ok results), as long as it is continuous.

If you want to get fancier you can distribute the points in a different way, and change a displacing function to be aware of the angle of the point being displaced (so you could hold circle origin for example) to achieve other, not necesserly round shapes. For example lines of ore.

Even if you have a world that can dynamically expand you can use this method. Say your max vein size is R. Then you have to check if in radius R (outside of the part being generated) there is a vein generated and if there is then generate it for the part of the world that is actually there (generation is based on noise/hash so you get persistent results for duplicated generation, therefore you don't have to worry about it). If your world is chunk based then while generating a chunk you can just generate veins from neighbouring chunks (but save only region you're generating, the rest is discarded).