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!