From 17c7ad989dd5ec20807dcd198801b66c3fa4c705 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Fri, 22 May 2026 17:16:15 +0800 Subject: [PATCH] refactor: use fBM for heightmap generation --- include/Cubed/gameplay/biome.hpp | 2 +- include/Cubed/gameplay/chunk.hpp | 13 +++- include/Cubed/tools/perlin_noise.hpp | 17 +++++- src/gameplay/builders/biome_builder.cpp | 4 +- src/gameplay/builders/desert_builder.cpp | 4 +- src/gameplay/builders/forest_builder.cpp | 6 +- src/gameplay/builders/mountain_builder.cpp | 10 +-- src/gameplay/builders/plain_builder.cpp | 6 +- src/gameplay/builders/river_builder.cpp | 10 +-- src/gameplay/chunk.cpp | 44 +++++++++++--- src/gameplay/chunk_generator.cpp | 57 +++++++++++++---- src/gameplay/tree.cpp | 6 +- src/gameplay/world.cpp | 27 ++++---- src/tools/perlin_noise.cpp | 71 +++++++++++++++++++--- 14 files changed, 208 insertions(+), 69 deletions(-) diff --git a/include/Cubed/gameplay/biome.hpp b/include/Cubed/gameplay/biome.hpp index 604ba94..8e1fcb7 100644 --- a/include/Cubed/gameplay/biome.hpp +++ b/include/Cubed/gameplay/biome.hpp @@ -6,7 +6,7 @@ namespace Cubed { constexpr float BIOME_NOISE_FREQUENCY = 0.03f; - +constexpr float HEIGHTMAP_NOISE_FREQUENCY = 0.001f; enum class BiomeType { PLAIN = 0, FOREST, DESERT, MOUNTAIN, RIVER, NONE }; struct BiomeHeightRange { diff --git a/include/Cubed/gameplay/chunk.hpp b/include/Cubed/gameplay/chunk.hpp index f119bb9..197122b 100644 --- a/include/Cubed/gameplay/chunk.hpp +++ b/include/Cubed/gameplay/chunk.hpp @@ -48,12 +48,21 @@ public: Chunk(Chunk&&) noexcept; Chunk& operator=(Chunk&&) noexcept; + static std::tuple world_to_block(int world_x, int world_y, + int world_z, int chunk_x, + int chunk_z); + static std::tuple world_to_block(const glm::ivec3& block_pos, + ChunkPos chunk_pos); + static std::tuple block_to_world(int x, int y, int z, + int chunk_x, int chunk_z); + static std::tuple block_to_world(const glm::ivec3& block_pos, + ChunkPos chunk_pos); BiomeType get_biome() const; ChunkPos get_chunk_pos() const; const std::vector& get_chunk_blocks() const; HeightMapArray get_heightmap() const; - static int get_index(int x, int y, int z); - static int get_index(const glm::vec3& pos); + static int index(int x, int y, int z); + static int index(const glm::vec3& pos); // Init Chunk // Determine biome from temperature and humidity noise void gen_phase_one(); diff --git a/include/Cubed/tools/perlin_noise.hpp b/include/Cubed/tools/perlin_noise.hpp index 078eef9..9a0e6a3 100644 --- a/include/Cubed/tools/perlin_noise.hpp +++ b/include/Cubed/tools/perlin_noise.hpp @@ -4,7 +4,7 @@ namespace Cubed { -class PerlinNoise { +class PerlinNoise3D { public: static void init(unsigned seed); static float noise(float x, float y, float z); @@ -18,4 +18,19 @@ private: static float grad(int hash, float x, float y, float z); }; +class PerlinNoise2D { +public: + static void init(unsigned seed); + static float noise(float x, float y); + static void reload(unsigned seed); + +private: + static inline std::atomic is_init = false; + static inline std::vector p; + + static float fade(float t); + static float lerp(float t, float a, float b); + static float grad(int hash, float x, float y); +}; + } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/builders/biome_builder.cpp b/src/gameplay/builders/biome_builder.cpp index e9772ea..6582982 100644 --- a/src/gameplay/builders/biome_builder.cpp +++ b/src/gameplay/builders/biome_builder.cpp @@ -10,7 +10,7 @@ void BiomeBuilder::build_bottom() { for (int x = 0; x < CHUNK_SIZE; x++) { for (int y = 0; y < 5; y++) { for (int z = 0; z < CHUNK_SIZE; z++) { - m_blocks[Chunk::get_index(x, y, z)] = 3; + m_blocks[Chunk::index(x, y, z)] = 3; } } } @@ -30,7 +30,7 @@ void BiomeBuilder::fill_water() { } int height = heightmap[x][z]; for (int y = height; y < SEA_LEVEL; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 7; + m_blocks[Chunk::index(x, y, z)] = 7; } } } diff --git a/src/gameplay/builders/desert_builder.cpp b/src/gameplay/builders/desert_builder.cpp index bb33e9f..25aefe0 100644 --- a/src/gameplay/builders/desert_builder.cpp +++ b/src/gameplay/builders/desert_builder.cpp @@ -19,11 +19,11 @@ void DesertBuilder::build_blocks() { for (int z = 0; z < CHUNK_SIZE; z++) { int height = static_cast(m_heightmap[x][z]); for (int y = 5; y < height - 5; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 3; + m_blocks[Chunk::index(x, y, z)] = 3; } for (int y = height - 5; y <= height; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 4; + m_blocks[Chunk::index(x, y, z)] = 4; } } } diff --git a/src/gameplay/builders/forest_builder.cpp b/src/gameplay/builders/forest_builder.cpp index 6df931d..34eda3f 100644 --- a/src/gameplay/builders/forest_builder.cpp +++ b/src/gameplay/builders/forest_builder.cpp @@ -23,12 +23,12 @@ void ForestBuilder::build_blocks() { for (int z = 0; z < CHUNK_SIZE; z++) { int height = static_cast(m_heightmap[x][z]); for (int y = 5; y < height - 5; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 3; + m_blocks[Chunk::index(x, y, z)] = 3; } for (int y = height - 5; y < height; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 2; + m_blocks[Chunk::index(x, y, z)] = 2; } - m_blocks[Chunk::get_index(x, height, z)] = 1; + m_blocks[Chunk::index(x, height, z)] = 1; } } } diff --git a/src/gameplay/builders/mountain_builder.cpp b/src/gameplay/builders/mountain_builder.cpp index 22f06b0..9ec08d8 100644 --- a/src/gameplay/builders/mountain_builder.cpp +++ b/src/gameplay/builders/mountain_builder.cpp @@ -19,19 +19,19 @@ void MountainBuilder::build_blocks() { for (int z = 0; z < CHUNK_SIZE; z++) { int height = static_cast(m_heightmap[x][z]); for (int y = 5; y < height - 5; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 3; + m_blocks[Chunk::index(x, y, z)] = 3; } for (int y = height - 5; y <= height - 1; y++) { if (y > 110) { - m_blocks[Chunk::get_index(x, y, z)] = 3; + m_blocks[Chunk::index(x, y, z)] = 3; } else { - m_blocks[Chunk::get_index(x, y, z)] = 2; + m_blocks[Chunk::index(x, y, z)] = 2; } } if (height > 110) { - m_blocks[Chunk::get_index(x, height, z)] = 3; + m_blocks[Chunk::index(x, height, z)] = 3; } else { - m_blocks[Chunk::get_index(x, height, z)] = 1; + m_blocks[Chunk::index(x, height, z)] = 1; } } } diff --git a/src/gameplay/builders/plain_builder.cpp b/src/gameplay/builders/plain_builder.cpp index 4cc6186..6c20fbd 100644 --- a/src/gameplay/builders/plain_builder.cpp +++ b/src/gameplay/builders/plain_builder.cpp @@ -19,12 +19,12 @@ void PlainBuilder::build_blocks() { for (int z = 0; z < CHUNK_SIZE; z++) { int height = static_cast(m_heightmap[x][z]); for (int y = 5; y < height - 5; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 3; + m_blocks[Chunk::index(x, y, z)] = 3; } for (int y = height - 5; y < height; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 2; + m_blocks[Chunk::index(x, y, z)] = 2; } - m_blocks[Chunk::get_index(x, height, z)] = 1; + m_blocks[Chunk::index(x, height, z)] = 1; } } } diff --git a/src/gameplay/builders/river_builder.cpp b/src/gameplay/builders/river_builder.cpp index 9d9a807..3e7fa6e 100644 --- a/src/gameplay/builders/river_builder.cpp +++ b/src/gameplay/builders/river_builder.cpp @@ -19,16 +19,16 @@ void RiverBuilder::build_blocks() { for (int z = 0; z < CHUNK_SIZE; z++) { int height = static_cast(m_heightmap[x][z]); for (int y = 5; y < height - 5; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 3; + m_blocks[Chunk::index(x, y, z)] = 3; } for (int y = height - 5; y <= height - 1; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 2; + m_blocks[Chunk::index(x, y, z)] = 2; } for (int y = height; y <= height; y++) { if (y >= SEA_LEVEL - 1) { - m_blocks[Chunk::get_index(x, y, z)] = 1; + m_blocks[Chunk::index(x, y, z)] = 1; } else { - m_blocks[Chunk::get_index(x, y, z)] = 2; + m_blocks[Chunk::index(x, y, z)] = 2; } } } @@ -46,7 +46,7 @@ void RiverBuilder::build_vegetation() { continue; } for (int y = height + 1; y < SEA_LEVEL; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 7; + m_blocks[Chunk::index(x, y, z)] = 7; } } } diff --git a/src/gameplay/chunk.cpp b/src/gameplay/chunk.cpp index ab8c272..0b406bc 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -47,6 +47,35 @@ Chunk& Chunk::operator=(Chunk&& other) noexcept { return *this; } +std::tuple Chunk::world_to_block(int world_x, int world_y, + int world_z, int chunk_x, + int chunk_z) { + int x, y, z; + y = world_y; + x = world_x - chunk_x * CHUNK_SIZE; + z = world_z - chunk_z * CHUNK_SIZE; + return {x, y, z}; +} + +std::tuple Chunk::world_to_block(const glm::ivec3& block_pos, + ChunkPos chunk_pos) { + return world_to_block(block_pos.x, block_pos.y, block_pos.z, chunk_pos.x, + chunk_pos.z); +} + +std::tuple Chunk::block_to_world(int x, int y, int z, + int chunk_x, int chunk_z) { + int world_x = x + chunk_x * CHUNK_SIZE; + int world_z = z + chunk_z * CHUNK_SIZE; + int world_y = y; + return {world_x, world_y, world_z}; +} +std::tuple Chunk::block_to_world(const glm::ivec3& block_pos, + ChunkPos chunk_pos) { + return block_to_world(block_pos.x, block_pos.y, block_pos.z, chunk_pos.x, + chunk_pos.z); +} + BiomeType Chunk::get_biome() const { return m_biome.load(); } ChunkPos Chunk::get_chunk_pos() const { return m_chunk_pos; } @@ -61,7 +90,7 @@ HeightMapArray Chunk::get_heightmap() const { return m_heightmap; } -int Chunk::get_index(int x, int y, int z) { +int Chunk::index(int x, int y, int z) { ASSERT(!(x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y || z >= CHUNK_SIZE)); if ((x * WORLD_SIZE_Y + y) * CHUNK_SIZE + z < 0 || @@ -73,8 +102,8 @@ int Chunk::get_index(int x, int y, int z) { return (x * WORLD_SIZE_Y + y) * CHUNK_SIZE + z; } -int Chunk::get_index(const glm::vec3& pos) { - return Chunk::get_index(pos.x, pos.y, pos.z); +int Chunk::index(const glm::vec3& pos) { + return Chunk::index(pos.x, pos.y, pos.z); } void Chunk::gen_vertex_data( @@ -95,7 +124,7 @@ void Chunk::gen_vertex_data( int world_x = x + m_chunk_pos.x * CHUNK_SIZE; int world_z = z + m_chunk_pos.z * CHUNK_SIZE; int world_y = y; - int cur_id = m_blocks[get_index(x, y, z)]; + int cur_id = m_blocks[index(x, y, z)]; // air if (cur_id == 0) { continue; @@ -132,7 +161,7 @@ void Chunk::gen_vertex_data( return false; } - int idx = Chunk::get_index(x, y, z); + int idx = Chunk::index(x, y, z); // not init if (static_cast(idx) >= chunk_blocks->size()) { @@ -164,7 +193,7 @@ void Chunk::gen_vertex_data( // neighbor_cull = m_world.is_block(glm::ivec3(world_x, // world_y, world_z) + DIR[face]); } else { - auto id = m_blocks[get_index(nx, ny, nz)]; + auto id = m_blocks[index(nx, ny, nz)]; if (!is_in_transparent_map(id)) { neighbor_cull = true; } else { @@ -242,7 +271,8 @@ void Chunk::gen_phase_four( Logger::error("ChunkGenerator is Nullptr"); return; } - m_generator->blend_heightmap_boundaries(neighbor_heightmap, neighbor_biome); + // m_generator->blend_heightmap_boundaries(neighbor_heightmap, + // neighbor_biome); } void Chunk::gen_phase_five() { diff --git a/src/gameplay/chunk_generator.cpp b/src/gameplay/chunk_generator.cpp index 5769268..e56b238 100644 --- a/src/gameplay/chunk_generator.cpp +++ b/src/gameplay/chunk_generator.cpp @@ -27,7 +27,8 @@ void ChunkGenerator::init() { std::random_device d; m_generator_seed = d(); Logger::info("Chunk Generator Seed {}", m_generator_seed); - PerlinNoise::init(m_generator_seed); + PerlinNoise3D::init(m_generator_seed); + PerlinNoise2D::init(m_generator_seed); is_init = true; } @@ -35,7 +36,7 @@ void ChunkGenerator::reload() { if (!is_seed_change) { return; } - PerlinNoise::reload(m_generator_seed); + PerlinNoise3D::reload(m_generator_seed); is_seed_change = false; } @@ -54,10 +55,10 @@ void ChunkGenerator::assign_chunk_biome() { auto m_chunk_pos = m_chunk.chunk_pos(); float x = static_cast(m_chunk_pos.x); float z = static_cast(m_chunk_pos.z); - float temp = PerlinNoise::noise(x * BIOME_NOISE_FREQUENCY, 0.0f, - z * BIOME_NOISE_FREQUENCY); - float humid = PerlinNoise::noise(x * BIOME_NOISE_FREQUENCY, 1.0f, - z * BIOME_NOISE_FREQUENCY); + float temp = PerlinNoise3D::noise(x * BIOME_NOISE_FREQUENCY, 0.0f, + z * BIOME_NOISE_FREQUENCY); + float humid = PerlinNoise3D::noise(x * BIOME_NOISE_FREQUENCY, 1.0f, + z * BIOME_NOISE_FREQUENCY); auto biome = get_biome_from_noise(temp, humid); m_chunk.biome(biome); } @@ -86,6 +87,7 @@ void ChunkGenerator::resolve_biome_adjacency_conflict( } } +/* void ChunkGenerator::generate_heightmap() { auto m_chunk_pos = m_chunk.chunk_pos(); @@ -114,6 +116,37 @@ void ChunkGenerator::generate_heightmap() { } } } +*/ + +void ChunkGenerator::generate_heightmap() { + auto chunk_pos = m_chunk.chunk_pos(); + auto& heightmap = m_chunk.heightmap(); + + for (int x = 0; x < CHUNK_SIZE; ++x) { + for (int z = 0; z < CHUNK_SIZE; ++z) { + float world_x = static_cast(x + chunk_pos.x * CHUNK_SIZE); + float world_z = static_cast(z + chunk_pos.z * CHUNK_SIZE); + + auto fbm_height = [](float x, float y, int octaves, + float lacunarity, float gain, float amplitude, + float frequency) -> float { + float value = 0.0f; + for (int i = 0; i < octaves; i++) { + value += amplitude * + PerlinNoise2D::noise(x * frequency, y * frequency); + frequency *= lacunarity; + amplitude *= gain; + } + return value; + }; + int octaves = 4; + float lacunarity = 2.0f; + float gain = 0.5f; + heightmap[x][z] = 64 + fbm_height(world_x, world_z, octaves, + lacunarity, gain, 40, 0.005f); + } + } +} void ChunkGenerator::blend_heightmap_boundaries( const std::array, 8>& neighbor_heightmap, @@ -379,8 +412,8 @@ void ChunkGenerator::blend_surface_blocks_borders( int nx, int nz) -> BlockType { // Search from topmost y downwards for the first non-zero block for (int y = WORLD_HEIGHT - 1; y >= 0; --y) { - int idx = Chunk::get_index( - nx, y, nz); // linear index: y * area + z * size + x + int idx = Chunk::index(nx, y, + nz); // linear index: y * area + z * size + x if (idx >= 0 && idx < static_cast(blocks.size()) && blocks[idx] != 0) { return blocks[idx]; @@ -396,7 +429,7 @@ void ChunkGenerator::blend_surface_blocks_borders( BlockType type_self = 0; int top_y = -1; top_y = m_heightmap[x][z]; - type_self = m_blocks[Chunk::get_index(x, top_y, z)]; + type_self = m_blocks[Chunk::index(x, top_y, z)]; if (top_y == -1) continue; // no block? skip @@ -470,7 +503,7 @@ void ChunkGenerator::blend_surface_blocks_borders( final_type = 2; } - m_blocks[Chunk::get_index(x, top_y, z)] = final_type; + m_blocks[Chunk::index(x, top_y, z)] = final_type; // bottom block unsigned fill_type = 2; if (final_type == 1) { @@ -479,7 +512,7 @@ void ChunkGenerator::blend_surface_blocks_borders( fill_type = 4; } for (int y = top_y - 5; y < top_y; y++) { - m_blocks[Chunk::get_index(x, y, z)] = fill_type; + m_blocks[Chunk::index(x, y, z)] = fill_type; } } } @@ -564,7 +597,7 @@ void ChunkGenerator::generate_cave() { if (y == 0) { continue; } - blocks[Chunk::get_index(x, y, z)] = 0; + blocks[Chunk::index(x, y, z)] = 0; } } } diff --git a/src/gameplay/tree.cpp b/src/gameplay/tree.cpp index 3aae5be..8a50744 100644 --- a/src/gameplay/tree.cpp +++ b/src/gameplay/tree.cpp @@ -30,7 +30,7 @@ static constexpr std::array TREE{{ bool build_tree(Chunk& chunk, const glm::ivec3& pos) { auto& block = chunk.get_chunk_blocks(); - if (block[Chunk::get_index(pos)] != 1) { + if (block[Chunk::index(pos)] != 1) { Logger::info("Root is not Grass Block"); return false; } @@ -43,13 +43,13 @@ bool build_tree(Chunk& chunk, const glm::ivec3& pos) { z >= CHUNK_SIZE) { return false; } - if (block[Chunk::get_index(tree_node)] != 0) { + if (block[Chunk::index(tree_node)] != 0) { return false; } } for (const auto& d : TREE) { auto tree_node = pos + d.offset; - chunk.set_chunk_block(Chunk::get_index(tree_node), d.id); + chunk.set_chunk_block(Chunk::index(tree_node), d.id); } return true; } diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index 0c5614f..d480614 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -86,6 +86,7 @@ void World::init_chunks() { std::this_thread::sleep_for(std::chrono::microseconds(200)); } } + /* void World::init_chunks() { @@ -332,6 +333,8 @@ ChunkPos World::chunk_pos(int world_x, int world_z) { return {chunk_x, chunk_z}; } +#pragma region ChunkGenerate + void World::gen_chunks_internal() { m_chunk_gen_fraction = 0.0f; m_chunk_gen_finished = false; @@ -597,6 +600,8 @@ void World::build_neighbor_context_for_affected_neighbors( } } +#pragma endregion + void World::start_gen_thread() { m_gen_running = true; Logger::info("Gen Thread Started"); @@ -667,15 +672,12 @@ int World::get_block(const glm::ivec3& block_pos) const { } const auto& chunk_blocks = it->second.get_chunk_blocks(); - int x, y, z; - y = block_pos.y; - x = block_pos.x - chunk_x * CHUNK_SIZE; - z = block_pos.z - chunk_z * CHUNK_SIZE; + auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z}); if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y || z >= CHUNK_SIZE) { return 0; } - return chunk_blocks[Chunk::get_index(x, y, z)]; + return chunk_blocks[Chunk::index(x, y, z)]; } bool World::is_block(const glm::ivec3& block_pos) const { @@ -687,15 +689,12 @@ bool World::is_block(const glm::ivec3& block_pos) const { return false; } const auto& chunk_blocks = it->second.get_chunk_blocks(); - int x, y, z; - y = block_pos.y; - x = block_pos.x - chunk_x * CHUNK_SIZE; - z = block_pos.z - chunk_z * CHUNK_SIZE; + auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z}); if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y || z >= CHUNK_SIZE) { return false; } - auto id = chunk_blocks[Chunk::get_index(x, y, z)]; + auto id = chunk_blocks[Chunk::index(x, y, z)]; if (id == 0) { return false; } else { @@ -718,16 +717,14 @@ void World::set_block(const glm::ivec3& block_pos, unsigned id) { return; } - int x, y, z; - y = world_y; - x = world_x - chunk_x * CHUNK_SIZE; - z = world_z - chunk_z * CHUNK_SIZE; + auto [x, y, z] = + Chunk::world_to_block(world_x, world_y, world_z, chunk_x, chunk_z); if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y || z >= CHUNK_SIZE) { return; } - it->second.set_chunk_block(Chunk::get_index(x, y, z), id); + it->second.set_chunk_block(Chunk::index(x, y, z), id); static const glm::ivec3 NEIGHBOR_DIRS[] = { {1, 0, 0}, {-1, 0, 0}, {0, 0, -1}, {0, 0, 1}}; diff --git a/src/tools/perlin_noise.cpp b/src/tools/perlin_noise.cpp index 114e833..77e7b72 100644 --- a/src/tools/perlin_noise.cpp +++ b/src/tools/perlin_noise.cpp @@ -1,8 +1,6 @@ #include "Cubed/tools/perlin_noise.hpp" -#include "Cubed/config.hpp" #include "Cubed/tools/cubed_assert.hpp" -#include "Cubed/tools/cubed_random.hpp" #include #include @@ -10,7 +8,7 @@ namespace Cubed { -void PerlinNoise::init(unsigned seed) { +void PerlinNoise3D::init(unsigned seed) { p.resize(256); std::iota(p.begin(), p.end(), 0); Logger::info("Init Perlin Noise With Seed {}", seed); @@ -20,7 +18,7 @@ void PerlinNoise::init(unsigned seed) { is_init = true; } -float PerlinNoise::noise(float x, float y, float z) { +float PerlinNoise3D::noise(float x, float y, float z) { ASSERT_MSG(is_init, "The PerlinNoise don't init!"); int ix = static_cast(std::floor(x)) & 255; int iy = static_cast(std::floor(y)) & 255; @@ -55,11 +53,13 @@ float PerlinNoise::noise(float x, float y, float z) { return (res + 1.0f) / 2.0f; } -float PerlinNoise::fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } +float PerlinNoise3D::fade(float t) { + return t * t * t * (t * (t * 6 - 15) + 10); +} -float PerlinNoise::lerp(float t, float a, float b) { return a + t * (b - a); } +float PerlinNoise3D::lerp(float t, float a, float b) { return a + t * (b - a); } -float PerlinNoise::grad(int hash, float x, float y, float z) { +float PerlinNoise3D::grad(int hash, float x, float y, float z) { int h = hash & 15; float u = h < 8 ? x : y; @@ -68,7 +68,7 @@ float PerlinNoise::grad(int hash, float x, float y, float z) { return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); } -void PerlinNoise::reload(unsigned seed) { +void PerlinNoise3D::reload(unsigned seed) { is_init = false; p.resize(256); std::iota(p.begin(), p.end(), 0); @@ -79,4 +79,59 @@ void PerlinNoise::reload(unsigned seed) { is_init = true; } +void PerlinNoise2D::init(unsigned seed) { + p.resize(256); + std::iota(p.begin(), p.end(), 0); + Logger::info("Init Perlin Noise With Seed {}", seed); + std::shuffle(p.begin(), p.end(), std::mt19937(seed)); + + p.insert(p.end(), p.begin(), p.end()); // 扩展到 512,方便索引 + is_init = true; +} + +float PerlinNoise2D::noise(float x, float y) { + ASSERT_MSG(is_init, "The PerlinNoise2D don't init!"); + + int ix = static_cast(std::floor(x)) & 255; + int iy = static_cast(std::floor(y)) & 255; + + x -= std::floor(x); + y -= std::floor(y); + + float u = fade(x); + float v = fade(y); + + int a = p[ix] + iy; + int b = p[ix + 1] + iy; + + float res = + lerp(v, lerp(u, grad(p[a], x, y), grad(p[b], x - 1, y)), + lerp(u, grad(p[a + 1], x, y - 1), grad(p[b + 1], x - 1, y - 1))); + + return (res + 1.0f) / 2.0f; // 映射到 [0, 1] +} + +float PerlinNoise2D::fade(float t) { + return t * t * t * (t * (t * 6 - 15) + 10); +} + +float PerlinNoise2D::lerp(float t, float a, float b) { return a + t * (b - a); } + +float PerlinNoise2D::grad(int hash, float x, float y) { + int h = hash & 3; // 使用低 2 位选择 4 个梯度方向 + float u = (h & 1) ? -x : x; + float v = (h & 2) ? -y : y; + return u + v; +} + +void PerlinNoise2D::reload(unsigned seed) { + is_init = false; + p.resize(256); + std::iota(p.begin(), p.end(), 0); + Logger::info("Reload Perlin Noise With Seed {}", seed); + std::shuffle(p.begin(), p.end(), std::mt19937(seed)); + + p.insert(p.end(), p.begin(), p.end()); + is_init = true; +} } // namespace Cubed \ No newline at end of file