Flax contains various utilities for sampling different noise functions such as:
Noise | Preview |
---|---|
Perlin | ![]() |
Simplex | ![]() |
Worley | ![]() |
Voronoi | ![]() |
Custom | ![]() |
Those can be accessed via scripting FlaxEngine.Utilities.Noise
static class and used to enrich procedurally generated worlds and content.
All visual graphs (materials, particles, animations) can sample noise functions both on CPU and GPU to enrich the content. All noise functions return normalized results in the range 0-1. Some of them return more noise components, such as Voronoi Noise where X=minDistToCell
, Y=randomColor
, Z=minEdgeDistance
. Depending on the context only one or even all components can be used. Follow the tooltip with documentation for every node.
Below is a sample code for custom UI Control that can be added to the scene for debugging or testing different noise types visually. Simply create UI Canvas
, add UI Control
to it, set control type to NoisePreview
, and use NoiseType
with NoiseScale
properties to analyze noises.
public class NoisePreview : ContainerControl
{
private GPUTexture _tempTexture;
private byte[] _data;
private Noises _noise = Noises.None;
private float _scale;
public enum Noises
{
[HideInEditor]
None,
Perlin,
Simplex,
Voronoi,
Worley,
Custom,
}
/// <summary>
/// Noise to preview.
/// </summary>
public Noises NoiseType = Noises.Perlin;
/// <summary>
/// Noise scale.
/// </summary>
[Limit(0.001f)]
public float NoiseScale = 10.0f;
/// <inheritdoc />
public NoisePreview()
{
Size = new Float2(64);
}
/// <inheritdoc />
public override void DrawSelf()
{
base.DrawSelf();
if (!_tempTexture)
{
// Create new GPU texture
var texture = new GPUTexture();
_tempTexture = texture;
var desc = GPUTextureDescription.New2D(64, 64, PixelFormat.R8G8B8A8_UNorm, GPUTextureFlags.ShaderResource);
if (texture.Init(ref desc))
return;
}
if (_noise != NoiseType || _scale != NoiseScale)
{
// Update noise texture
_noise = NoiseType;
_scale = NoiseScale;
UpdateTexture();
}
// Draw noise texture
Render2D.DrawTexture(_tempTexture, new Rectangle(Float2.Zero, Size));
}
/// <inheritdoc />
public override void OnDestroy()
{
// Ensure to cleanup resources
_tempTexture?.ReleaseGPU();
FlaxEngine.Object.Destroy(ref _tempTexture);
base.OnDestroy();
}
private unsafe void UpdateTexture()
{
var desc = _tempTexture.Description;
var size = desc.Width * desc.Height * PixelFormatExtensions.SizeInBytes(desc.Format);
if (_data == null || _data.Length != size)
_data = new byte[size];
fixed (byte* dataPtr = _data)
{
// Generate pixels data
Float2 uv;
var colorsPtr = (Color32*)dataPtr;
for (int y = 0; y < desc.Height; y++)
{
uv.Y = (float)y / desc.Height * _scale;
for (int x = 0; x < desc.Width; x++)
{
uv.X = (float)x / desc.Width * _scale;
// Sample noise at uv location
var noise = Vector3.Zero;
switch (_noise)
{
case Noises.Perlin:
noise = new Vector3(FlaxEngine.Utilities.Noise.PerlinNoise(uv));
break;
case Noises.Simplex:
noise = new Vector3(FlaxEngine.Utilities.Noise.SimplexNoise(uv));
break;
case Noises.Voronoi:
noise = FlaxEngine.Utilities.Noise.VoronoiNoise(uv);
break;
case Noises.Worley:
noise = new Vector3(FlaxEngine.Utilities.Noise.WorleyNoise(uv), 0.0f);
break;
case Noises.Custom:
noise = new Vector3(FlaxEngine.Utilities.Noise.CustomNoise(new Float3(uv, 0.0f)));
break;
}
colorsPtr[y * desc.Width + x] = (Color32)(Color)noise;
}
}
// Update texture data on a GPU (send data)
uint rowPitch = (uint)size / (uint)desc.Height;
uint slicePitch = (uint)size;
GPUDevice.Instance.MainContext.UpdateTexture(_tempTexture, 0, 0, new IntPtr(dataPtr), rowPitch, slicePitch);
_tempTexture.ResidentMipLevels = 1; // Mark mip-map as available (required for standard textures only - other than render textures)
}
}
}