glVertexAttribDivisor在nVidia上运行不正常?

时间:2017-03-02 15:56:36

标签: c++ linux opengl sdl sdl-2

我写了一些代码来测试OpenGL中的实例化:

#include "exception"
#include "fstream"
#include "GL/glew.h"
#include "glm/fwd.hpp"
#include "glm/glm.hpp"
#include "iostream"
#include "SDL2/SDL.h"
#include "sstream"
#include "stdexcept"
#include "unordered_map"

int                     g_xRes = 1024, g_yRes = 768;
SDL_Window *            g_window = nullptr;
SDL_GLContext           g_context = 0;

void init(int * argc, char ** argv);
void cleanup();
void mainLoop();
GLuint loadShader(const char * filepath, GLenum type);
GLuint makeProgram(GLuint vs, GLuint gs, GLuint fs, 
                   const std::unordered_map<std::string, GLuint> & fragDataLocations);

// This is done in order to prevent missing reference error on Windows
#ifdef main
# undef main
#endif

int main(int argc, char ** argv)
{
    try
    {
        init(&argc, argv);
        mainLoop();
    }
    catch(std::exception & ex)
    {
        std::cerr << argv[0] << ": " << ex.what() << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

void mainLoop()
{
    glm::vec3 vertices[] = {
        glm::vec3(0, 0, 0),
        glm::vec3(0, 0.25, 0),
        glm::vec3(0.25, 0, 0),
        glm::vec3(0.25, 0.25, 0),
        glm::vec3(0.50, 0, 0),
        glm::vec3(0.50, 0.25, 0),
        glm::vec3(0.75, 0, 0),
        glm::vec3(0.75, 0.25, 0),
    };

    GLushort elements[] = {
        0, 1, 2, 3, 2, 1, 4, 5, 6, 7, 6, 5
    };

    glm::vec3 colors[] = {
        glm::vec3(1, 0, 0),
        glm::vec3(0, 1, 0)
    };

    GLuint vao, vboPos, vboCol, ebo;

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

    glGenBuffers(1, &vboCol);
    glGenBuffers(1, &vboPos);
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ARRAY_BUFFER, vboPos);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, vboCol);
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);

    GLuint vs = loadShader("vs.glsl", GL_VERTEX_SHADER),
           fs = loadShader("fs.glsl", GL_FRAGMENT_SHADER);

    GLuint program = makeProgram(vs, 0, fs, { { "outColor", 0 } });
    glUseProgram(program);

    glBindBuffer(GL_ARRAY_BUFFER, vboPos);
    GLuint posAttr = glGetAttribLocation(program, "pos");
    glEnableVertexAttribArray(posAttr);
    glVertexAttribPointer(posAttr, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboCol);
    GLuint colAttr = glGetAttribLocation(program, "col");
    glEnableVertexAttribArray(colAttr);
    glVertexAttribPointer(colAttr, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glVertexAttribDivisor(colAttr, 1);

    glEnable(GL_DEPTH_TEST);

    SDL_Event e;
    while(true)
    {
        while(SDL_PollEvent(&e) != 0)
        {
            if(e.type == SDL_QUIT)
                return;
        }

        glClearColor(0, 0, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glDrawElementsInstanced(GL_TRIANGLES, sizeof(elements)/sizeof(elements[0]), GL_UNSIGNED_SHORT, 
            nullptr, sizeof(colors)/sizeof(colors[0]));

        assert(glGetError() == GL_NO_ERROR);

        SDL_GL_SwapWindow(g_window);
    }
}

void init(int * argc, char ** argv)
{
    std::ostringstream errMsgSStream;

    // Initialize SDL
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
    {
        errMsgSStream << "" << SDL_GetError();
        throw std::runtime_error(errMsgSStream.str());
    }

    // Manage OpenGL attributes
    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_DOUBLEBUFFER, true);
    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_ALPHA_SIZE, 8);

    // Create window
    g_window = SDL_CreateWindow("Example", SDL_WINDOWPOS_CENTERED, 
      SDL_WINDOWPOS_CENTERED, g_xRes, g_yRes, SDL_WINDOW_OPENGL);
    if(!g_window)
    {
        errMsgSStream << "" << SDL_GetError();
        throw std::runtime_error(errMsgSStream.str());
    }

    // Create a new device context
    g_context = SDL_GL_CreateContext(g_window);
    if(!g_context)
    {
        errMsgSStream << "" << SDL_GetError();
        throw std::runtime_error(errMsgSStream.str());
    }

    // Initialize GLEW
    glewExperimental = GL_TRUE;
    if(glewInit() != GLEW_OK)
    {
        errMsgSStream << "failed to initialize GLEW";
        throw std::runtime_error(errMsgSStream.str());
    }

    // Apparently GLEW can make glGetError return GL_INVALID_ENUM even if initialized correctly 
    while(glGetError() != GL_NO_ERROR);

    if(atexit(cleanup))
    {
        errMsgSStream << "failed to register cleanup function";
        throw std::runtime_error(errMsgSStream.str());
    }

    return;
}

