OpenGL, oh my god

Started by
6 comments, last by BBeck 7 years, 7 months ago

Was trying to port my game from SDL2's unoptimized immediate mode to something more efficient (vertexArrays, VBO).... But guys, after two days i already gave up. Why the heck is OpenGL such a nightmare ??

- clean documentation is impossible to find

- 5000 ways of doing the same thing in more or less confusing ways

- shaders ?? i just want to display a bunch of batched 2D textures...

- most tutorials are outdated or focus only on a small part of the problem

Please, can you suggest me a way of doing modern 2D rendering without having my brain explode ?

I'm using C, with SDL2 for everything (input, audio, events...) which is nice except its rendering part which is a bit outdated

Advertisement

Use SFML? :P

OpenGL is a bit confusing, precisely because it has both changed dramatically over the past ~20 years, but also has tried to remain backwards compatible, with the end result being OpenGL is pretty bloated - similar to Win32, except OpenGL is also literally designed-by-committee.

Addressing your last two points:

3) Even for just rendering a texture, you want a shader. The shader can be stupidly simple, but by saying "Why do I need a shader for just drawing a texture!" you yourself are walking into the same problem you were complaining about: Why should there be more than one way of doing things? Why should there be "Here's how you drawn if you want a your draw call to just involve a single texture, and here's an entirely different way to draw if you want your draw calls to involve more than just a single texture!"

4) There are plenty of modern OpenGL tutorials also. I found this tutorial helpful.

You can get a simple base going on with some basic setup code. Download GLEW and get that working if you haven't already.

I will post some code without error checking, and hopefully this step by step will help you understand better.

This is not a full example, and there might even be a typo in here, but if you start from the main function and read what's happening line by line, you will come to an understanding of how it all comes together.

Keep in mind the shader must have input attributes 0: vec4 position, 1: vec4 color and 2: vec2 uv.

This is intended to be a reference if you're stuck, not a complete example.

SDL_GLContext gl_context = nullptr;

std::string read_file(std::string path) {
std::ifstream file(path, std::ios::binary);
if (!file.is_open()) { return ""; }
std::stringstream result;
result << file.rdbuf();
return result.str();
}

void init_gl(SDL_Window* window) {

gl_context = SDL_GL_CreateContext(window);

glewInit();

glShadeModel(GL_SMOOTH);

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

}

int load_shader(int type, std::string path) {

int id = glCreateShader(type);

const char* source = read_file(path).c_str();

glShaderSource(id, 1, &source, 0);

glCompileShader(id);

return id; // look at glGetShaderInfoLog later on

}

int load_shader_program(std::string path_vert, std::string path_frag) {

int shader_program_id = glCreateProgram();

int vert_id = load_shader(GL_VERTEX_SHADER, path_vert);

int frag_id = load_shader(GL_FRAGMENT_SHADER, path_frag);

glAttachShader(shader_program_id, vert_id);

glAttachShader(shader_program_id, frag_id);

glLinkProgram(shader_program_id);

// look at glGetProgramInfoLog, glValidateProgram and glGetProgramiv later on

return shader_program_id;

}

struct vector4f { float x = 0.0f, y = 0.0f, z = 0.0f, w = 0.0f; };

struct vector3f { float x = 0.0f, y = 0.0f, z = 0.0f; };

struct vector2f { float x = 0.0f, y = 0.0f; };

struct gl_vertex {

vector4f position;

float r = 1.0f, g = 1.0f, b = 1.0f, a = 1.0f;

vector2f uv;

};

struct gl_shape {

int vao = 0;

int index_buffer = 0, vertex_buffer = 0;

std::vector<unsigned int> indices;

std::vector<gl_vertex> vertices;

};

void use_gl_shape(gl_shape* shape) {

glBindVertexArray(shape->vao);

glBindBuffer(GL_ARRAY_BUFFER, shape->index_buffer);

glBindBuffer(GL_ARRAY_BUFFER, shape->vertex_buffer);

}

