• entries
    436
  • comments
    1179
  • views
    764072

Node Graph UI for Accidental Noise Library

1735 views

So the last couple days I've been working on something that I've wanted to do for a long time now.

E3Wnvvp.png

I've been building it as part of a terrain editor I've been working on. It's still mostly uncomplete, but so far you can create nodes, drag links to link/unlink them, then output them to a greyscale image. In the works, I've got a few node types to implement, and a lot of glue code to implement saving/loading node graphs, hooking them up to generate terrain and terrain layer weights, etc... But it's coming along pretty nicely.

In the process, I have made a few fixes and additions to the noise library, to help with matters. One of the main additions is the Fractal node. The library, of course, has had fractals since the beginning, but the way they were implemented made it tough to allow them in the node graph editor. So instead, I implemented a special function type that can take as input a chain of functions for the layer noise, as well as other functions to provide the fractal parameters. Internally, the function will iterate over the number of octaves, and calculate the noise value. At each octave, the layer function chain is re-seeded with a new seed. This travels the function graph, and sets a new seed for any values of Seed type in the chain. This small change has opened up some easier ways of making fractals.

Additionally, I have added a Seeder module, which implements this internal re-seeding operation. I have implemented also a Randomize module. The randomize module takes a seed or seed chain, and uses it to randomize an output value from within a range.

It's kinda weird to talk about, so instead I'll demonstrate using a real-world solution to a common problem. Here is a fractal image of ridged noise:

nZYkXO3.png

This fractal is generated by layering successive layers, where the input is a Value noise basis passed through an Abs operation before being summed with previous layers. It creates the Ridged Multifractal variant, but you can see in that image that there are grid-based artifacts. These kinds of artifacts are common; each layer of noise is generated on a grid, and so the artifacts tend to sort of amplify as the fractal is built. You can mitigate it somewhat using different values for lacunarity (which is the value that scales the frequency for each successive layer) but that can only slightly reduce the visible appearance of artifacts, not eliminate them altogether. A long time ago, I advocated for applying a randomized axial rotation to each layer of noise, rotating the noise function around a specifiable axis in order to un-align the individual grid bases, and prevent the grid biases from amplifying one another. Previously, these rotations were built directly into the fractals, but that is no longer necessary. The new Randomizer and Fractal nodes now make this easy to incorporate in a more flexible way (or eliminate, if you really want artifacts):

49DwnX2.png

In this screenshot, you can see that I have set up a fractal node, and for the layer source I specify a gradient basis fed through an Abs function. That function in turn feeds a RotateDomain node, which feeds the layer input of the fractal. Attached to the angle input on the fractal is a Randomize node, that randomizes a value in the range 0 to 3. The result is this:

fbnNdcT.png

You can see that the grid artifacts are gone. The fractal iterates the layers, and at each layer it re-seeds the Layer input chain. This means that any node marked as a seed is re-set to a new value each time. This means that the Gradient basis node (which has a seed) is re-seeded, and the Randomize node that specifies the rotation angle is re-seeded. This means that each noise layer generates a different pattern than the other layers, and is rotated by a different amount around the Z axis. This misaligns the grid biases, preventing them from amplifying each other, and gives a nice non-artifacty fractal pattern.

I still have quite a bit to do in implementing the rest of the noise functions in ANL. But there you go, that's what I'm working on right now.



7 Comments


Recommended Comments

It seems like for some formulas, the number of nodes might explode dramatically.

Have you considered a node where you can execute a single line of math, similar to Excel's cell formulas?

If you already have scripting in your engine, you could leverage the same scripting backend.

bc3aa9160b.png 

(Note: the math in the box is dumb and meaningless)

Share this comment


Link to comment

I actually have considered it, yeah. As soon as I've got the existing stuff all wired up, I'll probably work on it. The functionality is mostly there, to parse out a string expression, so I don't think it'll be too troublesome.

As for the arrows and buttons on the output box... those are the first 64x64 block of pixels in the UI texture. I stuck a 64x64 BorderImage widget in there, with the idea that eventually it'll show a preview image of the noise module that automatically updates, and when you create a BorderImage in the Urho3D editor, it automatically assigns the UI texture. I'll change that soon.

Share this comment


Link to comment

So, I decided I'd go ahead and implement the expression node now:

ZoRiPgz.png

It works pretty well. :D

Share this comment


Link to comment

One of really few usages where such visual graph node approach really shines for people who can actually write code instead of connecting nodes. Great job !

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now