Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

noise: perlin2d fix + 3d implementation #193

Merged
merged 7 commits into from
Jan 6, 2024

Conversation

PottierLoic
Copy link
Contributor

@PottierLoic PottierLoic commented Dec 26, 2023

Fixed 2d perlin noise and added 3d function too !

using this code, the 2d perlin noise look like this:

for i in 0..pwidth {
  for j in 0..pheight {
    value := noise.perlin2d(i*0.1, j*0.1) * 255
    state.pixels[i][j] = u32(gx.rgb(u8(value), u8(value), u8(value)).abgr8())
  }
}

image

and using this code and by increasing depth we have this 3d perlin noise:

mut d := 0.01
for {
  for i in 0..pwidth {
    for j in 0..pheight {
      value := noise.perlin3d(i*0.02, j*0.02, d) * 255
      state.pixels[i][j] = u32(gx.rgb(u8(value), u8(value), u8(value)).abgr8())
    }
  }
  d += 0.02
  time.sleep(33 * time.millisecond)
}

perlin3d

Summary by CodeRabbit

  • New Features
    • Introduced a module for generating 2D and 3D Perlin noise, allowing for the creation of natural-looking textures and patterns.

Copy link

coderabbitai bot commented Dec 26, 2023

Walkthrough

The noise module has been expanded with new capabilities for generating Perlin noise, a popular gradient noise function. It now includes methods for both 2D and 3D noise generation. The update introduces a Perlin struct for 2D noise with methods to randomize and generate noise values, and a new function to calculate 3D Perlin noise.

Changes

File(s) Change Summary
noise/perlin2d.v Added Perlin struct with methods for 2D noise.
noise/perlin3d.v Introduced function for 3D Perlin noise.

Poem

🐰 "In the realm of code, so spry and bold,
A rabbit hopped through the fields of old.
With Perlin whispers in 2D and 3D, quite fair,
It crafted worlds with a random, soft flair." 🌌

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • You can directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
  • You can tag CodeRabbit on specific lines of code or entire files in the PR by tagging @coderabbitai in a comment. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • You can tag @coderabbitai in a PR comment and ask questions about the PR and the codebase. Examples:
    • @coderabbitai generate interesting stats about this repository from git and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid.
    • @coderabbitai read the files in the src/scheduler package and generate README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 3d59bad and 8a065e9.
Files selected for processing (4)
  • noise/perlin2d.v (1 hunks)
  • noise/perlin2d_test.v (1 hunks)
  • noise/perlin3d.v (1 hunks)
  • noise/perlin3d_test.v (1 hunks)
Additional comments: 14
noise/perlin2d_test.v (4)
  • 6-12: The constants single_perlin and square_perlin are used as expected values in tests. Ensure these values are correctly representing the expected output of the perlin2d function for the given inputs.

  • 14-28: The test_perlin2d function seeds the random number generator and performs assertions to check the correctness of the perlin2d function. It's important to ensure that the seed used here is appropriate for the test and that the assertions cover a sufficient range of cases.

  • 18-18: The assertion for a single point test is correct, but it's important to verify that the expected value single_perlin is accurate and that the tolerance 1.0e-6 is appropriate for the precision of the perlin2d function.

  • 21-27: The nested loops test a 3x3 grid of points. The test coverage is good, but it's crucial to verify that the expected values in square_perlin are correct and that the chosen tolerance is suitable for the function's precision.

noise/perlin3d_test.v (4)
  • 5-23: The constants single_perlin_3d and cube_perlin_3d are used as expected values in tests. It's important to ensure these values accurately represent the expected output of the perlin3d function for the given inputs.

  • 27-45: The test_perlin3d function seeds the random number generator and performs assertions to check the correctness of the perlin3d function. It's important to ensure that the seed used here is appropriate for the test and that the assertions cover a sufficient range of cases.

  • 32-32: The assertion for a single point test is correct, but it's important to verify that the expected value single_perlin_3d is accurate and that the tolerance 1.0e-6 is appropriate for the precision of the perlin3d function.

  • 35-44: The nested loops test a 3x3x3 grid of points. The test coverage is good, but it's crucial to verify that the expected values in cube_perlin_3d are correct and that the chosen tolerance is suitable for the function's precision.

noise/perlin3d.v (3)
  • 3-32: The perlin3d function implementation uses bitwise operations, linear interpolation, and gradient calculations to generate 3D Perlin noise. Ensure that the logic correctly implements the Perlin noise algorithm and that there are no off-by-one errors or incorrect assumptions about the input range.

  • 34-54: The grad3d function calculates the gradient for a given hash and coordinates. Verify that the hash function and the gradient calculation are correctly implemented according to the Perlin noise algorithm.

  • 31-31: The lerp function is used for linear interpolation. Ensure that this function is correctly implemented and used within the context of the Perlin noise algorithm.

noise/perlin2d.v (3)
  • 3-29: The perlin2d function implementation uses bitwise operations, linear interpolation, and gradient calculations to generate 2D Perlin noise. Ensure that the logic correctly implements the Perlin noise algorithm and that there are no off-by-one errors or incorrect assumptions about the input range.

  • 35-55: The grad2d function calculates the gradient for a given hash and coordinates. Verify that the hash function and the gradient calculation are correctly implemented according to the Perlin noise algorithm.

  • 31-31: The lerp function is used for linear interpolation. Ensure that this function is correctly implemented and used within the context of the Perlin noise algorithm.

Copy link
Member

@ulises-jeremias ulises-jeremias left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for your contribution! could you add comments on the functions? the previous definitions had comments so you can use them as reference

image

@ulises-jeremias ulises-jeremias linked an issue Dec 27, 2023 that may be closed by this pull request
4 tasks
@PottierLoic
Copy link
Contributor Author

Just realized that I didn't make it random, actually the permutation set never change...
We can't just shuffle for every value because it wouldn't make perlin noise anymore so maybe creating a generator struct that hold the permutations is possible ?
This way we can have a perlin.randomize() and a perlin.3d(x, y, z) etc.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 8a065e9 and a3eff98.
Files selected for processing (4)
  • noise/perlin2d.v (1 hunks)
  • noise/perlin2d_test.v (1 hunks)
  • noise/perlin3d.v (1 hunks)
  • noise/perlin3d_test.v (1 hunks)
Files skipped from review as they are similar to previous changes (4)
  • noise/perlin2d.v
  • noise/perlin2d_test.v
  • noise/perlin3d.v
  • noise/perlin3d_test.v

@larpon
Copy link
Contributor

larpon commented Dec 27, 2023

Also run v fmt on the files

@PottierLoic
Copy link
Contributor Author

completely forgot sorry

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between a3eff98 and 2224b52.
Files selected for processing (4)
  • noise/perlin2d.v (1 hunks)
  • noise/perlin2d_test.v (1 hunks)
  • noise/perlin3d.v (1 hunks)
  • noise/perlin3d_test.v (1 hunks)
Additional comments: 5
noise/perlin2d_test.v (1)
  • 8-13: The test case uses a fixed seed for reproducibility, which is good practice. However, ensure that the constant single_perlin used in the assertion reflects a known correct value for the given seed and coordinates. If this value is arbitrary or not well-documented, it could lead to confusion or false positives/negatives in the future.
noise/perlin3d_test.v (1)
  • 8-13: Similar to the 2D test, the 3D test uses a fixed seed for reproducibility. It's important to ensure that the constant single_perlin_3d represents an expected correct value for the given seed and coordinates. Documentation or comments explaining how this value was determined would be beneficial for maintainability.
noise/perlin3d.v (1)
  • 33-53: The grad3d function uses a match statement to determine the gradient based on the hash value. Ensure that the hash function provides a good distribution to avoid visual artifacts in the noise pattern. Additionally, the else case returns 0, which could be a silent failure if an unexpected hash value is encountered. Consider logging or handling this case explicitly.
noise/perlin2d.v (2)
  • 520-529: The Perlin struct initializes the permutation table with a shuffled version of the default set. The randomize method also reshuffles the table. Ensure that the shuffling provides the necessary randomness for the noise function. Additionally, consider documenting the fallback to the default permutation set in case of a shuffling error.

  • 555-583: The fade and lerp functions are utility functions used in noise calculation. Ensure that these functions are correctly implemented according to the Perlin noise algorithm. The fade function, in particular, should have a comment explaining the magic numbers used in the polynomial for maintainability.

@larpon
Copy link
Contributor

larpon commented Dec 27, 2023

@ulises-jeremias I'm unsure what the linter errors are about 🤔

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 3

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 2224b52 and 8d682fa.
Files selected for processing (3)
  • noise/lookup.v (1 hunks)
  • noise/perlin2d.v (1 hunks)
  • noise/perlin3d.v (1 hunks)
Additional comments: 6
noise/perlin2d.v (3)
  • 40-41: The fade function is correct and follows the Perlin noise algorithm.

  • 44-64: The grad2d function uses a match statement to return gradient values. The cases are exhaustive and correct.

  • 66-67: The lerp function correctly implements linear interpolation, which is a key part of the Perlin noise algorithm.

noise/perlin3d.v (2)
  • 3-38: The perlin3d function correctly implements the 3D Perlin noise algorithm. The permutation table access pattern is optimized following the previous suggestions.

  • 41-61: The grad3d function is correct and follows the Perlin noise algorithm for 3D space.

noise/lookup.v (1)
  • 3-516: The permutation table is a predefined set of values used in the Perlin noise algorithm. It appears to be correctly defined and matches the typical Perlin noise implementation.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 8d682fa and 604af08.
Files selected for processing (2)
  • noise/perlin2d.v (1 hunks)
  • noise/perlin3d.v (1 hunks)
Files skipped from review as they are similar to previous changes (2)
  • noise/perlin2d.v
  • noise/perlin3d.v

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 3

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 604af08 and bac656d.
Files selected for processing (1)
  • noise/perlin2d.v (1 hunks)
Additional comments: 3
noise/perlin2d.v (3)
  • 41-42: The fade function is correctly implemented as per the Perlin noise algorithm.

  • 45-65: The grad2d function is correctly implemented and matches the expected behavior for gradient computation in Perlin noise.

  • 67-68: The lerp function is a standard linear interpolation function and is correctly implemented.

