Hello GameDev!
I am porting an OpenGL 4.4 application to OpenGL 3.1 to get it to run on my Intel HD Graphics 3000 card (on my laptop). So far, things have been going well, but I ran into a problem when rendering more than one instance using the same vertex buffer (just changing the uniform buffer between the draw calls). When I do this, only the last instance is rendered. The non-ported code did not exhibit this problem and I just cannot see what I might have done to cause it to fail.
I have managed to isolate the bug in a minimal sample: http://pastebin.com/87DQhdrS
#include <SDL2/SDL.h>
#include <GL/gl3w.h>
#include <iostream>
#include <string>
const int OPENGL_MAJOR = 3;
const int OPENGL_MINOR = 1;
const int UNIFORM_BINDING = 1;
const char* VERTEX_SOURCE =
"#version 140 \n"
" \n"
"in vec2 in_position; \n"
" \n"
"layout(std140) uniform UniformBuffer \n"
"{ \n"
" vec4 offset; \n"
"}; \n"
" \n"
"void main() \n"
"{ \n"
" gl_Position = vec4(in_position, 0.0f, 1.0f);\n"
" gl_Position.x += offset.x; \n"
"} \n";
const char* FRAGMENT_SOURCE =
"#version 140 \n"
" \n"
"out vec4 out_color; \n"
" \n"
"void main() \n"
"{ \n"
" out_color = vec4(1.0f, 1.0f, 1.0f, 1.0f); \n"
"} \n";
struct UniformBuffer
{
float offset[4];
};
SDL_Window* window = nullptr;
SDL_GLContext glcontext = nullptr;
unsigned int viewport_width = 800;
unsigned int viewport_height = 600;
bool running = true;
GLuint vertexShader = 0;
GLuint fragmentShader = 0;
GLuint program = 0;
GLuint position_vbo = 0;
GLuint vao = 0;
UniformBuffer uniform_buffer_data;
GLuint uniform_buffer = 0;
void initialize();
void loadResources();
GLuint compileShader(const char* source, GLuint type);
void linkProgram(GLuint program);
void run();
void handleEvents();
void render();
int main(int argc, char* argv[])
{
initialize();
loadResources();
run();
return 0;
}
void initialize()
{
if (SDL_Init(0) != 0)
{
throw std::runtime_error("Failed to initialize SDL");
}
window = SDL_CreateWindow("gl3", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, viewport_width, viewport_height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (window == nullptr)
{
throw std::runtime_error("Failed to create SDL window");
}
SDL_GLcontextFlag flags = SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG;
#ifndef NDEBUG
flags = (SDL_GLcontextFlag)(flags | SDL_GL_CONTEXT_DEBUG_FLAG);
#endif
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, OPENGL_MAJOR);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, OPENGL_MINOR);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, flags);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
glcontext = SDL_GL_CreateContext(window);
if (glcontext == nullptr)
{
throw std::runtime_error("Failed to create OpenGL context");
}
if (gl3wInit() != 0)
{
throw std::runtime_error(std::string("Failed to initialize gl3w"));
}
if (gl3wIsSupported(OPENGL_MAJOR, OPENGL_MINOR) != 1)
{
throw std::runtime_error("OpenGL version not supported");
}
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glViewport(0, 0, viewport_width, viewport_height);
SDL_GL_SetSwapInterval(1);
}
void loadResources()
{
// VBOs.
float positions[] = { -0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f };
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &position_vbo);
glBindBuffer(GL_ARRAY_BUFFER, position_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, positions, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
// Shaders.
vertexShader = compileShader(VERTEX_SOURCE, GL_VERTEX_SHADER);
fragmentShader = compileShader(FRAGMENT_SOURCE, GL_FRAGMENT_SHADER);
program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
linkProgram(program);
glUniformBlockBinding(program, glGetUniformBlockIndex(program, "UniformBuffer"), UNIFORM_BINDING);
// UBOs.
uniform_buffer_data.offset[0] = 0.0f;
uniform_buffer_data.offset[1] = 0.0f;
uniform_buffer_data.offset[2] = 0.0f;
uniform_buffer_data.offset[3] = 0.0f;
glGenBuffers(1, &uniform_buffer);
glBindBufferBase(GL_UNIFORM_BUFFER, UNIFORM_BINDING, uniform_buffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformBuffer) * sizeof(float), &uniform_buffer_data, GL_DYNAMIC_DRAW);
}
GLuint compileShader(const char* source, GLuint type)
{
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE)
{
GLint logSize;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logSize);
std::string log;
if (logSize > 0)
{
int written;
log.resize(logSize);
glGetShaderInfoLog(shader, logSize, &written, &log[0]);
}
throw std::runtime_error("Failed to compile shader: " + log);
}
return shader;
}
void linkProgram(GLuint program)
{
glLinkProgram(program);
GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status != GL_TRUE)
{
GLint logSize;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logSize);
std::string log;
if (logSize > 0)
{
int written;
log.resize(logSize);
glGetProgramInfoLog(program, logSize, &written, &log[0]);
}
throw std::runtime_error("Failed to link program: " + log);
}
}
void run()
{
while (running)
{
handleEvents();
render();
}
}
void handleEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
{
running = false;
} break;
case SDL_WINDOWEVENT:
{
switch (event.window.event)
{
case SDL_WINDOWEVENT_RESIZED:
{
viewport_width = event.window.data1;
viewport_height = event.window.data2;
glViewport(0, 0, viewport_width, viewport_height);
} break;
}
} break;
}
}
}
void render()
{
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program);
glBindVertexArray(vao);
uniform_buffer_data.offset[0] = 1.3f;
glBindBufferBase(GL_UNIFORM_BUFFER, UNIFORM_BINDING, uniform_buffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformBuffer) * sizeof(float), &uniform_buffer_data);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
uniform_buffer_data.offset[0] = -0.4f;
glBindBufferBase(GL_UNIFORM_BUFFER, UNIFORM_BINDING, uniform_buffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformBuffer) * sizeof(float), &uniform_buffer_data);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
SDL_GL_SwapWindow(window);
}
I am using SDL2 and GL3W and I am working in Visual Studio 2013 on Windows 10. The biggest change when I ported was that I no longer could specify the uniform binding point in the shader, but had to use glUniformBlockBinding instead. Anyone familiar with this version of OpenGL who could tell me if I am doing something stupid?
Cheers!
- Rarosu