Created
June 9, 2025 23:13
-
-
Save christopherfujino/31ac9d2f45e5903ace260be888ed78cc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <glad/glad.h> | |
#include <GLFW/glfw3.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#define WIDTH 100 | |
#define HEIGHT 100 | |
#define ERROR_MSG_BUFFER_LEN 512 | |
// Explicitly specify core functionality, i.e. no deprecated features | |
// "in vec3" means input is 3 floats | |
// gl_Position is a predefined vec4 output variable | |
// the final 1.0 is the w-dimension in the vec4 | |
// | |
// This does nothing interesting because the input is already in the right | |
// format. | |
const char *vertexShaderSrc = "\ | |
#version 330 core \n\ | |
layout (location = 0) in vec3 aPos;\n\ | |
\n\ | |
void main() { \n\ | |
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n\ | |
}"; | |
// FragColor = RGBA | |
const char *fragmentShaderSrc = "\ | |
#version 330 core \n\ | |
\n\ | |
out vec4 FragColor; \n\ | |
\n\ | |
void main() { \n\ | |
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); \n\ | |
}"; | |
// A shader program links multiple shaders | |
static unsigned int shaderProgram = 0; | |
static unsigned int vertexShader = 0; | |
static unsigned int fragmentShader = 0; | |
/** Vertex Buffer Object */ | |
static unsigned int VBO = 0; | |
/** Vertex Array Object */ | |
static unsigned int VAO = 0; | |
static GLFWwindow *win = NULL; | |
/** Normalized device coordinates */ | |
const float vertices[] = { // x, y, z from -1.0 to 1.0 | |
// They will later be transformed by viewport transform | |
// vertex 1 | |
-0.5f, -0.5f, 0.0f, | |
// vertex 2 | |
0.5f, -0.5f, 0.0f, | |
// vertex 3 | |
0.0f, 0.5f, 0.0f}; | |
void framebuffer_size_callback(GLFWwindow *window, int width, int height) { | |
glViewport(0, 0, width, height); | |
} | |
void processInput(GLFWwindow *window) { | |
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { | |
glfwSetWindowShouldClose(window, 1); | |
} | |
} | |
void init() { | |
glfwInit(); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); | |
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | |
win = glfwCreateWindow(WIDTH, HEIGHT, "Foo Bar", | |
// monitor | |
NULL, | |
// share | |
NULL); | |
if (win == NULL) { | |
glfwTerminate(); | |
fprintf(stderr, "Could not create a window!\n"); | |
exit(1); | |
} | |
glfwMakeContextCurrent(win); | |
glfwSetFramebufferSizeCallback(win, framebuffer_size_callback); | |
// bind func pointer | |
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { | |
fprintf(stderr, "Failed to initialize GLAD\n"); | |
exit(1); | |
} | |
glViewport( | |
// x | |
0, | |
// y | |
0, | |
// width | |
WIDTH, | |
// height | |
HEIGHT); | |
// Shaders | |
vertexShader = glCreateShader(GL_VERTEX_SHADER); | |
// Bind the source to the shader | |
// glShaderSource(shader, count, src, length) | |
glShaderSource(vertexShader, 1, &vertexShaderSrc, NULL); | |
glCompileShader(vertexShader); | |
// check if compilation succeeded | |
int success = 0; | |
char infoLog[ERROR_MSG_BUFFER_LEN] = {0}; | |
// (shader, pname, *params) | |
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); | |
if (!success) { | |
glGetShaderInfoLog(vertexShader, ERROR_MSG_BUFFER_LEN, NULL, infoLog); | |
fprintf(stderr, "vertex shader compilation failed: %s\n", infoLog); | |
exit(1); | |
} | |
// Now compile fragment shader | |
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); | |
glShaderSource(fragmentShader, 1, &fragmentShaderSrc, NULL); | |
glCompileShader(fragmentShader); | |
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); | |
if (!success) { | |
glGetShaderInfoLog(fragmentShader, ERROR_MSG_BUFFER_LEN, NULL, infoLog); | |
fprintf(stderr, "fragment shader compilation failed: %s\n", infoLog); | |
exit(1); | |
} | |
// Now link shaders together to shaderProgram | |
shaderProgram = glCreateProgram(); | |
// (program, shader) | |
glAttachShader(shaderProgram, vertexShader); | |
glAttachShader(shaderProgram, fragmentShader); | |
glLinkProgram(shaderProgram); | |
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); | |
if (!success) { | |
glGetProgramInfoLog(shaderProgram, ERROR_MSG_BUFFER_LEN, NULL, infoLog); | |
fprintf(stderr, "shader program linking failed: %s\n", infoLog); | |
exit(1); | |
} | |
// Garbage collection | |
glDeleteShader(vertexShader); | |
glDeleteShader(fragmentShader); | |
// VAO = vertex array object | |
// | |
// after binding, all future vertex attribute calls will be stored in the | |
// VAO. | |
// | |
// A vertex array object stores the following: | |
// | |
// Calls to glEnableVertexAttribArray or glDisableVertexAttribArray. | |
// - Vertex attribute configurations via glVertexAttribPointer. | |
// - Vertex buffer objects associated with vertex attributes by calls to | |
// glVertexAttribPointer. | |
glGenVertexArrays( | |
// size | |
1, | |
// VAO pointer | |
&VAO); | |
glGenBuffers( | |
// size | |
1, | |
// pointer to buffers | |
&VBO); | |
glBindVertexArray(VAO); | |
// After this binding, future buffer calls will now refer to VBO | |
glBindBuffer( | |
// GL_ENUM_TARGET | |
GL_ARRAY_BUFFER, | |
// buffer ID | |
VBO); | |
// Copy user-defined data into the currently bound buffer. | |
// First arg is the type of data we are passing. | |
// | |
// The fourth parameter is one of: | |
// GL_STREAM_DRAW: the data is set only once and used by the GPU at most a few | |
// times. GL_STATIC_DRAW: the data is set only once and used many times. | |
// GL_DYNAMIC_DRAW: the data is changed a lot and used many times. | |
// | |
// glBufferData(GL_ENUM_TARGET, buffer size, buffer data, draw mode) | |
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); | |
// Tell OpenGL how to interpret the vertex data (per vertex attribute) | |
glVertexAttribPointer( | |
// Which vertex attribute we want to configure. | |
// Since we specified layout (location = 0) in vertex shader, | |
// use 0 | |
0, | |
// the size of the vertex attribute; vec3 -> 3 | |
3, | |
// type, vec* in GLSL constists of C floats | |
GL_FLOAT, | |
// should data be normalized | |
GL_FALSE, | |
// stride, or space between elements | |
sizeof(float) * 3, | |
// offset where position data begins in the buffer | |
NULL); | |
// Each vertex attribute takes its data from memory managed by a VBO and which | |
// VBO it takes its data from (you can have multiple VBOs) is determined by | |
// the VBO currently bound to GL_ARRAY_BUFFER when calling | |
// glVertexAttribPointer. Since the previously defined VBO is still bound | |
// before calling glVertexAttribPointer vertex attribute 0 is now associated | |
// with its vertex data. | |
glEnableVertexAttribArray(0); | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
// Is this real or an example? | |
// bind VAO to context | |
glBindVertexArray(0); | |
} | |
void render(GLFWwindow *window) { | |
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); | |
glClear(GL_COLOR_BUFFER_BIT); | |
glUseProgram(shaderProgram); | |
glBindVertexArray(VAO); | |
glDrawArrays(GL_TRIANGLES, 0, 3); | |
// glfw uses double buffering | |
glfwSwapBuffers(win); | |
} | |
int main() { | |
init(); | |
while (!glfwWindowShouldClose(win)) { | |
processInput(win); | |
render(win); | |
glfwPollEvents(); | |
} | |
glfwTerminate(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment