/// <summary> /// Generates a value-coherent-noise value from the coordinates of a /// three-dimensional input value. /// </summary> /// <param name="x">The x coordinate of the input value.</param> /// <param name="y">The y coordinate of the input value.</param> /// <param name="z">The z coordinate of the input value.</param> /// <param name="seed">The random number seed.</param> /// <param name="noiseQuality">The quality of the coherent-noise.</param> /// <returns>The generated value-coherent-noise value.</returns> /// <remarks> /// The return value ranges from -1.0 to +1.0. /// /// For an explanation of the difference between gradient noise and /// value noise, see the comments for the <see cref="GradientNoise3D"/> function. /// </remarks> public static double ValueCoherentNoise3D(double x, double y, double z, int seed = 0, NoiseQuality noiseQuality = NoiseQuality.Standard) { // Create a unit-length cube aligned along an integer boundary. This cube // surrounds the input point. var x0 = x > 0.0 ? (int)x : (int)x - 1; var x1 = x0 + 1; var y0 = y > 0.0 ? (int)y : (int)y - 1; var y1 = y0 + 1; var z0 = z > 0.0 ? (int)z : (int)z - 1; var z1 = z0 + 1; // Map the difference between the coordinates of the input value and the // coordinates of the cube's outer-lower-left vertex onto an S-curve. double xs = 0, ys = 0, zs = 0; switch (noiseQuality) { case NoiseQuality.Fast: xs = x - x0; ys = y - y0; zs = z - z0; break; case NoiseQuality.Standard: xs = NoiseMath.SCurve3(x - x0); ys = NoiseMath.SCurve3(y - y0); zs = NoiseMath.SCurve3(z - z0); break; case NoiseQuality.Best: xs = NoiseMath.SCurve5(x - x0); ys = NoiseMath.SCurve5(y - y0); zs = NoiseMath.SCurve5(z - z0); break; } // Now calculate the noise values at each vertex of the cube. To generate // the coherent-noise value at the input point, interpolate these eight // noise values using the S-curve value as the interpolant (trilinear // interpolation.) double n0, n1, ix0, ix1, iy0, iy1; n0 = ValueNoise3D(x0, y0, z0, seed); n1 = ValueNoise3D(x1, y0, z0, seed); ix0 = NoiseMath.Linear(n0, n1, xs); n0 = ValueNoise3D(x0, y1, z0, seed); n1 = ValueNoise3D(x1, y1, z0, seed); ix1 = NoiseMath.Linear(n0, n1, xs); iy0 = NoiseMath.Linear(ix0, ix1, ys); n0 = ValueNoise3D(x0, y0, z1, seed); n1 = ValueNoise3D(x1, y0, z1, seed); ix0 = NoiseMath.Linear(n0, n1, xs); n0 = ValueNoise3D(x0, y1, z1, seed); n1 = ValueNoise3D(x1, y1, z1, seed); ix1 = NoiseMath.Linear(n0, n1, xs); iy1 = NoiseMath.Linear(ix0, ix1, ys); return(NoiseMath.Linear(iy0, iy1, zs)); }
/// <summary> /// Create a new NoiseCube from the given source NoiseCube using trilinear filtering. /// /// When the sample is outside the source BorderValue is used by default. If <see cref="clamp"/>/> /// is set clamping is used instead. /// </summary> /// <param name="src">The source NoiseCube</param> /// <param name="width">Width of the new NoiseCube</param> /// <param name="height">Height of the new NoiseCube</param> /// <param name="depth">Depth of the new NoiseCube</param> /// <param name="clamp">Use clamping when the sample is outside the source NoiseCube</param> /// <returns>The new NoiseCube</returns> public static NoiseCube TrilinearFilter(NoiseCube src, int width, int height, int depth, bool clamp = false) { var dest = new NoiseCube(width, height, depth); float xratio = (float)src.Width / dest.Width; float yratio = (float)src.Height / dest.Height; float zratio = (float)src.Depth / dest.Depth; Parallel.For(0, dest.Depth, z => { for (int y = 0; y < dest.Height; ++y) { for (int x = 0; x < dest.Width; ++x) { float u = (x + 0.5f) * xratio - 0.5f; float v = (y + 0.5f) * yratio - 0.5f; float w = (z + 0.5f) * zratio - 0.5f; int x0 = NoiseMath.FastFloor(u); int y0 = NoiseMath.FastFloor(v); int z0 = NoiseMath.FastFloor(w); int x1 = x0 + 1; int y1 = y0 + 1; int z1 = z0 + 1; float xf = u - x0; float yf = v - y0; float zf = w - z0; if (clamp) { x0 = NoiseMath.Clamp(x0, 0, src.Width - 1); x1 = NoiseMath.Clamp(x1, 0, src.Width - 1); y0 = NoiseMath.Clamp(y0, 0, src.Height - 1); y1 = NoiseMath.Clamp(y1, 0, src.Height - 1); z0 = NoiseMath.Clamp(z0, 0, src.Depth - 1); z1 = NoiseMath.Clamp(z1, 0, src.Depth - 1); } float c000 = src.GetValue(x0, y0, z0); float c001 = src.GetValue(x0, y0, z1); float c010 = src.GetValue(x0, y1, z0); float c011 = src.GetValue(x0, y1, z1); float c100 = src.GetValue(x1, y0, z0); float c101 = src.GetValue(x1, y0, z1); float c110 = src.GetValue(x1, y1, z0); float c111 = src.GetValue(x1, y1, z1); float val = NoiseMath.Trilinear(xf, yf, zf, c000, c001, c010, c011, c100, c101, c110, c111); dest.SetValue(x, y, z, val); } } }); return(dest); }
/// <summary> /// Create a new NoiseMap from the given source NoiseMap using bilinear filtering. /// /// When the sample is outside the source BorderValue is used by default. If <see cref="clamp"/>/> /// is set clamping is used instead. /// </summary> /// <param name="src">The source NoiseMap</param> /// <param name="width">Width of the new NoiseMap</param> /// <param name="height">Height of the new NoiseMap</param> /// <param name="clamp">Use clamping when the sample is outside the source NoiseMap</param> /// <returns>The new NoiseMap</returns> public static NoiseMap BilinearFilter(NoiseMap src, int width, int height, bool clamp = false) { var dest = new NoiseMap(width, height); var xratio = (float)src.Width / dest.Width; var yratio = (float)src.Height / dest.Height; Parallel.For(0, dest.Height, y => { for (var x = 0; x < dest.Width; ++x) { var u = (x + 0.5f) * xratio - 0.5f; var v = (y + 0.5f) * yratio - 0.5f; var x0 = NoiseMath.FastFloor(u); var y0 = NoiseMath.FastFloor(v); var x1 = x0 + 1; var y1 = y0 + 1; var xf = u - x0; var yf = v - y0; if (clamp) { x0 = NoiseMath.Clamp(x0, 0, src.Width - 1); x1 = NoiseMath.Clamp(x1, 0, src.Width - 1); y0 = NoiseMath.Clamp(y0, 0, src.Height - 1); y1 = NoiseMath.Clamp(y1, 0, src.Height - 1); } var c00 = src.GetValue(x0, y0); var c01 = src.GetValue(x0, y1); var c10 = src.GetValue(x1, y0); var c11 = src.GetValue(x1, y1); var val = NoiseMath.Bilinear(xf, yf, c00, c01, c10, c11); dest.SetValue(x, y, val); } }); return(dest); }