如何生成有趣的柏林噪声 - 游戏中随机地图的算法基础

著名的柏林噪声在数学、物理学、计算机图形学中都很有名气,是生成有一定规律的随机图形的一种理论依据。现在,很多游戏中随机地图的生成都有柏林噪声的贡献。

本文将以一个有趣的生成随机图片的例子里演示一下有趣的柏林噪声直观化的结果。

例子代码使用了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 用柏林噪声生成的随机图片

柏林噪声算法中,使用了多个因子来控制图形的生成,可以达到色彩上、密度上、聚簇颗粒度等精细的控制并保证足够的随机性,深入的应用我们找机会再细说。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容