Comment on lines +5 to +7
pub struct Perlin {
mut:
perm []int = rand.shuffle_clone(permutations) or { panic(err) }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialization of perm array within the Perlin struct using rand.shuffle_clone without a seed may lead to non-random behavior as mentioned in the PR comments.

- perm []int = rand.shuffle_clone(permutations) or { panic(err) }
+ perm []int

Consider initializing the perm array in a separate method where a seed can be provided to ensure proper randomization.


Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
pub struct Perlin {
mut:
perm []int = rand.shuffle_clone(permutations) or { panic(err) }
pub struct Perlin {
mut:
perm []int

Comment on lines +10 to +14
// randomize is a function that shuffle the permutation set inside the Perlin struct
// will not shuffle if rand.seed is not changed
pub fn (mut perlin Perlin) randomize() {
perlin.perm = rand.shuffle_clone(permutations) or { panic(err) }
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The randomize method relies on an external seed change for rand. It would be more reliable to allow an optional seed parameter for explicit control.

- pub fn (mut perlin Perlin) randomize() {
+ pub fn (mut perlin Perlin) randomize(seed int) {
+     rand.seed(seed)
      perlin.perm = rand.shuffle_clone(permutations) or { panic(err) }
  }

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
// randomize is a function that shuffle the permutation set inside the Perlin struct
// will not shuffle if rand.seed is not changed
pub fn (mut perlin Perlin) randomize() {
perlin.perm = rand.shuffle_clone(permutations) or { panic(err) }
}
// randomize is a function that shuffle the permutation set inside the Perlin struct
// will not shuffle if rand.seed is not changed
pub fn (mut perlin Perlin) randomize(seed int) {
rand.seed(seed)
perlin.perm = rand.shuffle_clone(permutations) or { panic(err) }
}

Comment on lines +16 to +38
// perlin2d is a function that return a single value of perlin noise for a given 2d position
pub fn (perlin Perlin) perlin2d(x f64, y f64) f64 {
xi := int(x) & 0xFF
yi := int(y) & 0xFF

xf := x - int(x)
yf := y - int(y)

u := fade(xf)
v := fade(yf)

pxi := perlin.perm[xi]
pxi1 := perlin.perm[xi + 1]

aa := perlin.perm[pxi + yi]
ab := perlin.perm[pxi + yi + 1]
ba := perlin.perm[pxi1 + yi]
bb := perlin.perm[pxi1 + yi + 1]

x1 := lerp(grad2d(aa, xf, yf), grad2d(ba, xf - 1, yf), u)
x2 := lerp(grad2d(ab, xf, yf - 1), grad2d(bb, xf - 1, yf - 1), u)

return (lerp(x1, x2, v) + 1) / 2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The perlin2d function accesses the perm array multiple times which could be optimized by caching the relevant values.

+ let pxi_perm = perlin.perm[pxi]
+ let pxi1_perm = perlin.perm[pxi1]
- aa := perlin.perm[pxi + yi]
- ab := perlin.perm[pxi + yi + 1]
- ba := perlin.perm[pxi1 + yi]
- bb := perlin.perm[pxi1 + yi + 1]
+ aa := pxi_perm + yi
+ ab := pxi_perm + yi + 1
+ ba := pxi1_perm + yi
+ bb := pxi1_perm + yi + 1

Consider caching the perm[xi] and perm[xi + 1] values to reduce array access and potentially improve performance.


Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
// perlin2d is a function that return a single value of perlin noise for a given 2d position
pub fn (perlin Perlin) perlin2d(x f64, y f64) f64 {
xi := int(x) & 0xFF
yi := int(y) & 0xFF
xf := x - int(x)
yf := y - int(y)
u := fade(xf)
v := fade(yf)
pxi := perlin.perm[xi]
pxi1 := perlin.perm[xi + 1]
aa := perlin.perm[pxi + yi]
ab := perlin.perm[pxi + yi + 1]
ba := perlin.perm[pxi1 + yi]
bb := perlin.perm[pxi1 + yi + 1]
x1 := lerp(grad2d(aa, xf, yf), grad2d(ba, xf - 1, yf), u)
x2 := lerp(grad2d(ab, xf, yf - 1), grad2d(bb, xf - 1, yf - 1), u)
return (lerp(x1, x2, v) + 1) / 2
// perlin2d is a function that return a single value of perlin noise for a given 2d position
pub fn (perlin Perlin) perlin2d(x f64, y f64) f64 {
xi := int(x) & 0xFF
yi := int(y) & 0xFF
xf := x - int(x)
yf := y - int(y)
u := fade(xf)
v := fade(yf)
pxi := perlin.perm[xi]
pxi1 := perlin.perm[xi + 1]
let pxi_perm = perlin.perm[pxi]
let pxi1_perm = perlin.perm[pxi1]
aa := pxi_perm + yi
ab := pxi_perm + yi + 1
ba := pxi1_perm + yi
bb := pxi1_perm + yi + 1
x1 := lerp(grad2d(aa, xf, yf), grad2d(ba, xf - 1, yf), u)
x2 := lerp(grad2d(ab, xf, yf - 1), grad2d(bb, xf - 1, yf - 1), u)
return (lerp(x1, x2, v) + 1) / 2

@PottierLoic
Copy link
Contributor Author

I think it's good like this, I can add some examples later if needed.

@ulises-jeremias ulises-jeremias merged commit 95f081f into vlang:main Jan 6, 2024
@PottierLoic PottierLoic deleted the perlin2d/3d branch July 2, 2024 06:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

noise: Add 3D Perlin Noise to noise module
3 participants