Search code examples
c++openglglfwopengl-3opengl-4

Polygon tearing in OpenGL


500x500 grid with 1000 sub Divisions:

img

Just one question.

Why is this happening ?

#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;
}

This is the main code. Rest is just basic initializing code, nothing fancy. I've tried changing the swapinterval but that doesn't seems to be the problem. I can share code for the other classes if anyone wants to take a look. And I've also tried lowering the sub divisions.

Edit*

After increasing the value of far plane to 8000:

img

Still not crisp.


Solution

  • the edit with second image is telling you what is happening ... if tampering with znear/zfar changes output like that it means your depth buffer has low bitwidth to the range you want to use...

    However increasing zfar should make things worse (you just for some reason don't see it maybe its cut off or some weird math accuracy singularity).

    for me its usual to select the planes so:

    zfar/znear < (2^depth_buffer_bitwidth)/2
    
    1. check you depth_buffer_bitwidth

      Try to use 24 bits (you might have 16 bits right now). That should work on all gfx cards these days. You can try 32 bits too but that will work only on newer cards. I am using this code to get the max I can:

      However you are using GLFW so you need to find how to do it in it ... probably there is some hint for this in it ...

    2. increase znear as much as you can

      tampering znear has much much more impact than zfar...

    3. Use linear depth buffer

      this is the best option for large depth range views like terrains that covers stuf in whole depth view range. See:

      however you need shaders and new api for this... I do not think this is doable in old api but luckily you are on new api already ...

    4. if none of above is enough

      You can stack up more frustrums together at a cost of multiple rendering of the same geometry. for more info see: