【问题标题】:Artifacts rendering high-poly procedural meshes with OpenGL使用 OpenGL 渲染高多边形程序网格的工件
【发布时间】:2014-11-21 19:58:52
【问题描述】:

我在渲染一些程序生成的网格时遇到了一些问题。在渲染高多边形数网格时会出现一些非常奇怪的伪影(实际上并没有那么多)。我已经能够隔离问题,但我不知道为什么会这样。当我生成低多边形网格时,不会出现问题。

我创建了一个小演示,所以你可以下载源代码。 (见下文)

在我的应用程序中,当网格大约有大约 90 个三角形(120 个顶点和 270 个索引)时,这些伪影开始出现。要看到它出现在演示中,请将 initMesh 设置为 150 个环和 150 个扇区。

我已经为此工作了一周,但我无法解释为什么会发生这种情况。会是什么?

这是我的网格类:

#include "Mesh.h"

Mesh::Mesh()
{
    _vao = 0;

    _verticesVbo = 0;
    _texCoordsVbo = 0;
    _normalsVbo = 0;
    _indicesVbo = 0;
}

Mesh::~Mesh()
{   
    glDeleteBuffers(1, &_verticesVbo);
    glDeleteBuffers(1, &_texCoordsVbo);
    glDeleteBuffers(1, &_normalsVbo);
    glDeleteBuffers(1, &_indicesVbo);
    glDeleteVertexArrays(1, &_vao);
}

Mesh* Mesh::New(vector<Vertex> &vertices, vector<GLuint> &indices)
{
    Mesh* mesh = new Mesh();
    mesh->AddVertices(vertices, indices);
    return mesh;
}

void Mesh::AddVertices(vector<Vertex> &vertices, vector<GLuint> &indices)
{
    _vertices = vertices;
    _indices = indices;

    GLuint verticesSize = vertices.size() * 3 * sizeof(GLfloat);
    GLuint texCoordsSize = vertices.size() * 2 * sizeof(GLfloat);
    GLuint normalsSize = vertices.size() * 3 * sizeof(GLfloat);
    _indicesSize = indices.size() * sizeof(GLuint);

    GLfloat* vertexBuffer = new GLfloat[vertices.size() * 3];
    GLfloat* texCoordBuffer = new GLfloat[vertices.size() * 2];
    GLfloat* normalBuffer = new GLfloat[vertices.size() * 3];

    CreateBuffers(vertices, vertexBuffer, texCoordBuffer, normalBuffer);

    glGenVertexArrays(1, &_vao);
    glBindVertexArray(_vao);

    glGenBuffers(1, &_verticesVbo);
    glBindBuffer(GL_ARRAY_BUFFER, _verticesVbo);
    glBufferData(GL_ARRAY_BUFFER, verticesSize, vertexBuffer, GL_STATIC_DRAW);

    glGenBuffers(1, &_texCoordsVbo);
    glBindBuffer(GL_ARRAY_BUFFER, _texCoordsVbo);
    glBufferData(GL_ARRAY_BUFFER, texCoordsSize, texCoordBuffer, GL_STATIC_DRAW);

    glGenBuffers(1, &_normalsVbo);
    glBindBuffer(GL_ARRAY_BUFFER, _normalsVbo);
    glBufferData(GL_ARRAY_BUFFER, normalsSize, normalBuffer, GL_STATIC_DRAW);

    glGenBuffers(1, &_indicesVbo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indicesVbo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indicesSize, &indices[0], GL_STATIC_DRAW);

    delete[] vertexBuffer;
    delete[] texCoordBuffer;
    delete[] normalBuffer;
}

void Mesh::CreateBuffers(vector<Vertex> &vertices, 
                         GLfloat* &vertexBuffer, 
                         GLfloat* &texCoordBuffer, 
                         GLfloat* &normalBuffer)
{
    vector<Vertex>::iterator i;  
    unsigned int vIndex = 0;
    unsigned int tIndex = 0;
    unsigned int nIndex = 0;

    for (i = vertices.begin(); i != vertices.end(); ++i)
    {
        Vertex vertex = *i;

        GLfloat x = vertex.GetPosition().x;
        GLfloat y = vertex.GetPosition().y;
        GLfloat z = vertex.GetPosition().z;

        GLfloat u = vertex.GetTexCoord().x;
        GLfloat v = vertex.GetTexCoord().y;

        GLfloat r0 = vertex.GetNormal().x;
        GLfloat s0 = vertex.GetNormal().y;
        GLfloat t0 = vertex.GetNormal().z;

        vertexBuffer[vIndex++] = x;
        vertexBuffer[vIndex++] = y;
        vertexBuffer[vIndex++] = z;

        texCoordBuffer[tIndex++] = u;
        texCoordBuffer[tIndex++] = v;

        normalBuffer[nIndex++] = r0;
        normalBuffer[nIndex++] = s0;
        normalBuffer[nIndex++] = t0;
    }
}

