柏林噪声分形&幻想大陆地图生成

序言

之前介绍过perlin噪声的实现,现在应用实践一下——程序化生成幻想大陆
这里使用的是perlin噪声倍频技术(也称分形噪声),详情传送门:柏林噪声算法
代码示例使用的是shadertoy的语法规则,shandertoy传送门:ShaderToy

示例

柏林噪声分形&幻想大陆地图生成

#define amp 1.9 #define fre 1. #define oct 5.  #define laun 2. #define pers 0.8  #define zoom 5.  #define edge 1.0 #define delta_edge .2  #define snow        vec3(.9, .9, .9) #define mountains   vec3(.4, .4, .2) #define hills       vec3(.6, .6, .1) #define plain       vec3(.1, .8, .2) #define beach       vec3(.8, .8, .1) #define shallow_sea vec3(.1, .1, .9) #define deep_sea    vec3(.1, .1, .6)  #define v_snow =       .95 #define v_mountains    .90 #define v_hills        .80 #define v_plain        .70 #define v_beach        .55 #define v_shallow_sea  .50 #define v_deep_sea     .30  float rand(vec2 p){     return fract(sin(dot(p ,vec2(12.9898,78.233))) * 43758.5453); }  float noise(vec2 x) {     vec2 i = floor(x);     vec2 f = fract(x);      float a = rand(i);     float b = rand(i + vec2(1.0, 0.0));     float c = rand(i + vec2(0.0, 1.0));     float d = rand(i + vec2(1.0, 1.0));     vec2 u = f * f * f * (f * (f * 6. - 15.) + 10.);      float x1 = mix(a,b,u.x);     float x2 = mix(c,d,u.x);     return mix(x1,x2,u.y); }   void mainImage( out vec4 fragColor, in vec2 fragCoord ) { 	vec2 uv = (fragCoord.xy-0.5 * iResolution.xy) / iResolution.y;      vec2 u = fragCoord.xy / iResolution.xy;      float d = min(min(u.x, edge - u.x), min(u.y, edge - u.y));      float dw = smoothstep(0.0, delta_edge, d);          float val = .0;     uv *= zoom;           for(float i = 0.; i < oct; i++)     {         float a = amp * pow(pers, i);         float f = fre * pow(laun, i);         val += a * noise(uv * f) / oct;     }          val *= dw;          vec3 col = vec3(0.);     if (val < v_deep_sea)         col = deep_sea;              if (val >= v_deep_sea && val < v_shallow_sea)         col = shallow_sea;              if (val >= v_shallow_sea && val < v_beach)         col = beach;              if (val >= v_beach && val < v_plain)         col = plain;              if (val >= v_plain && val < v_hills )         col = hills ;              if (val >= v_hills && val < v_mountains)         col = mountains;              if (val >= v_mountains)         col = snow;               fragColor = vec4(col, 0.); }  

思路

生成地形轮廓

地形轮廓的生成主要依靠噪声,来看倍频相关代码(for迭代那部分)相关参数
主要参数

  • frequency 频率
  • amplitude 振幅
  • octave 八度,即迭代次数

相信相关三角函数都学过,就不赘述了
辅助参数
柏林噪声分形&amp;幻想大陆地图生成

  • lacunarity 隙度,修饰频率,使得频率随每个八度以指数增长
  • persistent 持久度,与隙度类似

使用上述代码的参数,随着迭代,每次迭代叠加的细节越来越多(频率更高),但影响越来越小(振幅更小),具象一点的比喻就像:第一次迭代产生山峰的轮廓,第二次迭代产生山峰上巨石的轮廓,第三次迭代产生小石头等的轮廓...

雕刻大陆

经过第一步我们的每一个uv都可以得到一个噪声值,因为噪声值是连续的,可以定义连续的区间为某个地形,这样产生的地形也一定是连续的。比如我把[-∞,0.5)区间定义为海洋,[0.5, 0.55)定义为沙滩等,如代码那一堆地形相关的define。接下来就是不断调整参数,使其参数在合理的区间变化(合理是指生成的大陆符合你的逻辑或审美),由于参数较多且关联,虽然在一定区间内有些规律可循,还是有点难以预料,我称之为——赛博炼丹。

大陆边缘处理

我们生成的是一片完整的大陆,边缘当然得是海!让生成的噪声乘以一个权重,改该权重在图片边缘部分的一个区间内递减,这里是delta_edge = 0.2的边缘区间,如上述代码d和dw的计算。

附录

简化版perlin噪声

柏林噪声分形&amp;幻想大陆地图生成

float rand(vec2 p){     return fract(sin(dot(p ,vec2(12.9898,78.233))) * 43758.5453); }  float noise(vec2 x) {     vec2 i = floor(x);     vec2 f = fract(x);      float a = rand(i);     float b = rand(i + vec2(1.0, 0.0));     float c = rand(i + vec2(0.0, 1.0));     float d = rand(i + vec2(1.0, 1.0));     vec2 u = f * f * f * (f * (f * 6. - 15.) + 10.);      float x1 = mix(a,b,u.x);     float x2 = mix(c,d,u.x);     return mix(x1,x2,u.y); }   void mainImage( out vec4 fragColor, in vec2 fragCoord ) { 	vec2 uv = (fragCoord.xy-0.5 * iResolution.xy) / iResolution.y;      uv *= 4.;     float val = noise(uv.xy) ;     fragColor = vec4(val); }  

分形噪声

柏林噪声分形&amp;幻想大陆地图生成

#define amp 1.9 #define fre 1. #define oct 5.  #define laun 2. #define pers 0.8  #define zoom 5.  float rand(vec2 p){     return fract(sin(dot(p ,vec2(12.9898,78.233))) * 43758.5453); }  float noise(vec2 x) {     vec2 i = floor(x);     vec2 f = fract(x);      float a = rand(i);     float b = rand(i + vec2(1.0, 0.0));     float c = rand(i + vec2(0.0, 1.0));     float d = rand(i + vec2(1.0, 1.0));     vec2 u = f * f * f * (f * (f * 6. - 15.) + 10.);      float x1 = mix(a,b,u.x);     float x2 = mix(c,d,u.x);     return mix(x1,x2,u.y); }   void mainImage( out vec4 fragColor, in vec2 fragCoord ) { 	vec2 uv = (fragCoord.xy-0.5 * iResolution.xy) / iResolution.y;            float val = .0;     uv *= zoom;           for(float i = 0.; i < oct; i++)     {         float a = amp * pow(pers, i);         float f = fre * pow(laun, i);         val += a * noise(uv * f) / oct;     }          vec3 col = vec3(val);      fragColor = vec4(col, 0.); } 

发表评论

评论已关闭。

相关文章