OpenGL 中的多边形撕裂

Polygon tearing in OpenGL

具有 1000 个子分区的 500x500 网格:

只有一个问题。

为什么会这样?

#include <iostream>
#include <sstream>
#include <vector>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"

#include "GameEngine.hpp"
#include "ShaderProgram.h"
#include "Camera.h"
#include "Mesh.h"

const char *title = "Terrain";

GameEngine engine;
OrbitCamera orbitCamera;
float gYaw = 0.0f;
float gPitch = 1.0f;
float gRadius = 200.0f;
const float MOUSE_SENSTIVITY = 0.25f;

bool gWireFrame = false;
void glfw_onKey(GLFWwindow *window, int key, int scancode, int action, int mode);
void glfw_onMouseMove(GLFWwindow *window, double posX, double posY);
void glfw_onMouseScroll(GLFWwindow *window, double deltaX, double deltaY);

int main()
{
    if (!engine.init(1024, 768, title))
    {
        std::cerr << "OpenGL init failed" << std::endl;
        std::cin.get();
        return -1;
    }

    //set callbacks
    glfwSetKeyCallback(engine.getWindow(), glfw_onKey);
    glfwSetCursorPosCallback(engine.getWindow(), glfw_onMouseMove);

    std::vector<Vertex> VER;

    std::vector<glm::vec3> verts;
    std::vector<unsigned int> indices;
    std::vector<glm::vec3> norms;

    int subDiv = 1000;
    int width = 500;
    int height = 500;
    int size = 0;

    for (int row = 0; row < subDiv; row++)
    {
        for (int col = 0; col < subDiv; col++)
        {
            float x = (float)((col * width) / subDiv - (width / 2.0));
            float z = ((subDiv - row) * height) / subDiv - (height / 2.0);
            glm::vec3 pos = glm::vec3(x, 0, z);
            verts.push_back(pos);
        }
    }

    size = subDiv * subDiv;

    size = verts.size();

    for (int row = 0; row < subDiv -1 ; row++)
    {
        for (int col = 0; col < subDiv -1; col++)
        {
             int row1 = row * (subDiv);
            int row2 = (row+1) * (subDiv);

            indices.push_back(row1+col);
            indices.push_back(row1+col+1);
            indices.push_back( row2+col+1);

            indices.push_back(row1+col);
            indices.push_back( row2+col+1);
            indices.push_back(row2+col);
        }
    }


    for (int i = 0; i < verts.size(); i++)
    {
        Vertex vertex;
        vertex.position = verts[i];

        vertex.normal = glm::vec3(0, 0, 0);
        vertex.texCoords = glm::vec2(0, 0);

        VER.push_back(vertex);
    }

    VER.begin();

    for (int i = 0; i < indices.size(); i += 3)
    {
        Vertex a = VER[indices[i]];
        Vertex b = VER[indices[i + 1]];
        Vertex c = VER[indices[i + 2]];

        glm::vec3 p = glm::cross(b.position - a.position, c.position - a.position);

        VER[indices[i]].normal += p;
        VER[indices[i + 1]].normal += p;
        VER[indices[i + 2]].normal += p;
    }

    for (int i = 0; i < VER.size(); i++)
    {
        VER[i].normal = glm::normalize(VER[i].normal);
    }



    glm::vec3 cubePos = glm::vec3(0.0f, 0.0f, -5.0f);

    GLuint vbo, vao, ibo;

    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);

    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, VER.size() * sizeof(Vertex), &VER[0], GL_STATIC_DRAW);


