WebGPU + Wasm + Rust > building mmo-ready procedural trees using Ambient engine (part 3/3)

Emmanuel BOTROS YOUSSEF
5 min readJun 7, 2023

--

randomness is everywhere in nature

Welcome to the third part of our series on building MMO-ready procedural trees using WebGPU, WebAssembly (Wasm), and Rust with the Ambient engine. in part 1 we introduced context and motivation, in part 2 we explained the generative system that handles tree creation, with a focus on the branching strategy. In this article, we will try to understand how randomness relates to our goal, achieving efficient entire eco systems generation !

Randomness in Nature and the Goal of Realistic Procedural Trees

Randomness is a fundamental aspect of nature and plays a crucial role in creating the diverse and intricate patterns found in the natural world. From the branching structures of trees to the distribution of leaves on a plant, random processes are responsible for the complexity and visual richness we observe.

In the context of procedural tree generation, our goal is to recreate the organic and natural appearance of trees using computational methods. By incorporating controlled randomness through pseudorandom number generation, we can mimic the stochastic patterns found in nature. This allows us to generate visually appealing and realistic trees with variation and complexity that closely resemble their natural counterparts.

Introduction to Pseudorandom Number Generators (PRNGs) and the ChaCha8Rng Crate

Procedural generation often relies on the generation of random numbers to create diverse and unique content. Pseudorandom Number Generators (PRNGs) are algorithms that produce a sequence of numbers that appear random but are actually deterministic. These algorithms take an initial value, called a seed, and use it to generate a sequence of numbers.

In our implementation, we utilize the ChaCha8Rng crate from the rand_chacha library. This crate provides a secure and efficient PRNG based on the ChaCha20 stream cipher. With ChaCha8Rng, we can seed the PRNG with a given value and generate random numbers within a specified range.

Enhancing Tree Generation with Pseudorandomness

Using a seeded PRNG opens up exciting possibilities for enhancing tree generation. By utilizing the gen_rn method, which generates a pseudorandom number between a given minimum and maximum value, we can introduce controlled randomness into various aspects of the tree creation process. This controlled randomness ensures that each generated tree is unique while maintaining consistency when using the same seed.

1. Branch Chance Variation

let branch_chance = tooling::gen_rn(tree.seed + i as i32, 0.0, 1.0) * i as f32;
if branch_chance > 0.2 {
// Generate branch based on chance
// ...
}

In this code snippet, the gen_rn method is used to determine the chance of a branch being created at a particular trunk segment. By adding the segment index (i) to the seed, we ensure that each segment has a different chance value. The resulting pseudorandom number is multiplied by the segment index to introduce variation. Adjusting the minimum and maximum values in gen_rn allows fine-tuning of the probability range for branch creation.

This controlled randomness adds diversity to the tree structure, ensuring that some segments generate branches while others do not.

2. Branch Direction

let branch_direction = vec3(
tooling::gen_rn(tree.seed + i as i32 + 1, -1.0, 1.0),
tooling::gen_rn(tree.seed + i as i32 + 2, -1.0, 1.0),
tooling::gen_rn(tree.seed + i as i32 + 3, 0.0, 1.0),
).normalize();

Here, gen_rn is used to generate pseudorandom values for the x, y, and z components of the branch direction vector. By adjusting the minimum and maximum values, we control the range of variation in each component. Normalizing the resulting vector ensures that the branch direction remains consistent regardless of the magnitude of the generated values.

This controlled randomness introduces subtle variations in branch directions, creating visually appealing and natural-looking tree structures.

3. Trunk Slope Calculation

let trunk_slope = calculate_trunk_slope(tree, branch_position);

Within the calculate_trunk_slope function, pseudorandomness can be utilized indirectly. The function incorporates the tree seed, trunk height, and radius to determine the normalized slope of the trunk at a given position. By incorporating the seed into the calculation, we introduce a level of variation to the slope. As a result, trees with the same parameters but different seeds exhibit unique trunk shapes and curvature.

This controlled randomness allows for the creation of diverse and visually interesting tree trunks within a given set of parameters.

4. Random Branch Rotation

let branch_rotation = calculate_branch_rotation(trunk_slope);

The calculate_branch_rotation function leverages the pseudorandomness of the trunk slope to calculate the rotation angle for the branch. By using the trunk_slope as an input, we ensure that each branch has a unique rotation angle based on its corresponding trunk segment. This introduces subtle variations in the orientation of branches, adding visual complexity and realism to the generated trees.

Generating an Entire Forest of Unique Trees

One of the key benefits of utilizing seeded randomness and procedural generation is the ability to generate an entire forest of unique trees with just a single seed and a limited set of parameters. Let’s compare this approach to the alternative of using pre-created 3D models for each tree in the forest.

Imagine a scenario where we want to create a forest consisting of 1000 unique trees. Without seeded randomness and procedural generation, the conventional approach would involve creating 1000 different 3D models, storing them, and then loading them into the game engine. This process would require significant storage space, network bandwidth, and computational resources.

On the other hand, using seeded randomness and procedural generation, we can achieve the same visual result by simply sending a single seed value and a set of parameters to generate each tree. The compactness of the data needed to represent the forest is significantly reduced compared to the alternative approach. Additionally, the generation of trees on-the-fly eliminates the need for storing and loading large numbers of 3D models, resulting in substantial resource savings.

By leveraging seeded randomness and procedural generation, developers can create vast and diverse virtual environments while minimizing the storage and performance overhead associated with traditional approaches.

Conclusion

Pseudorandomness, facilitated by the ChaCha8Rng crate, plays a vital role in enhancing the tree generation process. By utilizing the seeded PRNG and the gen_rn method, we introduce controlled randomness to various aspects of tree creation. This controlled randomness ensures that each generated tree is unique while maintaining consistency when using the same seed.

Furthermore, the use of seeded randomness and procedural generation extends beyond individual trees. By employing these techniques, developers can efficiently generate entire forests of unique trees with minimal data storage and resource requirements. This approach proves to be highly advantageous for large-scale open-world games and other applications requiring diverse and realistic virtual environments.

In the next part of this series, we will explore the rendering and optimization techniques necessary to bring these procedural trees to life within a WebGPU environment. Stay tuned for more exciting insights!

Thank you for reading, and happy coding!

--

--