//-mwindows -lgdi32
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#define WIDTH 1200
#define HEIGHT 800
#define OCTAVES 8
#define PERSIST 0.55f
#define LACUNARITY 2.0f
float seedOffset;
// 按钮ID
#define IDC_GENERATE_BUTTON 1001
// 全局窗口句柄
HWND hGenerateButton;
static int perm[512] = {
151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,
8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,
35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,
134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,
55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,
18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,
189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,
172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,
228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,
107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,
8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,
35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,
134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,
55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,
18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,
189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,
172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,
228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,
107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
float fade(float t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
float lerp(float t, float a, float b) {
return a + t * (b - a);
}
float grad(int hash, float x, float y) {
int h = hash & 15;
float u = h < 8 ? x : y;
float v = h < 4 ? y : (h == 12 || h == 14 ? x : 0);
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
float perlin2d(float x, float y) {
x += seedOffset;
y += seedOffset;
int X = (int)floor(x) & 255;
int Y = (int)floor(y) & 255;
x -= floor(x);
y -= floor(y);
float u = fade(x);
float v = fade(y);
int A = perm[X] + Y;
int AA = perm[A];
int AB = perm[A + 1];
int B = perm[X + 1] + Y;
int BA = perm[B];
int BB = perm[B + 1];
return lerp(v,
lerp(u, grad(perm[AA], x, y),
grad(perm[BA], x-1, y)),
lerp(u, grad(perm[AB], x, y-1),
grad(perm[BB], x-1, y-1)));
}
float fbm(float x, float y) {
float total = 0;
float amp = 1;
float freq = 1;
float max = 0;
for (int i = 0; i < OCTAVES; i++) {
total += perlin2d(x * freq, y * freq) * amp;
max += amp;
amp *= PERSIST;
freq *= LACUNARITY;
}
return total / max;
}
// 海洋 ≈ 65%,地形过渡更自然
COLORREF GetBiomeColor(float h) {
if (h < 0.41f) return RGB(25, 55, 110); // 深海
else if (h < 0.50f) return RGB(38, 95, 170); // 海洋
else if (h < 0.52f) return RGB(235, 218, 138); // 沙滩
else if (h < 0.57f) return RGB(155, 195, 98); // 平原
else if (h < 0.66f) return RGB(55, 135, 55); // 森林
else if (h < 0.74f) return RGB(218, 198, 98); // 沙漠
else if (h < 0.84f) return RGB(115, 115, 115); // 石头山
else return RGB(245, 245, 255); // 雪山
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
int x, y;
float height;
switch (msg) {
case WM_CREATE:
// 创建按钮
hGenerateButton = CreateWindow(
"BUTTON",
"重新生成地形",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
10, 10, 120, 35,
hwnd,
(HMENU)IDC_GENERATE_BUTTON,
((LPCREATESTRUCT)lParam)->hInstance,
NULL
);
break;
case WM_COMMAND:
// 按钮点击事件
if (LOWORD(wParam) == IDC_GENERATE_BUTTON) {
// 重新生成随机种子
srand((unsigned)time(NULL));
seedOffset = (float)(rand() % 20000) / 100.0f;
// 强制重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
for (y = 0; y < HEIGHT; y++) {
for (x = 0; x < WIDTH; x++) {
height = fbm((float)x / 220.0f, (float)y / 220.0f);
height = (height + 1.0f) * 0.5f;
SetPixel(hdc, x, y, GetBiomeColor(height));
}
}
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR cmd, int nShow) {
srand((unsigned)time(NULL));
seedOffset = (float)(rand() % 20000) / 100.0f;
const char CLASS_NAME[] = "PerlinMap";
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "自然地形 - 点击按钮重新生成",
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT,
NULL, NULL, hInstance, NULL
);
if (!hwnd) return 0;
ShowWindow(hwnd, nShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}