// perlin -- Calculate perlin noise // reference: "Simplex Noise Demystified" http://webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf private float perlin( Vector2[,] grid, int gridWidth, int gridHeight, Vector2 position) { int X = (int)Math.Floor(position.X); int Y = (int)Math.Floor(position.Y); // Get relative xy coordinates of point within that cell float x = position.X - X; float y = position.Y - Y; // Wrap the integer cells int x0 = StasisMathHelper.mod(X, gridWidth); int x1 = StasisMathHelper.mod(X + 1, gridWidth); int y0 = StasisMathHelper.mod(Y, gridHeight); int y1 = StasisMathHelper.mod(Y + 1, gridHeight); // Get gradients Vector2 g00 = grid[x0, y0]; Vector2 g10 = grid[x1, y0]; Vector2 g01 = grid[x0, y1]; Vector2 g11 = grid[x1, y1]; // Calculate noise contributions from each of the eight corners float n00 = Vector2.Dot(g00, new Vector2(x, y)); float n10 = Vector2.Dot(g10, new Vector2(x - 1, y)); float n01 = Vector2.Dot(g01, new Vector2(x, y - 1)); float n11 = Vector2.Dot(g11, new Vector2(x - 1, y - 1)); // Compute the fade curve value for each of x, y, z float u = perlinWeight(x); float v = perlinWeight(y); // Interpolate along x the contributions from each of the corners float nx00 = MathHelper.Lerp(n00, n10, u); float nx10 = MathHelper.Lerp(n01, n11, u); // Interpolate the results along y float value = MathHelper.Lerp(nx00, nx10, v); return(value); }
// Edge scatter pass public Texture2D edgeScatterPass( Texture2D current, List <Vector2> polygonPoints, List <string> textureUIDs, Vector2 direction, float threshold, bool hardCutoff, float spacing, bool useAbsoluteAngle, float absoluteAngle, float relativeAngle, float angleJitter, float jitter, float scale, float scaleJitter, Color baseColor, int randomRed, int randomGreen, int randomBlue, int randomAlpha) { Random rng = new Random(); // Initialize render targets and textures RenderTarget2D renderTarget = new RenderTarget2D(_graphicsDevice, current.Width, current.Height); Texture2D result = new Texture2D(_graphicsDevice, renderTarget.Width, renderTarget.Height); Color[] data = new Color[renderTarget.Width * renderTarget.Height]; // Load and validate textures List <Texture2D> textures = new List <Texture2D>(); foreach (string textureUID in textureUIDs) { Texture2D texture = ResourceManager.getTexture(textureUID); if (texture == null) { return(result); } textures.Add(texture); } if (textures.Count == 0) { return(current); } // Validate polygon points if (polygonPoints == null || polygonPoints.Count < 3) { return(current); } // Validate parameters spacing = Math.Max(0.05f, spacing); // Calculate half-texture offset Vector2 topLeft = polygonPoints[0]; Vector2 bottomRight = polygonPoints[0]; for (int i = 0; i < polygonPoints.Count; i++) { topLeft = Vector2.Min(polygonPoints[i], topLeft); bottomRight = Vector2.Max(polygonPoints[i], bottomRight); } // Draw _graphicsDevice.SetRenderTarget(renderTarget); _graphicsDevice.Clear(Color.Transparent); _spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend); _spriteBatch.Draw(current, current.Bounds, Color.White); bool hasDirection = direction.X != 0 || direction.Y != 0; for (int i = 0; i < polygonPoints.Count; i++) { Vector2 pointA = polygonPoints[i]; Vector2 pointB = polygonPoints[i == polygonPoints.Count - 1 ? 0 : i + 1]; Vector2 relative = pointB - pointA; Vector2 normal = relative; float perpDot = 0; normal.Normalize(); if (hasDirection) { direction.Normalize(); perpDot = direction.X * normal.Y - direction.Y * normal.X; } if (!hasDirection || perpDot > -threshold) { float relativeLength = relative.Length(); float currentPosition = 0f; while (currentPosition < relativeLength) { float angle = 0; Vector2 j = new Vector2((float)rng.NextDouble() * 2 - 1, (float)rng.NextDouble() * 2 - 1) * jitter; Vector2 position = pointA + normal * currentPosition + j; Texture2D texture = textures[rng.Next(textures.Count)]; Color actualColor = getRandomColor(baseColor, randomRed, randomGreen, randomBlue, randomAlpha, rng); float textureScale = StasisMathHelper.floatBetween(scale - scaleJitter, scale + scaleJitter, rng); if (useAbsoluteAngle) { angle = absoluteAngle + StasisMathHelper.floatBetween(-angleJitter, angleJitter, rng); } else { angle = (float)Math.Atan2(relative.Y, relative.X) + relativeAngle + StasisMathHelper.floatBetween(-angleJitter, angleJitter, rng); } _spriteBatch.Draw(texture, (position - topLeft) * Settings.BASE_SCALE, texture.Bounds, actualColor, angle, new Vector2(texture.Width, texture.Height) / 2, textureScale, SpriteEffects.None, 0); currentPosition += spacing; } } } _spriteBatch.End(); _graphicsDevice.SetRenderTarget(null); // Save render target into texture renderTarget.GetData <Color>(data); result.SetData <Color>(data); // Cleanup renderTarget.Dispose(); return(result); }
// Radial scatter pass public Texture2D radialScatterPass( Texture2D current, float growthFactor, List <string> textureUIDs, bool scaleWithGrowthFactor, float a, float b, float intersections, float maxRadius, int arms, bool twinArms, bool flipArms, bool useAbsoluteTextureAngle, float absoluteTextureAngle, float relativeTextureAngle, float textureAngleJitter, float jitter, float centerJitter, Vector2 centerOffset, Color baseColor, float minTextureScale, float maxTextureScale, int randomRed, int randomGreen, int randomBlue, int randomAlpha) { Random rng = new Random(); // Initialize render targets and textures RenderTarget2D renderTarget = new RenderTarget2D(_graphicsDevice, current.Width, current.Height); Texture2D result = new Texture2D(_graphicsDevice, renderTarget.Width, renderTarget.Height); Color[] data = new Color[renderTarget.Width * renderTarget.Height]; // Load and validate textures List <Texture2D> textures = new List <Texture2D>(); foreach (string textureUID in textureUIDs) { Texture2D texture = ResourceManager.getTexture(textureUID); if (texture == null) { return(result); } textures.Add(texture); } if (textures.Count == 0) { return(current); } // Modify parameters based on growth factor (r is modified later) intersections *= growthFactor * growthFactor * growthFactor; arms = (int)Math.Ceiling(arms * growthFactor * growthFactor); maxRadius *= growthFactor; jitter *= Math.Max(growthFactor, 0.1f); centerJitter *= growthFactor; centerOffset *= growthFactor; // Draw _graphicsDevice.SetRenderTarget(renderTarget); _graphicsDevice.Clear(Color.Transparent); _spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend); _spriteBatch.Draw(current, current.Bounds, Color.White); float thetaIncrement = StasisMathHelper.pi * 2 / intersections; float armRotationIncrement = StasisMathHelper.pi * 2 / (float)arms; Vector2 center = centerOffset + new Vector2(current.Width, current.Height) / 2 + new Vector2((float)(rng.NextDouble() * 2 - 1), (float)(rng.NextDouble() * 2 - 1)) * centerJitter; for (int i = 0; i < arms; i++) { float theta = 0; float r = 0; while (r < maxRadius) { r = a * (float)Math.Pow(StasisMathHelper.phi, b * (2f / StasisMathHelper.pi) * theta) * growthFactor; if (r < maxRadius) { float textureScale = StasisMathHelper.floatBetween(minTextureScale, maxTextureScale, rng); float modifiedTheta = (theta + armRotationIncrement * i) * (flipArms ? -1f : 1f); float randomAngleValue = textureAngleJitter == 0 ? 0 : StasisMathHelper.floatBetween(-textureAngleJitter, textureAngleJitter, rng); float textureAngle; if (useAbsoluteTextureAngle) { textureAngle = absoluteTextureAngle + randomAngleValue; } else { textureAngle = relativeTextureAngle + modifiedTheta + randomAngleValue; } Vector2 j = new Vector2((float)(rng.NextDouble() * 2 - 1) * jitter, (float)(rng.NextDouble() * 2 - 1) * jitter); Texture2D texture = textures[rng.Next(textures.Count)]; Color actualColor = getRandomColor(baseColor, randomRed, randomGreen, randomBlue, randomAlpha, rng); //float textureScale = scaleWithGrowthFactor ? growthFactor : 1f; _spriteBatch.Draw(texture, new Vector2(r * (float)Math.Cos(modifiedTheta), r * (float)Math.Sin(modifiedTheta)) + j + center, texture.Bounds, actualColor, textureAngle, new Vector2(texture.Width, texture.Height) / 2, textureScale, SpriteEffects.None, 0); if (twinArms) { j = new Vector2((float)(rng.NextDouble() * 2 - 1) * jitter, (float)(rng.NextDouble() * 2 - 1) * jitter); _spriteBatch.Draw(texture, new Vector2(r * (float)Math.Cos(-modifiedTheta), r * (float)Math.Sin(-modifiedTheta)) + j + center, texture.Bounds, actualColor, -textureAngle, new Vector2(texture.Width, texture.Height) / 2, textureScale, SpriteEffects.None, 0); } } theta += thetaIncrement; } } _spriteBatch.End(); _graphicsDevice.SetRenderTarget(null); // Save render target into texture renderTarget.GetData <Color>(data); result.SetData <Color>(data); // Cleanup renderTarget.Dispose(); return(result); }
// Uniform scatter pass public Texture2D uniformScatterPass( Texture2D current, List <string> textureUIDs, float horizontalSpacing, float verticalSpacing, float jitter, Color baseColor, int randomRed, int randomGreen, int randomBlue, int randomAlpha) { Random rng = new Random(); // Initialize render targets and textures RenderTarget2D renderTarget = new RenderTarget2D(_graphicsDevice, current.Width, current.Height); Texture2D result = new Texture2D(_graphicsDevice, renderTarget.Width, renderTarget.Height); Color[] data = new Color[renderTarget.Width * renderTarget.Height]; // Load and validate textures List <Texture2D> textures = new List <Texture2D>(); foreach (string textureUID in textureUIDs) { Texture2D texture = ResourceManager.getTexture(textureUID); if (texture == null) { return(result); } textures.Add(texture); } if (textures.Count == 0) { return(current); } // Draw _graphicsDevice.SetRenderTarget(renderTarget); _graphicsDevice.Clear(Color.Transparent); _spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend); _spriteBatch.Draw(current, current.Bounds, Color.White); for (float i = 0; i <= current.Width; i += horizontalSpacing) { for (float j = 0; j <= current.Height; j += verticalSpacing) { Vector2 position = new Vector2(i, j) + new Vector2(StasisMathHelper.floatBetween(0, jitter, rng), StasisMathHelper.floatBetween(0, jitter, rng)); float angle = StasisMathHelper.floatBetween(-3.14f, 3.14f, rng); Texture2D texture = textures[rng.Next(0, textures.Count)]; Color actualColor = getRandomColor(baseColor, randomRed, randomGreen, randomBlue, randomAlpha, rng); _spriteBatch.Draw(texture, position, texture.Bounds, actualColor, angle, new Vector2(texture.Width, texture.Height) / 2, 1f, SpriteEffects.None, 0); } } _spriteBatch.End(); _graphicsDevice.SetRenderTarget(null); // Save render target into texture renderTarget.GetData <Color>(data); result.SetData <Color>(data); // Cleanup renderTarget.Dispose(); return(result); }
// 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); }
// Leaves pass public Texture2D leavesPass( Texture2D current, float growthFactor, List <string> textureUids, Color baseColor ) { // Initialize render targets and textures RenderTarget2D renderTarget = new RenderTarget2D(_graphicsDevice, current.Width, current.Height); Texture2D result = new Texture2D(_graphicsDevice, renderTarget.Width, renderTarget.Height); Color[] data = new Color[renderTarget.Width * renderTarget.Height]; Random rng = new Random(); // Load and validate textures List <Texture2D> textures = new List <Texture2D>(); foreach (string textureUID in textureUids) { Texture2D texture = ResourceManager.getTexture(textureUID); if (texture == null) { return(result); } textures.Add(texture); } if (textures.Count == 0) { return(current); } float maxTextureSize = 256; float size = maxTextureSize * growthFactor; float maxRadius = (size / 2f) * 0.85f; float lowTint = 0.3f; if (size < 48f) { return(current); } // Draw _graphicsDevice.SetRenderTarget(renderTarget); _graphicsDevice.Clear(Color.Transparent); _spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend); _spriteBatch.Draw(current, current.Bounds, Color.White); for (float tint = lowTint; tint < 1f; tint += 0.1f) { float radius = maxRadius * (1f - tint + lowTint); int leafCount = (int)((radius / 2f) * (size / maxTextureSize)); //Console.WriteLine("maxTextureSize: {0}, size: {1}, maxRadius: {2}, radius: {3}, tint: {4}, leafCount: {5}", maxTextureSize, size, maxRadius, radius, tint, leafCount); for (int n = 0; n < leafCount; n++) { // Calculate random position Texture2D leafTexture = textures[rng.Next(textures.Count)]; Vector2 position = new Vector2(StasisMathHelper.floatBetween(-1f, 1f, rng), StasisMathHelper.floatBetween(-1f, 1f, rng)) * radius; position += new Vector2(current.Width, current.Height) / 2; // Calculate shadow value //float shadowValue = Math.Max(metamer.budQuality, 0.5f); // Calculate color value float r = StasisMathHelper.floatBetween(0.9f, 1.2f, rng); float g = StasisMathHelper.floatBetween(0.9f, 1.1f, rng); float b = StasisMathHelper.floatBetween(0.9f, 1f, rng); Color finalColor = new Color(tint * r * (baseColor.R / 255f), tint * g * (baseColor.G / 255f), tint * b * (baseColor.B / 255f)); float angle = (float)(rng.NextDouble() * Math.PI * 2); float scale = StasisMathHelper.floatBetween(0.25f, 1f, rng); _spriteBatch.Draw(leafTexture, position, leafTexture.Bounds, finalColor, angle, new Vector2(leafTexture.Width, leafTexture.Height) / 2, scale, SpriteEffects.None, 0); } } _spriteBatch.End(); _graphicsDevice.SetRenderTarget(null); // Save render target into texture renderTarget.GetData <Color>(data); result.SetData <Color>(data); // Cleanup renderTarget.Dispose(); return(result); }