OpenGL requires a window, loader, and math library. We will use the following dependencies for our project:
- glad (loader)
- SDL3 (window)
- glm (math)
At least for this guide, our only interest is in OpenGL, so the dependencies have been abstracted away. When you build the repository, the only file you need to code on is the draw.hpp
file.
Prerequisites
git clone https://gitea.com/brandonchui/ogl_basics.git
cd ogl_basics
There are 2 build scripts, depending on your operating system.
Windows:
.\build.bat
Mac:
chmod +x build.sh
./build.sh
Beginning
We start from our draw.hpp
file as mentioned earlier. You should see the following:
//draw.hpp
#pragma once
#include <glad/glad.h>
#include <glm/glm.hpp>
//globals
// ???
//functions
inline void DrawTriangle()
{
//TODO
}
By the end of this guide, we will be able to draw a triangle with about 50 lines of code filling in only the DrawTriangle()
function.
Making sure OpenGL works
Let's test that we can ask OpenGL to clear its screen and color in our window
inline void DrawTriangle()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //light green
glClear(GL_COLOR_BUFFER_BIT);
}
Creating the VBO (Vertex Buffer Object)
We use the VBO to store vertices into the GPU memory. We have defined our triangle as:
//globals
float vertices[] = {-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f};
With our vertices, we define our vbo
, which will just a type int
. This is the buffer id for our object.
int vbo;
glGenBuffers(1, &vbo);
Then we need to bind that buffer, in other words, we are telling OpenGL that we will not select that buffer to being worked on.
int vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);


We now need a way to copy our defined vertices into the buffer's memory, which the glBufferData
comes in.
int vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
Checkpoint
Creating the shader
Ideally, this will be a .glsl
file, but for now we can create this as a char*
. We would need a string importer if we were to use the .glsl
method.
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
Now, we create our shader with the id
again:
int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
Not done quite yet with shaders - we still have to attach the shader solurce to the object, and then compile it.
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
Fragment Shaders
You can think of the fragment shader as the color representation of a given pixel on screen. We will define it as a string-like type again:
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
The exact steps from the previous section will be performed:
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
Shader Program
We have our compiled shaders, but they still need to go somewhere. In comes the shader program, where the shaders will link to and then activated.
int shaderProgram;
shaderProgram = glCreateProgram();
With our shaderProgram
ready to go, we just simply attach whatever shaders we have made previous:
int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
To use our newly created shaders, we just call the glUseProgram
function:
glUseProgram(shaderProgram);
To tidy things up, since we compiled our shader, we don't need the leftovers, so we can safely delete it:
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
Creating the VAO (Vertex Array Object)
This will be similar to our vbo
:
int VAO;
glGenVertexArrays(1, &VAO);
We then bind it so we can select it
int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
Now we bind the buffer:
int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
We then need to set your vbo
pointers
int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
Our Triangle
Finally, in our rendering loop we call our draw function on each frame:
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);