Starting to refresh my OpenGL brain and figure a good chance to make tool sharp to survive in wild OpenGL Programming... TL;DR code

Why?

I know you should have a framework or some engine anyway. But what if not or you need to do some low level API hack? You probably need to anyway find a toy example to get started.

Another point is: graphics API is designed as driver friendly (you can even say unreadable). To do a simple task, you need very strange function combination (e.g. genbuffer, bindbuffer ...) and you need to handle all the states that you probably cannot remember all.

So the snippets are the quick resources to go when you run into these cases and they can also be a good tutorial for beginners.

Prerequisites

Install your favorite snippets engine e.g. coc-snippets and point it to your customized snippets like I did here. Setting up a working environment can be frustrating. If you work in *nix, I got your back. Just install Nix and direnv. Works best for Linux machine though. For more details, see my previous post. BTW, my script even help generate the glad version based on your need. Feel free to make changes to default.nix.

GL Snippets

Header

I meant survive in wild but not naked. You need these almost must-have headers. With GLFW, you don't need to worry about GL context binding to native window. With GLAD you care less about OGL function pointers. GLM saves you some scratch work to implement e.g. vec2.

// Generate your own header https://github.com/Dav1dde/glad/tree/glad2
#include <glad/glad.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

Basic Assets

Geometry

Would you like a triangle?

const GLfloat triangleVertex[] = {
   -1.0f, -1.0f, 0.0f,
   1.0f, -1.0f, 0.0f,
   0.0f,  1.0f, 0.0f,
};

Cube, and teapot are also in snippets, but a bit wordy to show here.

Shader

For exmaple, to quickly grab a working embedded shader is very handy if you just wanted to draw A SINGLE TRIANGLE!

const char* vertexShader =
"#version ${1:400}\n"
"in vec3 vp;"
"void main() {"
"  gl_Position = vec4(vp, 1.0);"
"}";

and of course there are fragment shader as well.

Push/Pop States

API leaves for you to manage all states, my dear. Let's finish it with one blow by enumerating all states possible. Cause you never know which state is giving you black screen 🤦. It's rocket launcher solution: no time for these little hide & see games😆!

GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer);
GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
GLint last_blend_src; glGetIntegerv(GL_BLEND_SRC, &last_blend_src);
GLint last_blend_dst; glGetIntegerv(GL_BLEND_DST, &last_blend_dst);
GLint last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, &last_blend_equation_rgb);
GLint last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &last_blend_equation_alpha);
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);

**O

VBO, VAO, EBO, FBO API is unhuman. But here a simple explanation: VBO is just buffer to store vertex, FBO is buffer to store pixels, while VAO is wrapper to manage VBO. Basically they are a bunch of gen and bind functions. Just checkout e.g. snippets.

Image & Texture

To test a simple image...

int width, height, channel;
unsigned char* image = stbi_load("image.png", &width, &height, &channel, STBI_rgb); // or STBI_rgb_alpha

if(image == nullptr)
    std::cout << "Cannot load image" << std::endl;

glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //GL_LINEAR_MIPMAP_LINEAR 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexImage2D(GL_TEXTURE_2D, 0, channel == 3 ? GL_RGB : GL_RBGA, w, h, 0, channel == 3 ? GL_RGB : GL_RBGA, GL_UNSIGNED_BYTE, image);
//glGenerateMipmap( GL_TEXTURE_2D); // generate mipmap if we use that

glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(image);

MVP

Model View Projection refresher

glm::mat4 Projection = glm::perspective(glm::radians(fovdegree), aspectRatio, near, far);
// Or, for an ortho camera :
//glm::mat4 Projection = glm::ortho(x0, x1, y0, y1, z0, z1); // In world coordinates
  
// View
glm::mat4 View = glm::lookAt(
    glm::vec3(0,0,-1), // Camera
    glm::vec3(0,0,0), // Lookat
    glm::vec3(0,1,0)  // Up
    );
  
// Model
glm::mat4 Model = glm::mat4(1.0f);
// MVP
glm::mat4 mvp = Projection * View * Model;

Test Our Snippets

Case 1

To draw a triangle to see if opengl snippet works. First glheader to include everthting and then get some globals with shader glsimplevsfs, and geometry gltriangle. Then create main function body with glhellworld and fill the intialization code with glvbo, then glvao and then in the main loop do gldraw. Totally time spent ~1 min if most variables are kept.

Case 2

TODO

Vulkan Snippets

Ok, OpenGL is not that bad. Let's crack Vulkan next time!

...