我正在尝试使用 OpenSimplex 噪声 生成随机的恒星场,但我注意到出现了重复的模式,现在我已经注意到了它,我无法停止注意到它。我想我已经找到了解决方法,但我仍然想知道为什么会发生这种情况。
目前,我的代码使用 OpenSimplex 噪声生成 2D 灰度图像,然后将低于阈值的每个像素设置为 0,因此只留下一小部分所谓随机的“星星”。
我目前正在使用这个包来实现噪声:https://github.com/ojrac/opensimplex-go
package main
import (
"flag"
"fmt"
"image"
"image/color"
"image/png"
"math/rand"
"os"
"time"
"github.com/ojrac/opensimplex-go"
)
const (
startupMessage = "starfieldtest"
)
var (
flgSize int
flgSeed int64
flgCullThreshold float64
)
func init() {
rand.Seed(time.Now().UnixNano())
seed := rand.Int63()
flag.IntVar(&flgSize, "size", 2048, "size of the starfield")
flag.Int64Var(&flgSeed, "seed", seed, "random noise seed value")
flag.Float64Var(&flgCullThreshold, "cull", 0.998, "normalised threshold to cull values below")
}
func main() {
fmt.Println(startupMessage)
flag.Parse()
img := generate(flgSize, flgSeed, float32(flgCullThreshold))
outName := fmt.Sprintf("generated_%s.png", time.Now().Format("20060102150405"))
pngFile, err := os.OpenFile(outName, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer pngFile.Close()
if err = png.Encode(pngFile, img); err != nil {
panic(err)
}
fmt.Printf("Done!\n")
}
func generate(size int, seed int64, threshold float32) *image.Gray {
noise := opensimplex.NewNormalized32(seed)
pix := image.NewGray(image.Rect(0, 0, size, size))
for y := 0; y < size; y++ {
for x := 0; x < size; x++ {
v := noise.Eval2(float32(x), float32(y))
if v < threshold {
v = 0
}
pix.SetGray(x, y, color.Gray{Y: uint8(v * 255.0)})
}
}
return pix
}
我认为我可以通过使用
Eval3
函数并在一定数量的像素后改变深度来解决这个问题,但这是预期的行为,因为它只是真正的伪随机,还是 OpenSimplex 噪声不应该这样做?我真的找不到任何有知识的人对这是否只是伪随机噪声的限制或实现的问题发表任何声明。
编辑:当我改变深度时,这种情况仍然会发生,似乎重复的模式存在于噪声的所有维度上。然而,这条线确实有效(当我摆脱维度并将其视为一长条包裹的像素线时):
v := noise.Eval2((float32(size)*float32(y))+float32(x), 0)
似乎这不是屏幕截图上唯一的重复图案。
根据我自己的经验回想一下,OpenSimplexNoise 仅存在于某些坐标之间。当您开始请求超出这些边界的点的值时,它会简单地回绕并为您提供与以前相同的值。
看起来总共有 8 次重复,所以我猜测,这个 OpenSimplexNoise 实现的上限是 2048(像素数)/ 8 = 256。但老实说,我记得它是 0.0-1.0。