void cleanup()
{
    SDL_GL_DeleteContext(g_context);
    SDL_DestroyWindow(g_window);
    SDL_Quit();
}

GLuint loadShader(const char * filepath, GLenum type)
{
    std::ifstream shaderFile;

    shaderFile.exceptions(std::ios_base::failbit);
    try {
        shaderFile.open(filepath);
    } catch(std::exception & ex) {
        std::ostringstream ss;
        ss << "failed to open file '" << filepath << "': " << ex.what();
        throw std::runtime_error(ss.str());
    }
    shaderFile.exceptions(std::ios_base::goodbit);

    GLuint res = glCreateShader(type);

    // Get the length of the file
    shaderFile.seekg(0, shaderFile.end);
    size_t fileSize = shaderFile.tellg();
    shaderFile.seekg(0, shaderFile.beg);

    // Allocate space for, and read the header source
    char * shaderSrc = new char[fileSize + 1];
    shaderFile.read(shaderSrc, fileSize);
    shaderSrc[fileSize] = '\0';

    // Try to compile shader
    GLint status;
    glShaderSource(res, 1, &shaderSrc, nullptr);
    glCompileShader(res);
    glGetShaderiv(res, GL_COMPILE_STATUS, &status);
    if(status != GL_TRUE)
    {
        char buf[0x10000];
        std::ostringstream ss;
        glGetShaderInfoLog(res, 0x10000, nullptr, buf);
        ss << "failed to compile file '" << filepath << "':" << std::endl << buf;
        glDeleteShader(res);
        throw std::runtime_error(ss.str());
    }

    return res;
}

GLuint makeProgram(GLuint vs, GLuint gs, GLuint fs, 
                   const std::unordered_map<std::string, GLuint> & fragDataLocations)
{
    GLuint res = glCreateProgram();

    // Attach shaders
    if(vs) glAttachShader(res, vs);
    if(gs) glAttachShader(res, gs);
    if(fs) glAttachShader(res, fs);

    // Bind data locations
    for(const auto & ent : fragDataLocations)
        glBindFragDataLocation(res, ent.second, ent.first.c_str());

    // Link program
    GLint status;
    glLinkProgram(res);
    glGetProgramiv(res, GL_LINK_STATUS, &status);
    if(vs) glDetachShader(res, vs);
    if(gs) glDetachShader(res, gs);
    if(fs) glDetachShader(res, fs); 
    if(status != GL_TRUE)
    {
        char buf[0x10000];
        std::ostringstream ss;
        glGetProgramInfoLog(res, 0x10000, nullptr, buf);
        ss << "failed to link program: " << std::endl << buf;
        glDeleteProgram(res);
        throw std::runtime_error(ss.str());
    }

    return res;
}

VS

#version 330

in vec3 pos;
in vec3 col;

out vec3 fCol;

void main()
{
    gl_Position = vec4(pos, 1);
    fCol = col;
}

PS:

#version 330

in vec3 fCol;

out vec4 outColor;

void main()
{
    outColor = vec4(fCol, 1);
}

我期待的输出看起来像这样:

enter image description here

然而,我得到的是:

enter image description here

我的代码有问题,或者我的驱动程序没有正确实现此功能(我在Ubuntu Linux 16.04上使用专有的NVIDIA驱动程序v340.102)?

1 个答案:

答案 0 :(得分:1)

首先,你的$item_id = 752;表示当有3个时只有1个组件。然后,你渲染2个实例,每个实例都有两个四边形,具有相同的位置,所以你看不到第二个实例

您可以将第二个实例的位置改为例如glVertexAttribPointer至少看到两个实例。