gl_shape create_quad() {

gl_shape quad;

quad.indices = { 0, 1, 2, 3, 2, 0 };

quad.vertices.insert(quad.vertices.begin(), 4, gl_vertex());

quad.vertices[0].position = { 0.0f, 0.0f, 0.0f, 1.0f };

quad.vertices[1].position = { 1.0f, 0.0f, 0.0f, 1.0f };

quad.vertices[2].position = { 1.0f, 1.0f, 0.0f, 1.0f };

quad.vertices[3].position = { 0.0f, 1.0f, 0.0f, 1.0f };

quad.vertices[0].uv = { 0.0f, 0.0f };

quad.vertices[1].uv = { 1.0f, 0.0f };

quad.vertices[2].uv = { 1.0f, 1.0f };

quad.vertices[3].uv = { 0.0f, 1.0f };

glGenVertexArrays(1, &quad.vao);

glGenBuffers(1, &quad.index_buffer);

glGenBuffers(1, &quad.vertex_buffer);

use_gl_shape(&quad);

glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned int) * quad.indices.size(), &quad.indices[0], GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(gl_vertex) * quad.vertices.size(), &quad.vertices[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), nullptr);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*) (4 * sizeof(float)));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*) (8 * sizeof(float)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);

return quad;

}

int main() {

// ... setup ...

init_gl(window);

int shader_program = load_shader_program("shader.vert", "shader.frag");

gl_shape quad = create_quad();

glClearColor(0.2f, 0.2f, 0.2f, 1.0f);

while (true) {

glClear(GL_COLOR_BUFFER_BIT);

glUseProgram(shader_program);

// <- This is where you set your projection and view matrix uniforms ->

// ...

// <- This is where you set your model matrix uniform ->

use_gl_shape(&quad);

glDrawElements(GL_TRIANGLES, shape.indices.size(), GL_UNSIGNED_INT, &shape.indices[0]);

SDL_GL_SwapWindow(window);

}

// ... cleanup ...

return 0;

}

If you need help figuring out projection and view matrices, feel free to ask.

much thanks for your help!

I found a nice tutorial too : http://learnopengl.com/#!In-Practice/2D-Game/Rendering-Sprites

Will probably manage to do something with that...

Here's a couple more good resources for modern opengl (which I found in my bookmarks from a while back!) https://open.gl/, and http://www.opengl-tutorial.org/. This video is quite funny and shares your frustration

:)

Learning a framework or something big like OpenGL C api can be TREMENDOUS work.

Take the time to learn the new openGL 3.0 api.

Make small changes to your project, for example don't switch to programmable shaders until you finished transforming to using VBO's with VAO.

I'd even advise to rewrite everything, if it's too hard (Limited resources) then take the first suggestion.

If you are not that needy, try refactoring using SFML. It basically can handle things for you.

Was trying to port my game from SDL2's unoptimized immediate mode to something more efficient (vertexArrays, VBO).... But guys, after two days i already gave up. Why the heck is OpenGL such a nightmare ??

- clean documentation is impossible to find

- 5000 ways of doing the same thing in more or less confusing ways

- shaders ?? i just want to display a bunch of batched 2D textures...

- most tutorials are outdated or focus only on a small part of the problem

Please, can you suggest me a way of doing modern 2D rendering without having my brain explode ?

I'm using C, with SDL2 for everything (input, audio, events...) which is nice except its rendering part which is a bit outdated

Because OpenGL like DIrectx is no longer using a fixed pipeline, and swapped to a programmable pipeline, you must use shaders to render everything now. This includes applying textures to models. This is not exclusive to OpenGL, DirectX also does this.

The general rule of thumb is that you're required to have a Vertex shader (which transforms everything from world space to screen space at the most basic level), and a fragment shader (which handles the process of actually drawing things to the screen.)

This might seem stupid, but it's actually really efficient. The same shader can be reused a thousand times over.

So when you now render an object, you bind data to resource buffers,including textures and then send the render call. This is what allows us to do batching so well. And a lot of other neat tricks.

I feel your pain.

Modern OGL is really more for 3D games. That's what the big boys make and that's what the graphics cards are designed to make. So doing 2D games with it is kind of awkward to say the least.

Quite frankly, you might benefit from learning 3D game programming in order to do your 2D projects if you want to do OGL. Because really that's what a 2D game is in OGL these days: a 3D game that simulates 2D.

SDL might kind of help you without getting into 3D if 2D is truly what you want to do. But as far as pure OGL, you need to get a solid understanding of 3D before you can do 2D, which is kind of counter intuitive and against conventional wisdom.

But what you're really doing in OGL is making 3D quads to represent 2D sprites and using an orthographic projection matrix instead of a perspective projection matrix.

Modern OGL is 3D. So you need to learn 3D in order to do 2D in modern OGL.

Once you understand 3D in OGL, 2D should be relatively simple. I know that's against conventional wisdom, but it's just the way it is. 2D is an after thought on modern graphics cards. 2D is just a confined way of doing 3D as far as modern graphics cards are concerned.

This topic is closed to new replies.

Advertisement