From fb8c0f12d593ebe3bdb90e2ddf25b3945217fb99 Mon Sep 17 00:00:00 2001 From: ae Date: Sun, 1 Jun 2025 18:36:24 +0300 Subject: [PATCH] feat: expanded perlin bg. gen. randomization - wider scale ranges for more visible pattern variations - larger offset ranges for more unique starting points - randomized noise weights instead of fixed 0.6/0.3/0.1 ratios - randomized gradient strength & direction (30% chance of reverse for each axis) - randomized light-to-dark effects - multiple combination methods: - standard linear blend (cur.) - multiplicative blend (flowing, organic patterns) - maximum blend (sharper, more contrasted patterns) - turbulence (chaotic, cloud-like patterns) - 30% chance for additional subtle sine/cosine distortion --- banner.go | 95 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 17 deletions(-) diff --git a/banner.go b/banner.go index 11bae86..f73c56c 100644 --- a/banner.go +++ b/banner.go @@ -5,6 +5,7 @@ import ( "image" "image/color" "image/png" + "math" "math/rand/v2" "os" "path/filepath" @@ -256,36 +257,96 @@ func genPerlinBG() *image.RGBA { img := image.NewRGBA(image.Rect(0, 0, config.BannerWidth, config.BannerHeight)) // noise params for interesting patterns - scale1 := 0.01 + rand.Float64()*0.02 // primary pattern scale (adjusted by lib) - scale2 := 0.03 + rand.Float64()*0.03 // secondary detail - scale3 := 0.08 + rand.Float64()*0.05 // fine detail + scale1 := 0.005 + rand.Float64()*0.025 // primary pattern scale (adjusted by lib) + scale2 := 0.02 + rand.Float64()*0.04 // secondary detail + scale3 := 0.06 + rand.Float64()*0.08 // fine detail // variation offsets - offsetX := rand.Float64() * 1000 - offsetY := rand.Float64() * 1000 + offsetX := rand.Float64() * 5000 + offsetY := rand.Float64() * 5000 - log.Infof("Generating background with theme: %s, scales: %.4f, %.4f, %.4f", theme, scale1, scale2, scale3) + // noise layer weights for different effects + weight1 := 0.4 + rand.Float64()*0.4 + weight2 := 0.2 + rand.Float64()*0.3 + weight3 := 0.1 + rand.Float64()*0.2 + + // normalize weights to sum to 1.0 + totalWeight := weight1 + weight2 + weight3 + weight1 /= totalWeight + weight2 /= totalWeight + weight3 /= totalWeight + + gradientStrengthX := rand.Float64() * 0.4 + gradientStrengthY := rand.Float64() * 0.3 + gradientDirectionX := 1.0 + gradientDirectionY := 1.0 + + // sometimes reverse gradient direction for variety + if rand.Float64() < 0.3 { + gradientDirectionX = -1.0 + } + if rand.Float64() < 0.3 { + gradientDirectionY = -1.0 + } + + // randomize noise combination method + combineMethod := rand.IntN(4) + + useDistortion := rand.Float64() < 0.3 // 30% chance + distortionStrength := rand.Float64() * 0.1 + + log.Debugf("BG theme: %s / Scales: %.4f, %.4f, %.4f / Weights: %.3f, %.3f, %.3f", + theme, scale1, scale2, scale3, weight1, weight2, weight3) + log.Debugf("Gradient: X=%.3f*%.0f, Y=%.3f*%.0f / Combine method: %d / Distortion %t (%.3f)", + gradientStrengthX, gradientDirectionX, gradientStrengthY, gradientDirectionY, + combineMethod, useDistortion, distortionStrength) for y := range config.BannerHeight { for x := range config.BannerWidth { - // multi-octave noise - fx := (float64(x) + offsetX) * scale1 - fy := (float64(y) + offsetY) * scale1 + baseX := float64(x) + offsetX + baseY := float64(y) + offsetY + + // optional distortion for more organic patterns + if useDistortion { + baseX += math.Sin(float64(y)*0.01) * distortionStrength * 100 + baseY += math.Cos(float64(x)*0.01) * distortionStrength * 100 + } + + // multi-octave noise with randomized scales + fx1 := baseX * scale1 + fy1 := baseY * scale1 + fx2 := baseX * scale2 + fy2 := baseY * scale2 + fx3 := baseX * scale3 + fy3 := baseY * scale3 // noise values at different scales - noise1 := perlinNoise.Noise2D(fx, fy) - noise2 := perlinNoise.Noise2D(fx/scale1*scale2, fy/scale1*scale2) - noise3 := perlinNoise.Noise2D(fx/scale1*scale3, fy/scale1*scale3) + noise1 := perlinNoise.Noise2D(fx1, fy1) + noise2 := perlinNoise.Noise2D(fx2, fy2) + noise3 := perlinNoise.Noise2D(fx3, fy3) - // combined noise layers with different weights - combined := noise1*0.6 + noise2*0.3 + noise3*0.1 + // different combination methods for variety + var combined float64 + switch combineMethod { + case 0: // standard linear combination + combined = noise1*weight1 + noise2*weight2 + noise3*weight3 + case 1: // multiplicative blend + combined = (noise1*weight1)*(1+noise2*weight2)*(1+noise3*weight3) - 1 + case 2: // maximum blend (creates sharper patterns) + values := []float64{noise1 * weight1, noise2 * weight2, noise3 * weight3} + combined = math.Max(math.Max(values[0], values[1]), values[2]) + case 3: // turbulence (abs. values create more chaotic patterns) + combined = math.Abs(noise1)*weight1 + math.Abs(noise2)*weight2 + math.Abs(noise3)*weight3 + } - // position-based gradient for a more dynamic look - gradientX := float64(x) / float64(config.BannerWidth) * 0.2 - gradientY := float64(y) / float64(config.BannerHeight) * 0.1 + // randomized gradient with variable direction and strength + gradientX := (float64(x) / float64(config.BannerWidth)) * gradientStrengthX * gradientDirectionX + gradientY := (float64(y) / float64(config.BannerHeight)) * gradientStrengthY * gradientDirectionY // combined and normalized to [0, 1] range noise := (combined + gradientX + gradientY + 1) / 2 + + // clamp to valid range if noise < 0 { noise = 0 }