// Create new public MaterialWorleyLayer() : base("worley", true) { _position = Vector2.Zero; _scale = 1f; _frequency = 1.2f; _gain = 0.5f; _lacunarity = 2f; _multiplier = 1f; _fbmOffset = Vector2.Zero; _colorLow = Color.Black; _colorHigh = Color.White; _iterations = 1; _blendType = LayerBlendType.Opaque; _invert = false; _worleyFeature = WorleyFeatureType.F1; _seed = 12345; }
// Create from xml public MaterialWorleyLayer(XElement data) : base(data) { _position = Loader.loadVector2(data.Attribute("position"), Vector2.Zero); _scale = Loader.loadFloat(data.Attribute("scale"), 1); _frequency = Loader.loadFloat(data.Attribute("frequency"), 1); _gain = Loader.loadFloat(data.Attribute("gain"), 0.5f); _lacunarity = Loader.loadFloat(data.Attribute("lacunarity"), 1.8f); _multiplier = Loader.loadFloat(data.Attribute("multiplier"), multiplier); _fbmOffset = Loader.loadVector2(data.Attribute("fbm_offset"), Vector2.Zero); _colorLow = Loader.loadColor(data.Attribute("color_low"), Color.Black); _colorHigh = Loader.loadColor(data.Attribute("color_high"), Color.White); _iterations = Loader.loadInt(data.Attribute("iterations"), 0); _blendType = (LayerBlendType)Loader.loadEnum(typeof(LayerBlendType), data.Attribute("blend_type"), (int)LayerBlendType.Opaque); _invert = Loader.loadBool(data.Attribute("invert"), false); _worleyFeature = (WorleyFeatureType)Loader.loadEnum(typeof(WorleyFeatureType), data.Attribute("worley_feature"), (int)WorleyFeatureType.F1); _seed = Loader.loadInt(data.Attribute("seed"), 12345); }
// fbmWorley -- Uses Fractional Brownian Motion to calculate worley noise private float fbmWorley( Vector2[,] grid, int gridWidth, int gridHeight, Vector2 position, float frequency, float gain, float lacunarity, Vector2 fbmOffset, int iterations, WorleyFeatureType worleyFeatureType) { float total = 0; float amplitude = gain; for (int i = 0; i < iterations; i++) { total += worley(grid, gridWidth, gridHeight, position * frequency + total * fbmOffset, worleyFeatureType) * amplitude; frequency *= lacunarity; amplitude *= gain; } return(total); }
// Worley noise pass public Texture2D worleyPass( Texture2D current, int seed, Vector2 position, float scale, float frequency, float gain, float lacunarity, float multiplier, Vector2 fbmOffset, Color colorLow, Color colorHigh, int iterations, WorleyFeatureType worleyFeatureType, bool invert) { Texture2D output = new Texture2D(_graphicsDevice, current.Width, current.Height); Color[] data = new Color[output.Width * output.Height]; int gridWidth = 32; int gridHeight = 32; Vector2[,] grid = new Vector2[gridWidth, gridHeight]; Random rng = new Random(seed); int chunkCount = (int)Math.Floor((float)output.Width / (float)CHUNK_SIZE) + 1; // Create gradient grid for (int i = 0; i < gridWidth; i++) { for (int j = 0; j < gridHeight; j++) { grid[i, j] = new Vector2( StasisMathHelper.floatBetween(-1, 1, rng), StasisMathHelper.floatBetween(-1, 1, rng)); } } Parallel.For(0, chunkCount, (count) => { int startIndex = count * CHUNK_SIZE; int endIndex = Math.Min((count + 1) * CHUNK_SIZE, output.Width); for (int i = startIndex; i < endIndex; i++) { for (int j = 0; j < output.Height; j++) { Vector2 p = new Vector2(i, j) / new Vector2(gridWidth, gridHeight); p += position; p /= scale; float value = fbmWorley( grid, gridWidth, gridHeight, p, frequency, gain, lacunarity, fbmOffset, iterations, worleyFeatureType); // Clamp values value = Math.Max(0, Math.Min(1, value)); // Multiply value value *= multiplier; // Invert value if necessary if (invert) { value = 1 - value; } Color color = Color.Lerp(colorLow, colorHigh, value); data[i + j * output.Width] = color; } } }); output.SetData <Color>(data); return(output); }
// worley -- Calculates a worley value private float worley( Vector2[,] grid, int gridWidth, int gridHeight, Vector2 position, WorleyFeatureType worleyFeatureType) { int xi = (int)Math.Floor(position.X); int yi = (int)Math.Floor(position.Y); float xf = position.X - (float)xi; float yf = position.Y - (float)yi; float distance1 = 9999999; float distance2 = 9999999; for (int y = -2; y < 3; y++) { for (int x = -2; x < 3; x++) { // Find feature point grid indices int fpx = (xi + x) % gridWidth; int fpy = (yi + y) % gridHeight; fpx = fpx < 0 ? fpx + gridWidth : fpx; fpy = fpy < 0 ? fpy + gridHeight : fpy; // Get feature point by getting gradient at grid cell and modify the coordinates Vector2 fp = grid[fpx, fpy]; fp.X += (float)x - xf; fp.Y += (float)y - yf; // Calculate distances float distance = fp.LengthSquared(); if (distance < distance1) { distance2 = distance1; distance1 = distance; } else if (distance < distance2) { distance2 = distance; } } } // Determine final value based on feature type float value = 0; if (worleyFeatureType == WorleyFeatureType.F1) { value = (float)Math.Sqrt(distance1); } else if (worleyFeatureType == WorleyFeatureType.F2) { value = (float)Math.Sqrt(distance2); } else if (worleyFeatureType == WorleyFeatureType.F2mF1) { value = (float)Math.Sqrt(distance2 - distance1); } return(value); }
// fbmWorley -- Uses Fractional Brownian Motion to calculate worley noise private float fbmWorley( Vector2[,] grid, int gridWidth, int gridHeight, Vector2 position, float frequency, float gain, float lacunarity, Vector2 fbmOffset, int iterations, WorleyFeatureType worleyFeatureType) { float total = 0; float amplitude = gain; for (int i = 0; i < iterations; i++) { total += worley(grid, gridWidth, gridHeight, position * frequency + total * fbmOffset, worleyFeatureType) * amplitude; frequency *= lacunarity; amplitude *= gain; } return total; }
// Worley noise pass public Texture2D worleyPass( Texture2D current, int seed, Vector2 position, float scale, float frequency, float gain, float lacunarity, float multiplier, Vector2 fbmOffset, Color colorLow, Color colorHigh, int iterations, WorleyFeatureType worleyFeatureType, bool invert) { Texture2D output = new Texture2D(_graphicsDevice, current.Width, current.Height); Color[] data = new Color[output.Width * output.Height]; int gridWidth = 32; int gridHeight = 32; Vector2[,] grid = new Vector2[gridWidth, gridHeight]; Random rng = new Random(seed); int chunkCount = (int)Math.Floor((float)output.Width / (float)CHUNK_SIZE) + 1; // Create gradient grid for (int i = 0; i < gridWidth; i++) { for (int j = 0; j < gridHeight; j++) { grid[i, j] = new Vector2( StasisMathHelper.floatBetween(-1, 1, rng), StasisMathHelper.floatBetween(-1, 1, rng)); } } Parallel.For(0, chunkCount, (count) => { int startIndex = count * CHUNK_SIZE; int endIndex = Math.Min((count + 1) * CHUNK_SIZE, output.Width); for (int i = startIndex; i < endIndex; i++) { for (int j = 0; j < output.Height; j++) { Vector2 p = new Vector2(i, j) / new Vector2(gridWidth, gridHeight); p += position; p /= scale; float value = fbmWorley( grid, gridWidth, gridHeight, p, frequency, gain, lacunarity, fbmOffset, iterations, worleyFeatureType); // Clamp values value = Math.Max(0, Math.Min(1, value)); // Multiply value value *= multiplier; // Invert value if necessary if (invert) value = 1 - value; Color color = Color.Lerp(colorLow, colorHigh, value); data[i + j * output.Width] = color; } } }); output.SetData<Color>(data); return output; }
// worley -- Calculates a worley value private float worley( Vector2[,] grid, int gridWidth, int gridHeight, Vector2 position, WorleyFeatureType worleyFeatureType) { int xi = (int)Math.Floor(position.X); int yi = (int)Math.Floor(position.Y); float xf = position.X - (float)xi; float yf = position.Y - (float)yi; float distance1 = 9999999; float distance2 = 9999999; for (int y = -2; y < 3; y++) { for (int x = -2; x < 3; x++) { // Find feature point grid indices int fpx = (xi + x) % gridWidth; int fpy = (yi + y) % gridHeight; fpx = fpx < 0 ? fpx + gridWidth : fpx; fpy = fpy < 0 ? fpy + gridHeight : fpy; // Get feature point by getting gradient at grid cell and modify the coordinates Vector2 fp = grid[fpx, fpy]; fp.X += (float)x - xf; fp.Y += (float)y - yf; // Calculate distances float distance = fp.LengthSquared(); if (distance < distance1) { distance2 = distance1; distance1 = distance; } else if (distance < distance2) { distance2 = distance; } } } // Determine final value based on feature type float value = 0; if (worleyFeatureType == WorleyFeatureType.F1) { value = (float)Math.Sqrt(distance1); } else if (worleyFeatureType == WorleyFeatureType.F2) { value = (float)Math.Sqrt(distance2); } else if (worleyFeatureType == WorleyFeatureType.F2mF1) { value = (float)Math.Sqrt(distance2 - distance1); } return value; }