Crystalline Bloom
Generated by GridFlow AI | Tags: fractal, crystal, growth, generative, recursive, blue, glow, procedural, organic, abstract
💡 AI 提示词
Fractal Crystal Growth🧠 核心算法要点
- A central seed initiates a recursive function that generates connected crystal segments.
- Each recursive call creates a new crystal segment (represented by a line) extending from the previous segment's endpoint.
- From the endpoint of each segment, multiple new segments can branch out with a controlled probability, introducing divergence.
- Parameters such as segment length, branching angle, color (hue, saturation, brightness), and depth are dynamically varied and passed to child segments, ensuring organic diversity and gradual changes.
- A `maxDepth` parameter limits the recursion, and segment size consistently reduces with increasing depth, creating a detailed fractal structure that diminishes in scale.
- Key rendering parameters like maximum depth, initial segment length, and stroke weight are made responsive, scaling relative to the canvas dimensions.
- Crystal segments are visualized as lines, with rotated rectangles ('facets') placed at their endpoints, and a glow effect is added using `p.drawingContext.shadow` for an ethereal appearance.
💻 原始 p5.js 代码
var sketch = function(p) {
let nodes = [];
let params = {}; // Store all parameters in an object for easy access and modification
p.setup = function() {
let container = document.getElementById('p5-wrapper'); p.createCanvas(container.offsetWidth, container.offsetHeight).parent('p5-wrapper');
p.angleMode(p.DEGREES);
p.rectMode(p.CENTER);
p.noLoop(); // Generate once and display. Can be removed for subtle animation.
p.regenerateCrystal();
};
p.windowResized = function() {
let container = document.getElementById('p5-wrapper'); p.resizeCanvas(container.offsetWidth, container.offsetHeight);
p.regenerateCrystal();
};
p.regenerateCrystal = function() {
nodes = [];
// Responsive parameters based on canvas size
params.maxDepth = p.floor(p.map(p.width * p.height, 200 * 200, 2000 * 2000, 5, 8, true)); // Clamp depth for different screen sizes
params.branchProbability = p.map(params.maxDepth, 5, 8, 0.65, 0.85);
params.initialSegmentLength = p.width * 0.08; // Length of the first segment, relative to width
params.sizeReductionFactor = 0.7; // How much segments shrink with depth
params.angleSpread = 80; // Total angular deviation for new branches
params.baseHue = p.random(180, 260); // Cool colors: blues, purples
params.colorVariance = 20; // How much hue can vary
params.strokeWeightFactor = p.width * 0.0015; // Base stroke weight, relative to width
// Setup color mode for HSB for easier color manipulation
p.colorMode(p.HSB, 360, 100, 100, 100); // Hue, Saturation, Brightness, Alpha
// Start recursive growth from the center, pointing upwards initially
// growCrystal(x, y, length, angle, depth, hue, saturation, brightness)
growCrystal(p.width / 2, p.height / 2, params.initialSegmentLength, -90, 0,
params.baseHue, 80, 90);
p.redraw(); // Redraw once after regeneration if noLoop() is active
};
function growCrystal(x, y, length, angle, depth, hue, saturation, brightness) {
if (depth >= params.maxDepth || length < 1) {
return; // Stop recursion if max depth reached or segment too small
}
// Calculate end point for the current segment
let endX = x + p.cos(angle) * length;
let endY = y + p.sin(angle) * length;
// Store the node (representing a crystal segment) for later drawing
nodes.push({
startX: x, startY: y,
endX: endX, endY: endY,
length: length,
angle: angle,
depth: depth,
color: p.color(hue, saturation, brightness)
});
let numBranches = p.floor(p.random(1, 4)); // 1 to 3 branches can emerge from this point
// Small initial random offset for the group of branches
let baseBranchAngle = angle + p.random(-params.angleSpread / 4, params.angleSpread / 4);
for (let i = 0; i < numBranches; i++) {
if (p.random(1) < params.branchProbability) { // Probabilistic branching
// Spread branches symmetrically around the baseBranchAngle
let nextAngle = baseBranchAngle + p.map(i, 0, numBranches - 1, -params.angleSpread / 2, params.angleSpread / 2);
// Add slight random jitter to nextAngle for organic look
nextAngle += p.random(-params.angleSpread / (params.maxDepth * 2), params.angleSpread / (params.maxDepth * 2));
let nextLength = length * params.sizeReductionFactor * p.random(0.9, 1.1); // Vary length slightly
let nextDepth = depth + 1;
// Vary color components for the next branch
let nextHue = hue + p.random(-params.colorVariance, params.colorVariance);
let nextSaturation = saturation * p.random(0.8, 1.1);
let nextBrightness = brightness * p.random(0.8, 1.1);
// Ensure color components stay within HSB ranges
nextHue = (nextHue + 360) % 360;
nextSaturation = p.constrain(nextSaturation, 30, 100);
nextBrightness = p.constrain(nextBrightness, 30, 100);
// Recursive call for the next crystal segment
growCrystal(endX, endY, nextLength, nextAngle, nextDepth, nextHue, nextSaturation, nextBrightness);
}
}
}
p.draw = function() {
p.background(0); // Solid black background
// Set up shadow properties for a glow effect around the crystal
p.drawingContext.shadowOffsetX = 0;
p.drawingContext.shadowOffsetY = 0;
p.drawingContext.shadowBlur = p.width * 0.015; // Responsive blur amount
for (let node of nodes) {
let c = node.color;
// Alpha fades out with depth, making deeper parts more transparent
let currentAlpha = p.map(node.depth, 0, params.maxDepth, 100, 30);
c.setAlpha(currentAlpha);
p.stroke(c); // Set stroke color
p.fill(c, 20); // Set semi-transparent fill color for facet shapes
// Set shadow color to match the stroke color for a coherent glow
p.drawingContext.shadowColor = p.color(c.levels[0], c.levels[1], c.levels[2], 100); // Full alpha for shadow color
// Stroke weight decreases with depth
p.strokeWeight(params.strokeWeightFactor * p.map(node.depth, 0, params.maxDepth, 1.5, 0.5));
// Draw the crystal segment (line)
p.line(node.startX, node.startY, node.endX, node.endY);
// Draw a "crystal facet" (rotated rectangle) at the end of the segment
p.push();
p.translate(node.endX, node.endY);
p.rotate(node.angle + 45); // Rotate to make it diamond-like
// Facet size decreases with depth
let rectSize = p.map(node.depth, 0, params.maxDepth, node.length * 0.3, node.length * 0.1);
rectSize = p.constrain(rectSize, p.width * 0.003, p.width * 0.015); // Constrain min/max facet size
p.rect(0, 0, rectSize, rectSize); // Draw the facet
p.pop();
}
};
};
new p5(sketch);
🎨 AI 艺术解读
This artwork, 'Crystalline Bloom,' evokes the silent, intricate process of natural mineral formation and biological growth. It visualizes the fractal beauty inherent in recursive patterns, where simple rules applied iteratively lead to complex, organic structures reminiscent of ice crystals or deep-sea mineral veins. The soft, glowing blue and purple hues suggest an otherworldly or ethereal environment, hinting at hidden energies and the slow, persistent accumulation of matter into breathtaking forms. Each branching path represents a trajectory of development, converging into an emergent, intricate complexity.
📝 补充说明
- **HSB Color Mode**: Utilizing `p.colorMode(p.HSB)` simplifies color manipulation, allowing independent and intuitive control over hue, saturation, and brightness, making color variations easier to manage for depth and randomness.
- **Shadow for Glow Effect**: The native `p.drawingContext.shadow*` properties are employed to create a subtle glow around the crystal segments, enhancing their ethereal quality without needing complex shader techniques or multiple drawing passes.
- **`p.noLoop()` and `p.redraw()`**: By calling `p.noLoop()` in `p.setup` and `p.redraw()` after `p.regenerateCrystal()`, the artwork is generated only once or upon window resize, conserving CPU resources when a static image or infrequent updates are desired.
- **Randomness for Organic Variation**: Introducing randomness in the number of branches, angles, sizes, and color components (hue, saturation, brightness) ensures that each generated crystal is unique and possesses organic, non-uniform variations, preventing a repetitive or sterile appearance.
- **Segment Representation**: Combining `p.line` for the primary crystal growth and `p.rect` (rotated into a diamond shape) for the 'facets' at segment endpoints provides a simple yet effective visual language for crystalline structures, augmented by the depth-based scaling and alpha transparency.