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
This commit is contained in:
ae 2025-06-01 18:36:24 +03:00
parent 04d6693c37
commit fb8c0f12d5
Signed by: ae
GPG Key ID: 995EFD5C1B532B3E

View File

@ -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
}