void Mesh::Render()
{   
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, _verticesVbo);
    glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, _texCoordsVbo);
    glVertexAttribPointer((GLuint)1, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, _normalsVbo);
    glVertexAttribPointer((GLuint)2, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indicesVbo);
    glDrawElements(GL_TRIANGLES, _indicesSize, GL_UNSIGNED_INT, 0);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
}

这里是创建网格的代码:

void initMesh(float radius, int rings, int sectors)
{
    float piOver2 = M_PI * 0.5f;

    vector<Vertex> vertices;
    vector<unsigned int> indices;

    float const R = 1.0f/(float)(rings);
    float const S = 1.0f/(float)(sectors);
    unsigned int r, s;

    for(r = 0; r < rings + 1; r++) 
    {
        for(s = 0; s < sectors + 1; s++) 
        {
            float y = sin(piOver2 * r * R);
            float x = cos(2.0 * M_PI * s * S) * sin(piOver2 + piOver2 * r * R);
            float z = sin(2.0 * M_PI * s * S) * sin(piOver2 + piOver2 * r * R);

            vec3 position = vec3(x, y, z) * radius;
            vec3 normal = normalize(vec3(x, y, z)) * radius;
            vec2 texCoord = vec2(s * R, r * R) * radius;

            vertices.push_back(Vertex(position, texCoord, normal));
        }
    }

    for(r = 0; r < rings; r++) 
    {
        for(s = 0; s < sectors; s++) 
        {
            int a = r * (sectors + 1) + s;
            int b = (r + 1) * (sectors + 1) + s;
            int c = (r + 1) * (sectors + 1) + (s + 1);
            int d = r * (sectors + 1) + (s + 1);

            indices.push_back(a);
            indices.push_back(b);
            indices.push_back(c);

            indices.push_back(c);
            indices.push_back(d);
            indices.push_back(a);
        }
    }

    _mesh = Mesh::New(vertices, indices);
}

这里是初始化OpenGL的代码:

bool createGLWindow()
{
    _window = SDL_CreateWindow(
        "TestMesh",
        SDL_WINDOWPOS_CENTERED, 
        SDL_WINDOWPOS_CENTERED, 
        1024, 
        768, 
        SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);

    if(_window == NULL)
    {
        LOG("Window could not be created! SDL_Error: " << SDL_GetError());
        return false;
    }

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    _glContext = SDL_GL_CreateContext(_window);

    if (!_glContext)
    {
        LOG("Could not create context:" << SDL_GetError());
        return false;
    }

    glewExperimental = GL_TRUE;

    GLenum glewInitStatus = glewInit();

    if(glewInitStatus != GLEW_OK)
    {
        LOG("Error" << glewGetErrorString(glewInitStatus))
            return false;
    }

    return true;
}

这里是渲染函数:

void render()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    _shader->Bind();
    _shader->GetUniform("mvp").Set(_projectionMatrix * _viewMatrix * _modelMatrix);
    _shader->GetUniform("color").Set(_color);
    _mesh->Render();
    _shader->Unbind();
}

这些是顶点着色器

#version 330

in vec3 inPosition;
in vec2 inTexCoord;
in vec3 inNormal;

uniform mat4 mvp;

out vec3 fragPosition;
out vec2 fragTexCoord;
out vec3 fragNormal;

void main()
{
    gl_Position = mvp * vec4(inPosition, 1.0);

    fragTexCoord = inTexCoord;
    fragPosition = inPosition;
    fragNormal = inNormal;
}

和片段着色器:

#version 330

uniform vec4 color;

in vec3 fragPosition;
in vec2 fragTexCoord;
in vec3 fragNormal;

out vec4 fragColor;

void main(void)
{
    vec3 lightPos = vec3(1.0, 1.0, 1.0);
    fragColor = max(dot(lightPos, fragNormal), 0.0) * 0.8 * color + color * 0.1;
}

渲染低多边形时的结果:

渲染高多边形时的结果:

您可以在这里下载演示的源代码:

demo + source code

【问题讨论】:

    标签: opengl rendering sdl artifacts


    【解决方案1】:

    用于索引缓冲区大小的单位不一致。在AddVertices() 方法中,它以字节为单位计算大小:

    _indicesSize = indices.size() * sizeof(GLuint);
    

    随后在与glBufferData() 的参数相同的方法中正确使用它,它确实需要以字节为单位的大小:

    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indicesSize, &indices[0], GL_STATIC_DRAW);
    

    但它也在Render() 方法中用作glDrawElements() 的参数:

    glDrawElements(GL_TRIANGLES, _indicesSize, GL_UNSIGNED_INT, 0);
    

    在这种情况下,传入的值需要是索引数,而不是字节大小。所以参数的值是大了 4 倍。

    您可能希望将成员变量设置为仅索引的数量:

    _indicesSize = indices.size();
    

    但请注意,您仍将字节大小传递给gBufferData()。

    另一个问题是,至少在发布的部分代码中,您从未启用深度测试。您需要在初始化期间的某个地方执行此操作:

    glEnable(GL_DEPTH_TEST);
    

    【讨论】:

      猜你喜欢
      • 2011-08-16
      • 2011-10-22
      • 1970-01-01
      • 1970-01-01
      • 2012-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-29
      相关资源
      最近更新 更多