// Vertex Positions
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    // Normals attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    // Vertex Texture Coords
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    int n = indices.size() * sizeof(unsigned int);


    glGenBuffers(1, &ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);


    glBindVertexArray(0);

    ShaderProgram shaderProgram;
    shaderProgram.loadShaders("shaders/vert.glsl", "shaders/frag.glsl");
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    while (!glfwWindowShouldClose(engine.getWindow()))
    {

        glfwPollEvents();

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glm::mat4 model, view, projection;

        model = glm::mat4(1.0f);

        orbitCamera.setLookAt(glm::vec3(0, 0, 0));
        orbitCamera.rotate(gYaw, gPitch);
        orbitCamera.setRadius(gRadius);

        model = glm::translate(model, glm::vec3(0, 0, 0));
        //model = glm::scale(model, glm::vec3(1, 0, 1));

        //model = scaleMat;

        projection = glm::perspective(glm::radians(45.0f), (float)engine.getWidth() / (float)engine.getHeight(), 0.00001f, 100.0f);

        shaderProgram.use();
        glm::vec3 viewPos;
        viewPos.x = orbitCamera.getPosition().x;
        viewPos.y = orbitCamera.getPosition().y;
        viewPos.z = orbitCamera.getPosition().z;

        shaderProgram.setUniform("projection", projection);
        shaderProgram.setUniform("view", orbitCamera.getViewMatrix());
        shaderProgram.setUniform("model", model);
        shaderProgram.setUniform("lightPos", glm::vec3(5, 10, 10));
        shaderProgram.setUniform("viewPos", viewPos);

        glBindVertexArray(vao);
        glDrawElements(GL_TRIANGLES,indices.size(), GL_UNSIGNED_INT, 0);
        //glDrawArrays(GL_TRIANGLES, 0, VER.size());
        glBindVertexArray(0);

        glfwSwapBuffers(engine.getWindow());
    }

    //cleanup
    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(1, &vbo);

    glfwTerminate();
    return 0;
}

void glfw_onKey(GLFWwindow *window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, GL_TRUE);
    }

    if (key == GLFW_KEY_E && action == GLFW_PRESS)
    {
        gWireFrame = !gWireFrame;
        if (gWireFrame)
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        else
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
}

void glfw_onMouseMove(GLFWwindow *window, double posX, double posY)
{
    static glm::vec2 lastMousePos = glm::vec2(0, 0);
    if (glfwGetMouseButton(engine.getWindow(), GLFW_MOUSE_BUTTON_LEFT) == 1)
    {
        gYaw -= ((float)posX - lastMousePos.x) * MOUSE_SENSTIVITY;
        gPitch += ((float)posY - lastMousePos.y) * MOUSE_SENSTIVITY;
    }
    if (glfwGetMouseButton(engine.getWindow(), GLFW_MOUSE_BUTTON_RIGHT) == 1)
    {
        float dx = 0.01f * ((float)posX - lastMousePos.x);
        float dy = 0.01f * ((float)posY - lastMousePos.y);
        gRadius += dx - dy;
    }
    lastMousePos.x = (float)posX;
    lastMousePos.y = (float)posY;
}

这是主要代码。其余只是基本的初始化代码,没什么特别的。 我试过更改 swapinterval 但这似乎不是问题所在。 如果有人想看,我可以为另一个 类 分享代码。而且我也试过降低子分区。

编辑*

将far plane的值增加到8000后:

还是不脆。

对第二张图片的编辑告诉你发生了什么......如果篡改 znear/zfar 改变了输出,这意味着你的深度缓冲区的位宽低于你想要使用的范围......

然而,增加 zfar 会使事情变得更糟(您只是出于某种原因看不到它,可能是它被截断了,或者是一些奇怪的数学精度奇异点)。

对我来说,select 飞机是这样的:

zfar/znear < (2^depth_buffer_bitwidth)/2
  1. 检查你depth_buffer_bitwidth

    尝试使用 24 位(您现在可能有 16 位)。如今,这应该适用于所有 gfx 卡。您也可以尝试 32 位,但这只适用于较新的卡。我正在使用此代码来获得最大的收益:

    • What is the proper OpenGL initialisation on Intel HD 3000?

    但是您使用的是 GLFW,因此您需要在其中找到操作方法...可能其中有一些提示...

  2. 尽可能增加znear

    篡改 znearzfar 影响大得多...

  3. 使用线性深度缓冲区

    这是大深度范围视图的最佳选择,例如在整个深度视图范围内覆盖 stuf 的地形。参见:

    但是你需要着色器和新的 api...我认为这在旧的 api 中是不可行的,但幸运的是你已经在新的 api 上了...

  4. 如果以上none就够了

    您可以将更多的平截头体堆叠在一起,代价是对同一几何体进行多次渲染。有关详细信息,请参阅:

如何初始化 OpenGL?

你在使用GL_BLEND吗? 使用混合可以很好地获得消除锯齿的多边形边缘,但这也意味着即使绘制了非常半透明的片段,您的 z 缓冲区也会更新。这可以防止绘制具有相同 z 深度的其他不透明片段,这可能是导致这些孔洞的原因。您可以尝试禁用 GL_BLEND 以查看问题是否消失。

你用的是什么深度函数? 默认设置为 GL_LESS。您可能想尝试 glDepthFunc(GL_LEQUAL); 因此将绘制具有相同 z 深度的片段。但是,由于舍入误差,这可能无法完全解决您的问题。