著名的柏林噪聲在數學、物理學、計算機圖形學中都很有名氣,是生成有一定規律的隨機圖形的一種理論依據。現在,很多遊戲中隨機地圖的生成都有柏林噪聲的貢獻。
![如何生成有趣的柏林噪聲 - 遊戲中隨機地圖的算法基礎](http://p2.ttnews.xyz/loading.gif)
本文將以一個有趣的生成隨機圖片的例子裡演示一下有趣的柏林噪聲直觀化的結果。
![如何生成有趣的柏林噪聲 - 遊戲中隨機地圖的算法基礎](http://p2.ttnews.xyz/loading.gif)
例子代碼使用了Go語言(Golang)來生成柏林噪聲(Perlin Noise)並繪製對應隨機的圖形。柏林噪聲算法被廣泛用於生成遊戲或影視動畫中的地圖、各種波形紋理效果等,代碼中做了必要的註釋,大家可以看看通過該算法生成的隨機圖像能夠達到什麼效果。
package main
import (
"image"
"image/color"
"image/png"
"math"
"math/rand"
"net/http"
"time"
)
// 下面是一組生成柏林噪聲圖像有關的算法函數
// 根據算法生成噪聲,加入隨機數種子帶來更多的隨機性
func noise(x, y int64, seedA int64) float64 {
n := x + y*57 + seedA*7
m := (n << 13) ^ n
return (1.0 - float64((m*(m*m*15731+789221)+1376312589)&0x7fffffff)/float64(0x40000000))
}
// 使噪聲平滑一些
func smoothedNoise(x float64, y float64, seedA int64) float64 {
xInt := int64(math.Trunc(x))
yInt := int64(math.Trunc(y))
cornersT := (noise(xInt-1, yInt-1, seedA) + noise(xInt+1, yInt-1, seedA) + noise(xInt-1, yInt+1, seedA) + noise(xInt+1, yInt+1, seedA)) / 16
sidesT := (noise(xInt-1, yInt, seedA) + noise(xInt+1, yInt, seedA) + noise(xInt, yInt-1, seedA) + noise(xInt, yInt+1, seedA)) / 8
centerT := noise(xInt, yInt, seedA) / 4
return cornersT + sidesT + centerT
}
// 用於調整的函數
func interpolate(a, b, x float64) float64 {
c := x * math.Pi
d := (1 - math.Cos(c)) * 0.5
return a*(1-d) + b*d
}
// 進一步調整噪聲
func interpolatedNoise(x, y float64, seedA int64) float64 {
xInt := math.Trunc(x)
xFrac := x - xInt
yInt := math.Trunc(y)
yFrac := y - yInt
v1 := smoothedNoise(xInt, yInt, seedA)
v2 := smoothedNoise(xInt+1, yInt, seedA)
v3 := smoothedNoise(xInt, yInt+1, seedA)
v4 := smoothedNoise(xInt+1, yInt+1, seedA)
i1 := interpolate(v1, v2, xFrac)
i2 := interpolate(v3, v4, xFrac)
return interpolate(i1, i2, yFrac)
}
// PerlinNoise2D 根據指定參數生成二維的柏林噪聲
func PerlinNoise2D(x, y float64, seedA int64, alphaA float64, betaA float64, octavesA int) float64 {
// 存放結果的變量
resultT := 0.0
// 放大係數
scaleT := 1.0
// 循環調整octavesA次
for i := 0; i < octavesA; i++ {
resultT += interpolatedNoise(x, y, seedA) / scaleT
// *= 操作符與 += 類似,表示 scaleT = scaleT * alphaA
scaleT *= alphaA
x *= betaA
y *= betaA
}
return resultT
}
// GetPerlinNoise2DColor 用於計算座標為 (x, y)點的顏色
func GetPerlinNoise2DColor(x, y float64, alphaA, betaA float64, octavesA int, seedA int64, currentColorA color.Color) color.Color {
// 先計算噪聲(即變化量)
noiseT := PerlinNoise2D(x, y, seedA, alphaA, betaA, octavesA)
// 獲取當前顏色,準備進行改變
r, g, b, a := currentColorA.RGBA()
r = r % 256
g = g % 256
b = b % 256
a = a % 256
// 放大係數
scaleT := 9.1
// 用取模法隨機確定修改RGB三原色中的哪個
rgbSelectorT := int(noiseT*scaleT/100) % 3
// 根據選擇來對某一種原色進行變化
switch rgbSelectorT {
case 0:
r = r + uint32(noiseT*scaleT)
r = r % 256
case 1:
g = g + uint32(noiseT*scaleT)
g = g % 256
case 2:
b = b + uint32(noiseT*scaleT)
b = b % 256
}
return &color.RGBA{byte(r), byte(g), byte(b), byte(a)}
}
// 處理根路徑請求的函數,將返回一個隨機圖片
func handleImage(respA http.ResponseWriter, reqA *http.Request) {
// 設置圖片的大小
widthT := 480.0
heightT := 360.0
// 設置生成柏林噪聲的參數,再加入一些隨機性
alphaT := 0.31 + rand.Float64()*0.2 - 0.1
betaT := 0.22 + rand.Float64()*0.2 - 0.1
octavesT := 3 + rand.Intn(10)
seedT := rand.Int()
// 準備新的圖片
imageT := image.NewNRGBA(image.Rect(0, 0, int(widthT), int(heightT)))
// 準備存放不斷變化顏色的colorT變量,並確定一個初始顏色startColorT
var colorT color.Color
colorT = color.RGBA{byte(rand.Int() % 256), byte(rand.Int() % 256), byte(rand.Int() % 256), 0xFF}
startColorT := colorT
// 循環計算並設置每個點的顏色
for i := 0.0; i < heightT; i++ {
for j := 0.0; j < widthT; j++ {
// 變化的顏色根據柏林噪聲基於初始顏色進行變化
colorT = GetPerlinNoise2DColor(j, i, alphaT, betaT, octavesT, int64(seedT), startColorT)
// 設置點的顏色
imageT.Set(int(j), int(i), colorT)
}
}
// 寫入網頁響應頭中的內容類型,表示是png格式的圖片
respA.Header().Set("Content-Type", "image/png")
// 進行png格式的圖形編碼,並以流式方法寫入http響應中
png.Encode(respA, imageT)
}
func main() {
// 初始化隨機數種子
rand.Seed(time.Now().Unix())
// 設定根路由處理函數
http.HandleFunc("/", handleImage)
// 在指定端口上監聽
http.ListenAndServe(":8837", nil)
}
代碼 15‑17 image6/image6.go
代碼 15‑17運行後,用瀏覽器訪問本機8837端口可以看到生成的圖片,不斷刷新頁面可以看到每次都會生成不一樣的隨機圖片,下面是幾個生成的圖片示例。
圖 15.6 用柏林噪聲生成的隨機圖片
圖 15.7 用柏林噪聲生成的隨機圖片
圖 15.8 用柏林噪聲生成的隨機圖片
圖 15.9 用柏林噪聲生成的隨機圖片
圖 15.10 用柏林噪聲生成的隨機圖片
柏林噪聲算法中,使用了多個因子來控制圖形的生成,可以達到色彩上、密度上、聚簇顆粒度等精細的控制並保證足夠的隨機性,深入的應用我們找機會再細說。
閱讀更多 陸滿庭 的文章