From 4a6fe21a3e8228a7d71a2c5e3cfcfa4e7af75f41 Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein Date: Sun, 13 Sep 2020 16:53:05 +0200 Subject: [PATCH] Frustum & Backface Culling --- lib/FrustumCull.h | 117 +++++++++++++++++++++++++++++ src/dragonblocks/chunk.cpp | 2 + src/dragonblocks/client.cpp | 6 +- src/dragonblocks/cube.cpp | 12 +-- src/dragonblocks/mesh.cpp | 6 +- src/dragonblocks/mesh.hpp | 5 +- src/dragonblocks/render_engine.cpp | 13 +++- src/dragonblocks/render_engine.hpp | 3 +- src/dragonblocks/scene.cpp | 4 +- src/dragonblocks/scene.hpp | 4 +- 10 files changed, 154 insertions(+), 18 deletions(-) create mode 100644 lib/FrustumCull.h diff --git a/lib/FrustumCull.h b/lib/FrustumCull.h new file mode 100644 index 0000000..ab97929 --- /dev/null +++ b/lib/FrustumCull.h @@ -0,0 +1,117 @@ +#include + +class Frustum +{ +public: + Frustum() {} + + // m = ProjectionMatrix * ViewMatrix + Frustum(glm::mat4 m); + + // http://iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm + bool IsBoxVisible(const glm::vec3& minp, const glm::vec3& maxp) const; + +private: + enum Planes + { + Left = 0, + Right, + Bottom, + Top, + Near, + Far, + Count, + Combinations = Count * (Count - 1) / 2 + }; + + template + struct ij2k + { + enum { k = i * (9 - i) / 2 + j - 1 }; + }; + + template + glm::vec3 intersection(const glm::vec3* crosses) const; + + glm::vec4 m_planes[Count]; + glm::vec3 m_points[8]; +}; + +inline Frustum::Frustum(glm::mat4 m) +{ + m = glm::transpose(m); + m_planes[Left] = m[3] + m[0]; + m_planes[Right] = m[3] - m[0]; + m_planes[Bottom] = m[3] + m[1]; + m_planes[Top] = m[3] - m[1]; + m_planes[Near] = m[3] + m[2]; + m_planes[Far] = m[3] - m[2]; + + glm::vec3 crosses[Combinations] = { + glm::cross(glm::vec3(m_planes[Left]), glm::vec3(m_planes[Right])), + glm::cross(glm::vec3(m_planes[Left]), glm::vec3(m_planes[Bottom])), + glm::cross(glm::vec3(m_planes[Left]), glm::vec3(m_planes[Top])), + glm::cross(glm::vec3(m_planes[Left]), glm::vec3(m_planes[Near])), + glm::cross(glm::vec3(m_planes[Left]), glm::vec3(m_planes[Far])), + glm::cross(glm::vec3(m_planes[Right]), glm::vec3(m_planes[Bottom])), + glm::cross(glm::vec3(m_planes[Right]), glm::vec3(m_planes[Top])), + glm::cross(glm::vec3(m_planes[Right]), glm::vec3(m_planes[Near])), + glm::cross(glm::vec3(m_planes[Right]), glm::vec3(m_planes[Far])), + glm::cross(glm::vec3(m_planes[Bottom]), glm::vec3(m_planes[Top])), + glm::cross(glm::vec3(m_planes[Bottom]), glm::vec3(m_planes[Near])), + glm::cross(glm::vec3(m_planes[Bottom]), glm::vec3(m_planes[Far])), + glm::cross(glm::vec3(m_planes[Top]), glm::vec3(m_planes[Near])), + glm::cross(glm::vec3(m_planes[Top]), glm::vec3(m_planes[Far])), + glm::cross(glm::vec3(m_planes[Near]), glm::vec3(m_planes[Far])) + }; + + m_points[0] = intersection(crosses); + m_points[1] = intersection(crosses); + m_points[2] = intersection(crosses); + m_points[3] = intersection(crosses); + m_points[4] = intersection(crosses); + m_points[5] = intersection(crosses); + m_points[6] = intersection(crosses); + m_points[7] = intersection(crosses); + +} + +// http://iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm +inline bool Frustum::IsBoxVisible(const glm::vec3& minp, const glm::vec3& maxp) const +{ + // check box outside/inside of frustum + for (int i = 0; i < Count; i++) + { + if ((glm::dot(m_planes[i], glm::vec4(minp.x, minp.y, minp.z, 1.0f)) < 0.0) && + (glm::dot(m_planes[i], glm::vec4(maxp.x, minp.y, minp.z, 1.0f)) < 0.0) && + (glm::dot(m_planes[i], glm::vec4(minp.x, maxp.y, minp.z, 1.0f)) < 0.0) && + (glm::dot(m_planes[i], glm::vec4(maxp.x, maxp.y, minp.z, 1.0f)) < 0.0) && + (glm::dot(m_planes[i], glm::vec4(minp.x, minp.y, maxp.z, 1.0f)) < 0.0) && + (glm::dot(m_planes[i], glm::vec4(maxp.x, minp.y, maxp.z, 1.0f)) < 0.0) && + (glm::dot(m_planes[i], glm::vec4(minp.x, maxp.y, maxp.z, 1.0f)) < 0.0) && + (glm::dot(m_planes[i], glm::vec4(maxp.x, maxp.y, maxp.z, 1.0f)) < 0.0)) + { + return false; + } + } + + // check frustum outside/inside box + int out; + out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].x > maxp.x) ? 1 : 0); if (out == 8) return false; + out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].x < minp.x) ? 1 : 0); if (out == 8) return false; + out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].y > maxp.y) ? 1 : 0); if (out == 8) return false; + out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].y < minp.y) ? 1 : 0); if (out == 8) return false; + out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].z > maxp.z) ? 1 : 0); if (out == 8) return false; + out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].z < minp.z) ? 1 : 0); if (out == 8) return false; + + return true; +} + +template +inline glm::vec3 Frustum::intersection(const glm::vec3* crosses) const +{ + float D = glm::dot(glm::vec3(m_planes[a]), crosses[ij2k::k]); + glm::vec3 res = glm::mat3(crosses[ij2k::k], -crosses[ij2k::k], crosses[ij2k::k]) * + glm::vec3(m_planes[a].w, m_planes[b].w, m_planes[c].w); + return res * (-1.0f / D); +} diff --git a/src/dragonblocks/chunk.cpp b/src/dragonblocks/chunk.cpp index 5c6433d..2100511 100644 --- a/src/dragonblocks/chunk.cpp +++ b/src/dragonblocks/chunk.cpp @@ -137,6 +137,8 @@ void Chunk::updateMesh() mesh = new Mesh(scene, &vertices[0], vertices.size() * sizeof(GLfloat)); mesh->pos = pos * SIZE + SIZE / 2; + mesh->minp = vec3(- SIZE / 2 - 1); + mesh->maxp = vec3(+ SIZE / 2 + 1); mesh->textures = textures; mesh->vertices_per_texture = 6; if (! mesh_created_before) { diff --git a/src/dragonblocks/client.cpp b/src/dragonblocks/client.cpp index c98cec3..c8fa8db 100644 --- a/src/dragonblocks/client.cpp +++ b/src/dragonblocks/client.cpp @@ -18,7 +18,7 @@ Client::Client() { log("Initalizing..."); - Texture::mipmap = false; + Texture::mipmap = true; Texture::bilinear_filter = false; Texture::initArgs(); @@ -41,8 +41,8 @@ Client::Client() player = Player::createLocalplayer(render_engine->camera, render_engine->input_handler, map); player->pitch_move = false; - player->yaw = -90; - player->pitch = -80; + player->yaw = 0; + player->pitch = 0; player->speed = 10; player->pos = vec3(8, 8, 8); diff --git a/src/dragonblocks/cube.cpp b/src/dragonblocks/cube.cpp index f506de5..e132b70 100644 --- a/src/dragonblocks/cube.cpp +++ b/src/dragonblocks/cube.cpp @@ -9,21 +9,21 @@ GLfloat dragonblocks::cube[DRAGONBLOCKS_CUBE_VERTEX_COUNT] = { -0.5, +0.5, -0.5, +0.0, +1.0, -0.5, -0.5, -0.5, +0.0, +0.0, - -0.5, -0.5, +0.5, +0.0, +0.0, - +0.5, -0.5, +0.5, +1.0, +0.0, + -0.5, -0.5, +0.5, +0.0, +0.0, +0.5, +0.5, +0.5, +1.0, +1.0, + +0.5, -0.5, +0.5, +1.0, +0.0, +0.5, +0.5, +0.5, +1.0, +1.0, - -0.5, +0.5, +0.5, +0.0, +1.0, -0.5, -0.5, +0.5, +0.0, +0.0, + -0.5, +0.5, +0.5, +0.0, +1.0, -0.5, +0.5, +0.5, +1.0, +1.0, - -0.5, +0.5, -0.5, +0.0, +1.0, -0.5, -0.5, -0.5, +0.0, +0.0, + -0.5, +0.5, -0.5, +0.0, +1.0, -0.5, -0.5, -0.5, +0.0, +0.0, - -0.5, -0.5, +0.5, +1.0, +0.0, -0.5, +0.5, +0.5, +1.0, +1.0, + -0.5, -0.5, +0.5, +1.0, +0.0, - +0.5, +0.5, +0.5, +1.0, +1.0, + +0.5, +0.5, +0.5, +1.0, +1.0, +0.5, +0.5, -0.5, +0.0, +1.0, +0.5, -0.5, -0.5, +0.0, +0.0, +0.5, -0.5, -0.5, +0.0, +0.0, diff --git a/src/dragonblocks/mesh.cpp b/src/dragonblocks/mesh.cpp index 2d077f3..e8741ac 100644 --- a/src/dragonblocks/mesh.cpp +++ b/src/dragonblocks/mesh.cpp @@ -3,6 +3,7 @@ #include #include #include +#include "FrustumCull.h" #include "gldebug.hpp" #include "mesh.hpp" #include "scene.hpp" @@ -76,7 +77,7 @@ Mesh::Animation::Animation(Mesh::Animation::Type t, void (*o)(void *), void *e) } } -void Mesh::render(double dtime, ShaderProgram *shader_program) +void Mesh::render(double dtime, ShaderProgram *shader_program, Frustum *frustum) { rendering = true; @@ -95,6 +96,9 @@ void Mesh::render(double dtime, ShaderProgram *shader_program) mat4 model_matrix = animation.getModelMatrix(dtime, pos, size, rotation_axis, rotation_angle); CHECKERR + if (! frustum->IsBoxVisible(model_matrix * vec4(minp, 1.0), model_matrix * vec4(maxp, 1.0))) + return; + shader_program->set("model", model_matrix); CHECKERR glBindVertexArray(VAO); CHECKERR diff --git a/src/dragonblocks/mesh.hpp b/src/dragonblocks/mesh.hpp index b603bc9..4dafcc9 100644 --- a/src/dragonblocks/mesh.hpp +++ b/src/dragonblocks/mesh.hpp @@ -4,6 +4,8 @@ #include "gl.hpp" #include "texture.hpp" +class Frustum; + namespace dragonblocks { class Scene; @@ -44,11 +46,12 @@ namespace dragonblocks int vertices_per_texture; glm::vec3 pos, size, rotation_axis; + glm::vec3 minp, maxp; float rotation_angle = 0; std::vector textures; Animation animation; - void render(double dtime, ShaderProgram *); + void render(double dtime, ShaderProgram *, Frustum *); bool isRendering(); void die(); diff --git a/src/dragonblocks/render_engine.cpp b/src/dragonblocks/render_engine.cpp index 5476bd1..fb23d35 100644 --- a/src/dragonblocks/render_engine.cpp +++ b/src/dragonblocks/render_engine.cpp @@ -1,5 +1,6 @@ #include #include +#include "FrustumCull.h" #include "camera.hpp" #include "gldebug.hpp" #include "input_handler.hpp" @@ -21,11 +22,16 @@ void RenderEngine::render() input_handler->processInput(dtime); glEnable(GL_DEPTH_TEST); CHECKERR + glEnable(GL_CULL_FACE); CHECKERR + glCullFace(GL_BACK); CHECKERR + glFrontFace(GL_CW); CHECKERR glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); CHECKERR updateViewMatrix(); CHECKERR - scene->render(dtime, shader_program); + Frustum frustum(projection_matrix * view_matrix); + + scene->render(dtime, shader_program, &frustum); window->swapBuffers(); CHECKERR glfwPollEvents(); CHECKERR @@ -39,13 +45,14 @@ bool RenderEngine::running() void RenderEngine::updateProjectionMatrix() { dvec2 bounds = window->getSize(); - mat4 projection_matrix = perspective(radians(fov), bounds.x / bounds.y, 0.01, render_distance); + projection_matrix = perspective(radians(fov), bounds.x / bounds.y, 0.01, render_distance); shader_program->set("projection", projection_matrix); CHECKERR } void RenderEngine::updateViewMatrix() { - shader_program->set("view", camera->getViewMatrix()); CHECKERR + view_matrix = camera->getViewMatrix(); + shader_program->set("view", view_matrix); CHECKERR } void RenderEngine::setSky(vec3 sky) diff --git a/src/dragonblocks/render_engine.hpp b/src/dragonblocks/render_engine.hpp index fcf5474..c1732c6 100644 --- a/src/dragonblocks/render_engine.hpp +++ b/src/dragonblocks/render_engine.hpp @@ -34,7 +34,8 @@ namespace dragonblocks RenderEngine(); ~RenderEngine(); - private: + private: + glm::mat4 view_matrix, projection_matrix; double last_time; double render_distance; double fov; diff --git a/src/dragonblocks/scene.cpp b/src/dragonblocks/scene.cpp index fa230b5..2f74bb2 100644 --- a/src/dragonblocks/scene.cpp +++ b/src/dragonblocks/scene.cpp @@ -14,12 +14,12 @@ void Scene::remove(Mesh *m) meshes.erase(m); } -void Scene::render(double dtime, ShaderProgram *shader_program) +void Scene::render(double dtime, ShaderProgram *shader_program, Frustum *frustum) { auto renderlist = meshes; for (auto it = renderlist.begin(); it != renderlist.end(); it++) { Mesh *mesh = *it; - mesh->render(dtime, shader_program); + mesh->render(dtime, shader_program, frustum); } } diff --git a/src/dragonblocks/scene.hpp b/src/dragonblocks/scene.hpp index fdf0d86..fc0f62d 100644 --- a/src/dragonblocks/scene.hpp +++ b/src/dragonblocks/scene.hpp @@ -2,6 +2,8 @@ #include +class Frustum; + namespace dragonblocks { class Mesh; @@ -11,7 +13,7 @@ namespace dragonblocks public: void add(Mesh *); void remove(Mesh *); - void render(double, ShaderProgram *); + void render(double, ShaderProgram *, Frustum *); void clear(); void run(); -- 2.44.0