terrain

Real-time terrain generation using marching cubes
git clone git://git.christianermann.dev/terrain
Log | Files | Refs | README | LICENSE

commit 4f5cb4dc20278fa511955ad253adb2eb0252a40e
parent 85c7eca6038d4186603dd73b6fe136ec827a7cf9
Author: Christian Ermann <christianermann@gmail.com>
Date:   Sat, 28 May 2022 20:15:12 -0700

Render terrain surface and caves.

Diffstat:
Minclude/marching_cubes.h | 11++++++++---
Minclude/mesh.h | 7++++++-
Mshaders/basic.fs | 5++++-
Mshaders/basic.vs | 5++++-
Msrc/main.c | 36++++++++++++++++++++++++++++++++++--
Msrc/marching_cubes.c | 61++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/mesh.c | 28++++++++++++++++++++++++----
7 files changed, 134 insertions(+), 19 deletions(-)

diff --git a/include/marching_cubes.h b/include/marching_cubes.h @@ -1,13 +1,18 @@ #ifndef MC_H #define MC_H -#include "vec.h" +#include "mesh.h" #include "sdf.h" int MC_index(const Vec3 corners[8], SDF f, float isolevel); -int MC_vertices(const Vec3 corners[8], SDF f, float isolevel, int mc_index, - Vec3 vertices[3]); +int MC_vertices( + const Vec3 corners[8], + SDF f, + float isolevel, + int mc_index, + Vertex vertices[3] +); int MC_indices(int mc_index, unsigned int vertex_offset, const unsigned int edge_offsets[12], unsigned int indices[15]); diff --git a/include/mesh.h b/include/mesh.h @@ -6,9 +6,14 @@ #include "glad/glad.h" typedef struct { + Vec3 position; + Vec3 normal; +} Vertex; + +typedef struct { unsigned int vertex_capacity; unsigned int vertex_count; - Vec3 *vertices; + Vertex *vertices; unsigned int index_capacity; unsigned int index_count; diff --git a/shaders/basic.fs b/shaders/basic.fs @@ -1,6 +1,7 @@ #version 330 core in vec3 frag_pos; +in vec3 frag_norm; out vec4 frag_color; @@ -16,7 +17,9 @@ void main() vec3 normal = normalize(cross(dy, dx)); float slope = (normal.y + 1.0) * 0.5; - vec3 object_color = mix(vec3(1.0, 0.8, 0.6), vec3(0.7, 0.7, 0.7), slope); + + normal = mix(normal, -frag_norm, 1 - slope); + vec3 object_color = mix(vec3(0.6, 0.7, 0.5), vec3(0.5, 0.6, 0.7), slope); float d = length(pointlight_pos - frag_pos); float attenuation = 1.0 / (1.0 + 0.022 * d + 0.0019 * d * d); diff --git a/shaders/basic.vs b/shaders/basic.vs @@ -1,13 +1,16 @@ #version 330 core layout(location = 0) in vec3 in_position; +layout(location = 1) in vec3 in_normal; out vec3 frag_pos; +out vec3 frag_norm; uniform mat4 camera; void main() { frag_pos = in_position; - gl_Position = camera * vec4(in_position, 1.0); + frag_norm = in_normal; + gl_Position = camera * vec4(frag_pos, 1.0); } diff --git a/src/main.c b/src/main.c @@ -18,6 +18,39 @@ float perlinSDF(const Vec3 p) + 0.1f * perlin(p[0] * 0.1f, p[1] * 0.1f, p[2] * 0.1f); } +float caveSDF(const Vec3 p) +{ + float o1 = perlin(p[0] * 0.01f, p[1] * 0.01f, p[2] * 0.01f); + + return o1; +} + +float terrainSDF(const Vec3 p) +{ + float o1 = 50.0f * perlin(p[0] * 0.005f, 0.0f, p[2] * 0.005f); + float o2 = 2.0f * perlin(p[0] * 0.050f, 0.0f, p[2] * 0.050f); + float o3 = 1.0f * perlin(p[0] * 0.100f, 0.0f, p[2] * 0.100f); + + float surface = p[1] - o1 - o2 - o3; + float cave = perlinSDF(p); + + float k = 32.0f; + float h = 0.5f - 0.5f * (cave - surface) / k; + if (h < 0.0f) + { + h = 0.0f; + } + if (h > 1.0f) + { + h = 1.0f; + } + + float a = cave * (1.0f - h) + surface * h; + float b = k * h * (1.0f - h); + + return a + b; +} + float sphereSDF(const float p[3]) { return sqrtf(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]) - 25.0f; @@ -46,7 +79,7 @@ int main(int argc, char** argv) ChunkManager chunk_manager = ChunkManager_create( camera->position, 3, - perlinSDF, + terrainSDF, 0.0f ); ChunkManager_drawChunks(&chunk_manager); @@ -59,7 +92,6 @@ int main(int argc, char** argv) if (glfwGetKey(app->window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetInputMode(app->window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - //glfwSetWindowShouldClose(app->window, true); } if (glfwGetKey(app->window, GLFW_KEY_ENTER) == GLFW_PRESS) { diff --git a/src/marching_cubes.c b/src/marching_cubes.c @@ -330,23 +330,70 @@ static void MC_interpolateVertexPosition(const Vec3 a, const Vec3 b, SDF f, return; } +// Inigo Quilez trapezoidal algorithm +// These normals could be computed in vertex shader if it had access to the SDF +static void MC_normal(const Vec3 p, SDF f, Vec3 dst) +{ + const float h = 0.01f; + + Vec3 x1 = { h, -h, -h }; + Vec3 x2 = { -h, -h, h }; + Vec3 x3 = { -h, h, -h }; + Vec3 x4 = { h, h, h }; + + Vec3 x5, x6, x7, x8; + Vec3_add(p, x1, x5); + Vec3_add(p, x2, x6); + Vec3_add(p, x3, x7); + Vec3_add(p, x4, x8); + + Vec3_scale(x1, f(x5), x1); + Vec3_scale(x2, f(x6), x2); + Vec3_scale(x3, f(x7), x3); + Vec3_scale(x4, f(x8), x4); + + Vec3_add(x1, x2, x1); + Vec3_add(x3, x4, x3); + Vec3_add(x1, x3, x1); + + Vec3_normalize(x1, dst); +} + int MC_vertices(const Vec3 corners[8], SDF f, float isolevel, int mc_index, - Vec3 vertices[3]) + Vertex vertices[3]) { if (EDGE_TABLE[mc_index] & 1) { - MC_interpolateVertexPosition(corners[0], corners[1], f, isolevel, - vertices[0]); + MC_interpolateVertexPosition( + corners[0], + corners[1], + f, + isolevel, + vertices[0].position + ); + MC_normal(vertices[0].position, f, vertices[0].normal); } if (EDGE_TABLE[mc_index] & 8) { - MC_interpolateVertexPosition(corners[3], corners[0], f, isolevel, - vertices[1]); + MC_interpolateVertexPosition( + corners[3], + corners[0], + f, + isolevel, + vertices[1].position + ); + MC_normal(vertices[1].position, f, vertices[1].normal); } if (EDGE_TABLE[mc_index] & 256) { - MC_interpolateVertexPosition(corners[0], corners[4], f, isolevel, - vertices[2]); + MC_interpolateVertexPosition( + corners[0], + corners[4], + f, + isolevel, + vertices[2].position + ); + MC_normal(vertices[2].position, f, vertices[2].normal); } return 3; } diff --git a/src/mesh.c b/src/mesh.c @@ -17,12 +17,32 @@ void Mesh_init(Mesh *m, unsigned int vertex_count, unsigned int index_count) glGenBuffers(1, &m->vbo); glBindBuffer(GL_ARRAY_BUFFER, m->vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof *m->vertices * vertex_count, NULL, - GL_STATIC_DRAW); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3), (GLvoid*)0); + glBufferData( + GL_ARRAY_BUFFER, + sizeof *m->vertices * vertex_count, + NULL, + GL_STATIC_DRAW + ); + + glVertexAttribPointer( + 0, + 3, + GL_FLOAT, + GL_FALSE, + sizeof(Vertex), + (GLvoid*)0 + ); glEnableVertexAttribArray(0); + glVertexAttribPointer( + 1, + 3, + GL_FLOAT, + GL_FALSE, + sizeof(Vertex), + (GLvoid*)sizeof(Vec3) + ); + glEnableVertexAttribArray(1); glGenBuffers(1, &m->ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ebo);