Skip to content

Instantly share code, notes, and snippets.

@dmoa
Created September 10, 2021 22:46
Show Gist options
  • Save dmoa/0f6eda587d8bf3ae3403df91a62a7c23 to your computer and use it in GitHub Desktop.
Save dmoa/0f6eda587d8bf3ae3403df91a62a7c23 to your computer and use it in GitHub Desktop.
OpenGL Holy Grail
// This example loads an image and a shader that are used to draw the image.
#include <math.h>
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_opengl.h>
#define print SDL_Log
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG
#include "stb_image.h"
char* gl_error_string(GLenum err);
char* LoadFile(char* path);
GLuint LoadImage(char* path);
GLuint LoadShader(char* path, GLenum shader_type);
struct Graphics {
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_GLContext gl_context = NULL;
void Init() {
SDL_Init(SDL_INIT_VIDEO);
// window
window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 800, SDL_WINDOW_OPENGL);
gl_context = SDL_GL_CreateContext(window);
glClearColor(0.5, 0.5, 0.5, 1);
if (glewInit() != GLEW_OK) {
print("Glew failed to init!");
exit(EXIT_FAILURE);
}
char* glsl_v = glGetString(GL_SHADING_LANGUAGE_VERSION);
print("glsl version: %s", glsl_v);
// misc
glEnable(GL_TEXTURE_2D);
stbi_set_flip_vertically_on_load(true);
// transparency
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void Shutdown() {
SDL_GL_DeleteContext(gl_context);
SDL_DestroyWindow(window);
SDL_Quit();
}
};
int main(int argc, char *argv[]) {
Graphics graphics;
graphics.Init();
GLuint texture = LoadImage("assets/chess.png");
// Shader Loading
GLuint frag = LoadShader("src/shader.frag", GL_FRAGMENT_SHADER);
GLuint vert = LoadShader("src/shader.vert", GL_VERTEX_SHADER);
GLuint program = glCreateProgram();
glAttachShader(program, frag);
glAttachShader(program, vert);
glLinkProgram(program);
// Checking for program errors.
GLint status;
glValidateProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, & status);
if (!status) {
GLint len;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
if (len > 1) {
char *log;
log = malloc(len);
glGetProgramInfoLog(program, len, &len, log);
fprintf(stderr, "%s\n\n", log);
free(log);
}
print("Failed to link program!");
}
glUseProgram(program);
print("%i", glGetUniformLocation(program, "pixel_texture"));
glUniform1i(glGetUniformLocation(program, "pixel_texture"), 0);
bool quit = false;
SDL_Event event;
while (! quit) {
while (SDL_PollEvent(& event)) {
if (event.type == SDL_QUIT) {
quit = true;
}
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) {
quit = true;
}
}
glClear(GL_COLOR_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, texture);
glBegin(GL_QUADS); // Draw textured Quad
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 0.0); // Top Left Corner
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, 0.0); // Top Right Corner
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,-1.0, 0.0); // Bottom Right Corner
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,-1.0, 0.0); // Bottom Left Corner
glEnd();
SDL_GL_SwapWindow(graphics.window);
SDL_Delay(10);
}
glDeleteTextures(1, & texture);
graphics.Shutdown();
return 0;
}
char* LoadFile(char* path) {
FILE* file = fopen(path, "r");
if (! file) {
print("Couldn't load %s !", file);
return NULL;
}
// 0L because fseek takes a long integer, but we still only want zero
fseek(file, 0L, SEEK_END);
long file_size = ftell(file);
fseek(file, 0L, SEEK_SET);
char* buffer = (char*) calloc(file_size, sizeof(char));
fread(buffer, sizeof(char), file_size, file);
// buffer[file_size] = '\0'; // Text files usually don't have the terminating string character.
fclose(file);
return buffer;
}
GLuint LoadImage(char* path) {
int width; int height; int num_channels;
void* pixels = stbi_load(path, & width, & height, & num_channels, 0);
if (! pixels) {
print("Couldn't load %s", path);
return -1;
}
GLuint texture;
glGenTextures(1, & texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
// I'm 90% sure the rest of this function just sets what would be the default parameters,
// but I'm doing this just in case.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
auto format = (num_channels == 3) ? GL_RGB : GL_RGBA;
glTexImage2D(
GL_TEXTURE_2D, // target
0, // level, 0 = base, no minimap,
format, // internal format
width, // width
height, // height
0, // This value must be 0.
format, // format
GL_UNSIGNED_BYTE, // data type of the pixel datatype
pixels
);
// glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(pixels);
return texture;
}
GLuint LoadShader(char* path, GLenum shader_type) {
char* file = LoadFile(path);
GLuint shader = glCreateShader(shader_type);
glShaderSource(shader, 1, & file, NULL);
glCompileShader(shader);
free(file);
// Check for errors:
GLint success; GLint length;
glGetShaderiv(shader, GL_COMPILE_STATUS, & success);
if (! success) {
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, & length);
if (length) {
char* log = (char*) malloc(length);
glGetShaderInfoLog(shader, length, NULL, log);
print("%s\n\n", log);
}
print("Error compiling shader %s.", path);
}
return shader;
}
char* gl_error_string(GLenum err)
{
switch (err)
{
// opengl 2 errors
case GL_NO_ERROR:
return "GL_NO_ERROR";
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
case GL_INVALID_VALUE:
return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
case GL_STACK_OVERFLOW:
return "GL_STACK_OVERFLOW";
case GL_STACK_UNDERFLOW:
return "GL_STACK_UNDERFLOW";
case GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
case GL_TABLE_TOO_LARGE:
return "GL_TABLE_TOO_LARGE";
// opengl 3 errors
case GL_INVALID_FRAMEBUFFER_OPERATION:
return "GL_INVALID_FRAMEBUFFER_OPERATION";
default:
print("unknown error");
return NULL;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment