查看截锥剔除问题

kg7wmglp  于 2022-09-26  发布在  其他
关注(0)|答案(3)|浏览(178)

因此,我正在尝试实现截锥剔除。这里的问题是,在我能做到这一点之前,我需要了解一些事情。

首先,是平面交点:

我的理解是,平面可以由三个点定义;让我们称它们为p0, p1, and p2

鉴于此,我们知道可以按如下方式计算平面的法线:

(psuedo代码)

vec3 edge0 = p1 - p0;
vec3 edge1 = p2 - p0;

vec3 normal = normalize( cross( edge0, edg1 ) ) // => edge1 X edge2 / length( edge1 X edge2 );

现在,假设我们有一个函数,它基本上告诉我们一个给定点是否以某种方式“穿过”平面。

(MOAR伪码)

bool crossesPlane( vec3 plane[3], vec3 point )
{
    vec3 normal = getNormal( plane ); // perform same operations as described above
    float D = dot( -normal, plane[ 0 ] ); // plane[ 0 ] is arbitrary - could just as well be the 2nd or 3rd point in the array

    float dist = dot(normal, point) + D; 

    return dist >= 0; // dist < 0 means we're on the opposite side of the plane's normal. 
}

其背后的逻辑推理是,由于视图截体包含六个独立的平面(近、远、左、上、右、下),我们希望获取这六个平面中的每一个,并实质上将它们“传递”给crossesPlane()函数,用于单个点(一个点,该点的六个测试)。

如果对crossesPlane()的这六个调用中的一个返回false,那么我们希望剔除有问题的点,从而有效地导致该点被锥体剔除,当然,该点将不会被呈现。

问题

  • 这是适当剔除视锥的正确方法吗?
  • 如果是这样的话,为给定的多边形取一个任意的顶点列表,并使用这种方法对每个顶点进行测试,是不是一种有效的方法?
  • 虽然AABB可以用来代替多边形/网格进行扑杀测试,但在这种情况下,它们仍然常用吗?它们仍然被认为是“一般的”Goto方法吗?

注意事项

如果D3D和OpenGL在实现上有什么不同之处,我们将不胜感激。

ogsagwnx

ogsagwnx1#

  • 是的,这基本上是截顶剔除点的正确方法。
  • 不,单独对每个顶点执行此测试不是剔除任意多边形的有效方法。考虑一个与锥体相交的非常大的三角形的情况:它的所有顶点可能都在锥体之外,但该三角形仍然与锥体相交,因此应该进行渲染。
  • AABB可以用于截锥剔除,通常是一个很好的选择,尽管您仍然必须处理AABB的所有顶点都在截断之外但与截断相交的情况。边界球体使内部/外部测试稍微简单一些,但它们所包含的对象的边界往往较宽松。然而,这通常是一种合理的权衡。
hvvq6cgz

hvvq6cgz2#

你的方法大体上是正确的。
通常使用AABB或边界球体,而不是测试任意形状的每个顶点。
然而,对于AABB,在某些情况下,所有的角都在锥体之外,但长方体仍然与锥体相交。保守的解决方案是,如果所有角点都位于至少一个平面的外侧,则仅拒绝长方体。
对于AABB,有一个常见的优化:对于锥体的每个平面,您只需要检查“最近”和“最远”的角,而不是所有的6个角。这方面和总体上的锥体剔除的一个很好的资源是:
http://www.lighthouse3d.com/tutorials/view-frustum-culling/

编辑:这里是另一篇如何找到不完全位于一个平面的外侧,但仍然不与锥体相交的AABB的文章:
http://www.iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm

e5njpo68

e5njpo683#

提纲:

这里是一个改编自基本learnopengl tutorial的最小工作示例。


# include <glad/glad.h>

# include <GLFW/glfw3.h>

# define STB_IMAGE_IMPLEMENTATION

# include <stb_image.h>

# include <vector>

# include <glm/glm.hpp>

# include <glm/gtc/matrix_transform.hpp>

# include <glm/gtc/type_ptr.hpp>

# include <learnopengl/shader_m.h>

# include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// camera
glm::vec3 cameraPos   = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp    = glm::vec3(0.0f, 1.0f, 0.0f);
glm::mat4 projection;
glm::mat4 view;

bool firstMouse = true;
float yaw   = -90.0f;   // yaw is initialized to -90.0 degrees since a yaw of 0.0 results in a direction vector pointing to the right so we initially rotate a bit to the left.
float pitch =  0.0f;
float lastX =  800.0f / 2.0;
float lastY =  600.0 / 2.0;
float fov   =  45.0f;

// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;

float pointPlaneDistance(const glm::vec3 &point, const glm::vec3 &planePosition, const glm::vec3 &planeNormal);

bool isMinkowskiFace(const glm::vec3 &a, const glm::vec3 &b, const glm::vec3 &b_x_a, const glm::vec3 &c, const glm::vec3 &d, const glm::vec3 &d_x_c);

class Edge {
public:
    glm::vec3 v0;
    glm::vec3 v1;
    std::vector<int> face_indices;
    Edge(const glm::vec3 &v0, const glm::vec3 &v1);
};

class Face {
public:
    std::vector<glm::vec3> vertices;
    glm::vec3 position;
    glm::vec3 normal;
    Face(const glm::vec3 &v0, const glm::vec3 &v1, const glm::vec3 &v2);
};

class Body {
public:
    glm::vec3 cm_position;
    std::vector<glm::vec3> vertices;
    std::vector<Edge> edges;
    std::vector<Face> faces;

    Body();

    int update(const std::vector<glm::vec3> &vertices);
};

class Query {
public:
    float max_seperation;
    std::vector<int> max_index;
    glm::vec3 best_axis;
    int type;
    Query(float max_seperation, std::vector<int> best_index, glm::vec3 best_axis) {
        this->max_seperation = max_seperation;
        this->max_index = max_index;
        this->best_axis = best_axis;
    }
};

std::vector<Query> SAT(const Body &hullA, const Body &hullB);

float pointPlaneDistance(const glm::vec3 &point, const glm::vec3 &planePosition, const glm::vec3 &planeNormal) {
    return glm::dot(point - planePosition, planeNormal);
}

bool isMinkowskiFace(const glm::vec3 &a, const glm::vec3 &b, const glm::vec3 &b_x_a, const glm::vec3 &c, const glm::vec3 &d, const glm::vec3 &d_x_c) {
    float cba = glm::dot(c, b_x_a);
    float dba = glm::dot(d, b_x_a);
    float adc = glm::dot(a, d_x_c);
    float bdc = glm::dot(b, d_x_c);
    return cba * dba < 0.0f && adc * bdc < 0.0f && cba * bdc > 0.0f;
}

Face::Face(const glm::vec3 &v0, const glm::vec3 &v1, const glm::vec3 &v2) {
    this->vertices = {v0, v1, v2};
    this->position = v0;
    this->normal = glm::normalize(glm::cross(v0 - v2, v1 - v0));
}

Edge::Edge(const glm::vec3 &v0, const glm::vec3 &v1) {
    this->v0 = v0;
    this->v1 = v1;
}

Body::Body() {
        this->vertices = {};
        this->edges = {};
        this->faces = {};
        this->cm_position = glm::vec3(0,0,0);
}
// in world space
int Body::update(const std::vector<glm::vec3> &v) {

    this->faces = {
        Face(v.at(0), v.at(1), v.at(2)),
        Face(v.at(4), v.at(5), v.at(1)),
        Face(v.at(1), v.at(5), v.at(6)),
        Face(v.at(3), v.at(2), v.at(6)),
        Face(v.at(7), v.at(4), v.at(0)), 
        Face(v.at(7), v.at(6), v.at(5)),                      
    };

    this->edges = {
        Edge(v.at(0), v.at(1)),
        Edge(v.at(1), v.at(2)),
        Edge(v.at(2), v.at(3)),
        Edge(v.at(3), v.at(0)),
        Edge(v.at(4), v.at(5)),
        Edge(v.at(5), v.at(6)),
        Edge(v.at(6), v.at(7)),
        Edge(v.at(7), v.at(4)), 
        Edge(v.at(4), v.at(0)),
        Edge(v.at(5), v.at(1)),
        Edge(v.at(6), v.at(2)),
        Edge(v.at(7), v.at(3)),
    };

    this->edges[0].face_indices = {0, 1};
    this->edges[1].face_indices = {0, 2};
    this->edges[2].face_indices = {0, 3};
    this->edges[3].face_indices = {0, 4};
    this->edges[4].face_indices = {1, 5};
    this->edges[5].face_indices = {2, 5};
    this->edges[6].face_indices = {3, 5};
    this->edges[7].face_indices = {4, 5};
    this->edges[8].face_indices = {1, 4};
    this->edges[9].face_indices = {1, 2};
    this->edges[10].face_indices = {2, 3};
    this->edges[11].face_indices = {3, 4};

    this->vertices = v;

    return 0;

}

// find furthest along n
glm::vec3 get_support(const std::vector<glm::vec3> &vertices, glm::vec3 n) {
    glm::vec3 _v;
    float _d = -FLT_MAX;
    for (unsigned int i = 0; i < vertices.size(); i++) {
        glm::vec3 v = vertices.at(i);
        float d = glm::dot(v, n);
        if ( d > _d) {
            _d = d;
            _v = v;
        }
    }
    return _v;
}

Query query_face_directions(const Body &hullA, const Body &hullB) {
    float max_seperation = -FLT_MAX;
    std::vector<int> max_index;
    max_index.push_back(-1);
    max_index.push_back(-1);
    glm::vec3 best_axis;
    for (int i = 0; i < hullA.faces.size(); i++) {
        Face f = hullA.faces.at(i);
        glm::vec3 support_point = get_support(hullB.vertices, f.normal * -1.0f);
        float dist = pointPlaneDistance(support_point, f.position, f.normal);
        if (dist > max_seperation) {
            max_index = {i, -1};
            max_seperation = dist;
            best_axis = f.normal;
        }
    }
    return Query(max_seperation, max_index, best_axis);
}

Query query_edge_directions(const Body &hullA, const Body &hullB) {
    float max_seperation = -FLT_MAX;
    std::vector<int> max_index;
    max_index.push_back(-1);
    max_index.push_back(-1);
    glm::vec3 best_axis;

    for (int i = 0; i < hullA.edges.size(); i++) {
        Edge edge_a = hullA.edges.at(i);
        glm::vec3 edge_a_n1 = hullA.faces[edge_a.face_indices[0]].normal;
        glm::vec3 edge_a_n2 = hullA.faces[edge_a.face_indices[1]].normal;

        for (int j = 0; j < hullB.edges.size(); j++) {
            Edge edge_b = hullB.edges.at(j);
            glm::vec3 edge_b_n1 = hullB.faces[edge_b.face_indices[0]].normal;
            glm::vec3 edge_b_n2 = hullB.faces[edge_b.face_indices[1]].normal;
            // negate last two values for minkowski difference
            bool builds_face = isMinkowskiFace(edge_a_n1, edge_a_n2, glm::cross(edge_a_n1, edge_a_n2), edge_b_n1 * -1.0f, edge_b_n2 * -1.0f, glm::cross(edge_b_n1 * -1.0f, edge_b_n2 * -1.0f));

            if (!builds_face) {
                continue;
            }
            glm::vec3 axis = glm::normalize(glm::cross(edge_a.v1 - edge_a.v0, edge_b.v1 - edge_b.v0));
            // check edges arent parallel
            if (glm::length(axis) < 0.0001f) {
                continue;
            }
            // check normal is pointing away from A
            if (glm::dot(axis, edge_a.v0 - hullA.cm_position) < 0.0f) {
                axis = axis * -1.0f;
            }

            float dist1 = pointPlaneDistance(edge_b.v0, edge_a.v0, axis);
            float dist2 = pointPlaneDistance(edge_b.v1, edge_a.v0, axis);
            float dist;
            if (dist1 > dist2) {
                dist = dist1;
            }
            else {
                dist = dist2;
            }

            // keep largest penetration
            if (max_seperation == -FLT_MAX || dist > max_seperation) {
                max_index = {i,j};
                max_seperation = dist;
                best_axis = axis;
            }

        }
    }
    return Query(max_seperation, max_index, best_axis);
}

std::vector<Query> SAT(const Body &hullA, const Body &hullB) {

    // and the cross product of the edges if they build a face on the minkowski
    Query edge_query = query_edge_directions(hullA, hullB);
    if (edge_query.max_seperation > 0.0f) {
        edge_query.type = 0;
        return {edge_query};
    }
    // test all normals of hull_a as axes
    Query face_query_a = query_face_directions(hullA, hullB);
    if (face_query_a.max_seperation > 0.0f) {
        face_query_a.type = 1;
        return {face_query_a};
    }

    // and all normals of hull_b as axes
    Query face_query_b = query_face_directions(hullA, hullB);
    if (face_query_b.max_seperation > 0.0f) {
        face_query_b.type = 2;
        return {face_query_b};
    }

    // return queries with smallest penetration
    bool face_contact_a = face_query_a.max_seperation > edge_query.max_seperation;
    bool face_contact_b = face_query_b.max_seperation > edge_query.max_seperation;
    if (face_contact_a && face_contact_b) {
        face_query_a.type = 1;
        face_query_b.type = 2;
        return {face_query_a, face_query_b};
    } else {
        edge_query.type = 0;
        return {edge_query};
    }
}

int main()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

# ifdef __APPLE__

    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

# endif

    // glfw window creation
    // --------------------
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // tell GLFW to capture our mouse
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // configure global opengl state
    // -----------------------------
    glEnable(GL_DEPTH_TEST);

    // build and compile our shader zprogram
    // ------------------------------------
    Shader ourShader("7.3.camera.vs", "7.3.camera.fs");

    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    float vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    // world space positions of our cubes
    glm::vec3 cubePositions[] = {
        glm::vec3( 0.0f,  0.0f,  0.0f),
        glm::vec3( 2.0f,  5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f),
        glm::vec3(-3.8f, -2.0f, -12.3f),
        glm::vec3( 2.4f, -0.4f, -3.5f),
        glm::vec3(-1.7f,  3.0f, -7.5f),
        glm::vec3( 1.3f, -2.0f, -2.5f),
        glm::vec3( 1.5f,  2.0f, -2.5f),
        glm::vec3( 1.5f,  0.2f, -1.5f),
        glm::vec3(-1.3f,  1.0f, -1.5f)
    };
    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // texture coord attribute
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    // load and create a texture 
    // -------------------------
    unsigned int texture1, texture2;
    // texture 1
    // ---------
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1);
    // set the texture wrapping parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // set texture filtering parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // load image, create texture and generate mipmaps
    int width, height, nrChannels;
    stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.
    //unsigned char *data = stbi_load(FileSystem::getPath("resources/textures/container.jpg").c_str(), &width, &height, &nrChannels, 0);
    unsigned char *data = stbi_load("resources/textures/container.jpg", &width, &height, &nrChannels, 0);

    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);
    // texture 2
    // ---------
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // set the texture wrapping parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // set texture filtering parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // load image, create texture and generate mipmaps
    //data = stbi_load(FileSystem::getPath("resources/textures/awesomeface.png").c_str(), &width, &height, &nrChannels, 0);
    data = stbi_load("resources/textures/awesomeface.png", &width, &height, &nrChannels, 0);

    if (data)
    {
        // note that the awesomeface.png has transparency and thus an alpha channel, so make sure to tell OpenGL the data type is of GL_RGBA
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);

    // tell opengl for each sampler to which texture unit it belongs to (only has to be done once)
    // -------------------------------------------------------------------------------------------
    ourShader.use();
    ourShader.setInt("texture1", 0);
    ourShader.setInt("texture2", 1);

    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // per-frame time logic
        // --------------------
        float currentFrame = static_cast<float>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

        // bind textures on corresponding texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);

        // activate shader
        ourShader.use();

        // pass projection matrix to shader (note that in this case it could change every frame)
        projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        ourShader.setMat4("projection", projection);

        // camera/view transformation
        view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        ourShader.setMat4("view", view);

        float nearDist = 0.1f;
        float farDist = 100.0f;

        // calculate frustum data per frame (lighthouse3d.com tutorial for reference)
        float ar = (float)SCR_WIDTH / (float)SCR_HEIGHT;
        float Hnear = 2 * tan(glm::radians(fov/2)) * nearDist;
        float Wnear = Hnear * ar;
        float Hfar = 2 * tan(glm::radians(fov/2)) * farDist;
        float Wfar = Hfar * ar; 

        glm::vec3 Cnear = cameraPos + glm::normalize(cameraFront) * nearDist;
        glm::vec3 Cfar = cameraPos + glm::normalize(cameraFront) * farDist;
        glm::vec3 cameraRight = glm::cross(cameraFront, cameraUp);
        glm::vec3 topRightFar = Cfar + (cameraUp * (Hfar / 2)) + (cameraRight * (Wfar / 2));
        glm::vec3 bottomRightFar = Cfar - (cameraUp * (Hfar / 2)) + (cameraRight * (Wfar / 2));

        glm::vec3 topLeftFar =  Cfar + (cameraUp * (Hfar / 2)) - (cameraRight * (Wfar / 2));
        glm::vec3 bottomLeftFar =  Cfar - (cameraUp * (Hfar / 2)) - (cameraRight * (Wfar / 2));

        glm::vec3 topRightNear = Cnear + (cameraUp * (Hnear / 2)) + (cameraRight * (Wnear / 2));
        glm::vec3 topLeftNear =  Cnear + (cameraUp * (Hnear / 2)) - (cameraRight * (Wnear / 2));

        glm::vec3 bottomLeftNear = Cnear - (cameraUp * (Hnear /2)) - (cameraRight * (Wnear / 2));
        glm::vec3 bottomRightNear = Cnear - (cameraUp * (Hnear /2)) + (cameraRight * (Wnear / 2));

        glm::vec3 aux = glm::normalize((Cnear + cameraRight * (float)(Wnear / 2)) - cameraPos);
        glm::vec3 rightNormal = glm::normalize(glm::cross(aux, cameraUp));
        aux = glm::normalize((Cnear - cameraRight * (float)(Wnear / 2)) - cameraPos);
        glm::vec3 leftNormal = glm::normalize(glm::cross(aux, cameraUp));

        aux = glm::normalize((Cnear + cameraUp * (float)(Hnear / 2)) - cameraPos);
        glm::vec3 topNormal =  glm::normalize(glm::cross(aux, cameraRight));

        aux = glm::normalize((Cnear - cameraUp * (float)(Hnear / 2)) - cameraPos);
        glm::vec3 bottomNormal =  glm::normalize(glm::cross(aux, cameraRight));

        glm::vec3 backNormal = cameraFront;
        glm::vec3 frontNormal = -1.0f * cameraFront;

        Body frustum_body;

        frustum_body.update({topLeftNear, bottomLeftNear, bottomRightNear, topRightNear, topLeftFar, bottomLeftFar, bottomRightFar, topRightFar});

        // render boxes
        glBindVertexArray(VAO);
        int meshesCulled = 0;
        for (unsigned int i = 0; i < 10; i++)
        {

            // calculate the model matrix for each object and pass it to shader before drawing
            glm::mat4 model = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first
            model = glm::translate(model, cubePositions[i]);
            float angle = 20.0f * i;
            model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));

            Body box_body;

            std::vector<float> aabb = {-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f};
            glm::vec3 _min = glm::vec3(aabb[0], aabb[1], aabb[2]);
            glm::vec3 _max = glm::vec3(aabb[3], aabb[4], aabb[5]);

            glm::vec3 v5 = glm::vec3(model * glm::vec4(_min.x, _min.y, _min.z, 1.0f));
            glm::vec3 v3 = glm::vec3(model * glm::vec4(_max.x, _max.y, _max.z, 1.0f));
            glm::vec3 v4 = glm::vec3(model * glm::vec4(_min.x, _max.y, _min.z, 1.0f));
            glm::vec3 v6 = glm::vec3(model * glm::vec4(_max.x, _min.y, _min.z, 1.0f));
            glm::vec3 v7 = glm::vec3(model * glm::vec4(_max.x, _max.y, _min.z, 1.0f));
            glm::vec3 v2 = glm::vec3(model * glm::vec4(_max.x, _min.y, _max.z, 1.0f));
            glm::vec3 v0 = glm::vec3(model * glm::vec4(_min.x, _max.y, _max.z, 1.0f));
            glm::vec3 v1 = glm::vec3(model * glm::vec4(_min.x, _min.y, _max.z, 1.0f));
            std::vector<glm::vec3> v = {v0, v1, v2, v3, v4, v5, v6, v7};
            box_body.update(v);

            box_body.cm_position = cubePositions[i];

            std::vector<Query> queries = SAT(box_body, frustum_body);
            float max_seperation = -FLT_MAX;
            for (unsigned int i = 0; i < queries.size(); i++) {
                Query query = queries[i];
                if (query.max_seperation > max_seperation) {
                    max_seperation = query.max_seperation;
                }
            }

            if (max_seperation > 0.0f) {
                // mesh is culled
                meshesCulled += 1;
            } else {

                ourShader.setMat4("model", model);
                glDrawArrays(GL_TRIANGLES, 0, 36);
            }
        }

        std::cout << 10 - meshesCulled << " of 10 meshes drawn!" << std::endl;

        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    float cameraSpeed = static_cast<float>(2.5 * deltaTime);
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        cameraPos += cameraSpeed * cameraFront;
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        cameraPos -= cameraSpeed * cameraFront;
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
{
    float xpos = static_cast<float>(xposIn);
    float ypos = static_cast<float>(yposIn);

    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
    lastX = xpos;
    lastY = ypos;

    float sensitivity = 0.1f; // change this value to your liking
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw += xoffset;
    pitch += yoffset;

    // make sure that when pitch is out of bounds, screen doesn't get flipped
    if (pitch > 89.0f)
        pitch = 89.0f;
    if (pitch < -89.0f)
        pitch = -89.0f;

    glm::vec3 front;
    front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    front.y = sin(glm::radians(pitch));
    front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    cameraFront = glm::normalize(front);
}

// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    fov -= (float)yoffset;
    if (fov < 1.0f)
        fov = 1.0f;
    if (fov > 45.0f)
        fov = 45.0f;
}

控制台打印每一帧有多少个OBB被剔除:

相关问题