0%

程序化内容生成(PCG)

本篇博客灵感来源于https://www.voidgame.space/articles/Voidmatrix/pcg-game-map-1/

fmod(sin(x 12.9898 + y 78.233) * 43758.5453, 1.0);
这个表达式的作用是将一个二维点 (x, y) 映射到一个介于 0 到 1 之间的伪随机值

柏林噪声

生成基础噪声值

1
2
3
4
5
double noise(int x, int y)
{
return fmod(sin(x * 12.9898 + y * 78.233) * 43758.5453, 1.0);
//这个表达式的作用是将一个二维点 (x, y) 映射到一个介于 0 到 1 之间的伪随机值
}

生成平滑噪声值

1
2
3
4
5
6
7
8
double smooth_noise(int x, int y)
{
// 计算角落、边和中心的平均噪声值
double corners = (noise(x - 1, y - 1) + noise(x + 1, y - 1) + noise(x - 1, y + 1) + noise(x + 1, y + 1)) / 16;
double sides = (noise(x - 1, y) + noise(x + 1, y) + noise(x, y - 1) + noise(x, y + 1)) / 8;
double center = noise(x, y) / 4;
// 返回平滑噪声值
return corners + sides + center;

插值公式

线性插值

1
2
3
4
5
6
7
8
9
10
11
//在两个值 a 和 b 之间进行插值,插值的程度由参数 x 控制
double interpolate(double a, double b, double x)
{
// 计算插值权重
double ft = x * 3.1415927;//映射范围在 [0, 1] 的 x 值到一个角度范围内
double f = (1 - cos(ft)) * 0.5;
//当 x 接近 0 或 1 时,f 的值趋近于 0,而当 x 接近 0.5 时,f 的值趋近于 1

// 返回插值结果
return a * (1 - f) + b * f;
}

插值计算

这里指双线插值(把在X方向进行两次线性插值得到的结果再在Y方向进行一次线性插值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
double interpolated_noise(double x, double y)
{
// 提取整数部分和小数部分
int integer_X = static_cast<int>(x);
double fractional_X = x - integer_X;

int integer_Y = static_cast<int>(y);
double fractional_Y = y - integer_Y;

// 计算插值点的平滑噪声值(在X方向进行两次线性插值)
double v1 = smooth_noise(integer_X, integer_Y);
double v2 = smooth_noise(integer_X + 1, integer_Y);
double v3 = smooth_noise(integer_X, integer_Y + 1);
double v4 = smooth_noise(integer_X + 1, integer_Y + 1);

//X方向
double i1 = interpolate(v1, v2, fractional_X);
double i2 = interpolate(v3, v4, fractional_X);

//Y方向
// 返回插值后的噪声值(在Y方向上将X方向插值的结果进行插值)
return interpolate(i1, i2, fractional_Y);
}

主循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int main()
{
const int width = 1280;
const int height = 720;

initgraph(width, height);

// 循环生成每个像素点的噪声值并转换为灰度颜色
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// 生成插值噪声,并控制噪声的规模
double value = interpolated_noise(x / 50.0, y / 50.0);//50.0可以控制噪声的规模
// 将噪声值转换为灰度颜色
COLORREF color = RGB((int)(value * 255),//将噪声值映射到灰度范围
(int)(value * 255), (int)(value * 255));//使用该灰度值来设置RGB颜色的三个分量,使得它们相等

// 在指定位置绘制像素
putpixel(x, y, color);
}
}

system("pause");

return 0;
}