opengl-learn

step-by-step introduction to OpenGL ES 2.0
git clone https://0xdd.org/code/opengl-learn.git
Log | Files | Refs | LICENSE

commit 81153bdbc220710adfac845d2491462e914d175c
parent 92d6d25eff4b7dd560d03b3b79a10be05df1260c
Author: David DiPaola <DavidDiPaola@users.noreply.github.com>
Date:   Tue, 22 May 2018 06:10:16 -0400

initial commit

Diffstat:
.gitignore | 5+++++
00-triangle.c | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Makefile | 28++++++++++++++++++++++++++++
ogl/_ogl.c | 10++++++++++
ogl/_ogl.h | 6++++++
ogl/ogl.h | 24++++++++++++++++++++++++
ogl/ogl_init.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ogl/ogl_program_attribute_get_ID.c | 22++++++++++++++++++++++
ogl/ogl_program_build.c | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ogl/ogl_vertex_buffer_load.c | 17+++++++++++++++++
10 files changed, 368 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,5 @@ +*.o +00-triangle + +.*.swp + diff --git a/00-triangle.c b/00-triangle.c @@ -0,0 +1,96 @@ +#include <stdio.h> + +#include <stdlib.h> + +#include <GL/glew.h> +#include <GLFW/glfw3.h> +#include "ogl/ogl.h" + +int +main() { + int status; + + GLFWwindow * window = NULL; + status = ogl_init(400, 240, "00 - triangle", &window); + if (status < 0) { + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + return status; + } + + const char * vertexshader = + "#version 100" "\n" + "attribute vec4 position;" "\n" + "void main(){" "\n" + " gl_Position = position;" "\n" + "}" "\n" + ; + const char * fragmentshader = + "#version 100" "\n" + "void main() {" "\n" + " gl_FragColor = vec4(1,0,0,1);" "\n" + "}" "\n" + ; + GLuint program_ID = 0; + status = ogl_program_build( + vertexshader, fragmentshader, + &program_ID + ); + if (status < 0) { + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + return status; + } + + GLint program_attribute_position_ID = 0; + status = ogl_program_attribute_get_ID(program_ID, "position", &program_attribute_position_ID); + if (status < 0) { + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + return status; + } + + static const GLfloat triangle_vertex_data[] = { + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + }; + GLuint triangle_vertex_buffer_ID = 0; + status = ogl_vertex_buffer_load(triangle_vertex_data, sizeof(triangle_vertex_data), &triangle_vertex_buffer_ID); + if (status < 0) { + return status; + } + + do { + glClear(GL_COLOR_BUFFER_BIT); + + glUseProgram(program_ID); + + glEnableVertexAttribArray(program_attribute_position_ID); + glBindBuffer(GL_ARRAY_BUFFER, triangle_vertex_buffer_ID); + glVertexAttribPointer( + program_attribute_position_ID, // The attribute we want to configure + 3, // size + GL_FLOAT, // type + GL_FALSE, // is normalized? + 0, // stride + (GLvoid *)0 // array buffer offset + ); + glDrawArrays(GL_TRIANGLES, 0, 3); // 3 indices starting at 0 -> 1 triangle + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(triangle_vertex_buffer_ID); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + while ( + (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS) + && + (glfwWindowShouldClose(window) == 0) + ); + + glDeleteBuffers(1, &triangle_vertex_buffer_ID); + glDeleteProgram(program_ID); + + glfwTerminate(); + + return 0; +} + diff --git a/Makefile b/Makefile @@ -0,0 +1,28 @@ +SRC = \ + 00-triangle.c \ + ogl/_ogl.c ogl/ogl_init.c ogl/ogl_program_build.c ogl/ogl_program_attribute_get_ID.c ogl/ogl_vertex_buffer_load.c +LIB = glew gl glfw3 +BIN = 00-triangle + +OBJ = $(SRC:.c=.o) + +CFLAGS ?= -std=c99 -Wall -fwrapv -g +LIB_CFLAGS += $(shell pkg-config --cflags glew gl glfw3) +LIB_LDFLAGS += $(shell pkg-config --libs glew gl glfw3) + +.PHONY: all +all: $(BIN) + +.PHONY: clean +clean: + @echo [RM] $(OBJ) $(BIN) + @rm -rf $(OBJ) $(BIN) + +%.o: %.c + @echo [CC] $< + @$(CC) $(CFLAGS) $(LIB_CFLAGS) -c $< -o $@ + +$(BIN): $(OBJ) + @echo [LD] $^ -o $@ + @$(CC) $(LDFLAGS) $^ -o $@ $(LIB_LDFLAGS) + diff --git a/ogl/_ogl.c b/ogl/_ogl.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +int _ogl_window_width = 0; +int _ogl_window_height = 0; + +void +_ogl_glfw_error(int error, const char * description) { + fprintf(stderr, "GLFW ERROR: (%i) %s" "\n", error, description); +} + diff --git a/ogl/_ogl.h b/ogl/_ogl.h @@ -0,0 +1,6 @@ +extern int _ogl_window_width; +extern int _ogl_window_height; + +void +_ogl_glfw_error(int error, const char * description); + diff --git a/ogl/ogl.h b/ogl/ogl.h @@ -0,0 +1,24 @@ +int +ogl_init( + int window_width, int window_height, const char * window_title, + GLFWwindow ** out_window +); + +int +ogl_program_build( + const char * vertexshader, const char * fragmentshader, + GLuint * out_program_ID +); + +int +ogl_program_attribute_get_ID( + GLuint programID, const char * attribute_name, + GLint * out_attribute_ID +); + +int +ogl_vertex_buffer_load( + const GLvoid * data, GLsizeiptr data_size, + GLuint * out_bufferID +); + diff --git a/ogl/ogl_init.c b/ogl/ogl_init.c @@ -0,0 +1,59 @@ +#include <stdio.h> + +#include <GL/glew.h> + +#include <GLFW/glfw3.h> + +#include "ogl.h" +#include "_ogl.h" + +int +ogl_init( + int window_width, int window_height, const char * window_title, + GLFWwindow ** out_window +) { + GLFWwindow * window; + int status; + + glfwSetErrorCallback(&_ogl_glfw_error); + + status = glfwInit(); + if (!status) { /* TODO GLFW_TRUE doesn't exist..? */ + fprintf(stderr, "GLFW glfwInit(): ERROR" "\n"); + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + return -1; + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + window = glfwCreateWindow(window_width, window_height, window_title, NULL, NULL); + if (window == NULL){ + fprintf(stderr, "GLFW glfwCreateWindow(): ERROR" "\n"); + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + glfwTerminate(); + return -1; + } + _ogl_window_width = window_width; + _ogl_window_height = window_height; + + glfwMakeContextCurrent(window); + + GLenum glewstatus; + glewstatus = glewInit(); + if (glewstatus != GLEW_OK) { + fprintf(stderr, "GLEW glewInit(): ERROR" "\n"); + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + glfwTerminate(); + return -1; + } + + glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE); + + glClearColor(0.0f, 0.0f, 0.4f, 0.0f); + + (*out_window) = window; + return 0; +} + diff --git a/ogl/ogl_program_attribute_get_ID.c b/ogl/ogl_program_attribute_get_ID.c @@ -0,0 +1,22 @@ +#include <stdio.h> + +#include <GL/glew.h> + +int +ogl_program_attribute_get_ID( + GLuint program_ID, const char * attribute_name, + GLint * out_attribute_ID +) { + GLint attribute_ID; + + attribute_ID = glGetAttribLocation(program_ID, attribute_name); + if (attribute_ID < 0) { + fprintf(stderr, "GL glGetAttribLocation(%i, \"%s\"): ERROR" "\n", program_ID, attribute_name); + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + return -1; + } + + (*out_attribute_ID) = attribute_ID; + return 0; +} + diff --git a/ogl/ogl_program_build.c b/ogl/ogl_program_build.c @@ -0,0 +1,101 @@ +#include <stdio.h> + +#include <stdlib.h> + +#include <GL/glew.h> + +#include <GLFW/glfw3.h> + +static int +_shader_compile( + GLenum shader_type, const char * shader_code, + GLuint * out_shader_ID +) { + GLuint shader_ID; + const char * shader_type_str = (shader_type == GL_VERTEX_SHADER) ? "vertex" : "fragment"; + + shader_ID = glCreateShader(shader_type); + + glShaderSource(shader_ID, 1, &shader_code, NULL); + + glCompileShader(shader_ID); + GLsizei log_length = 0; + glGetShaderiv(shader_ID, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 0) { + GLchar * log = calloc(log_length, sizeof(GLchar)); /* TODO handle error */ + glGetShaderInfoLog(shader_ID, log_length, NULL, log); + fprintf(stderr, "GL %s shader compiler: %s" "\n", shader_type_str, log); + free(log); + } + + GLint status_compile = GL_FALSE; + glGetShaderiv(shader_ID, GL_COMPILE_STATUS, &status_compile); + if (status_compile != GL_TRUE) { + fprintf(stderr, "GL %s shader compiler: ERROR" "\n", shader_type_str); + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + return -1; + } + + (*out_shader_ID) = shader_ID; + return 0; +} + +int +ogl_program_build( + const char * vertexshader, const char * fragmentshader, + GLuint * out_program_ID +) { + /* + GLenum status; + status = glGetError(); + if (status != GL_NO_ERROR) { + fprintf(stderr, "GL ERROR: foo() failed (GLenum %i)" "\n", status); + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + } + */ + int status; + + GLuint vertexshader_ID; + status = _shader_compile(GL_VERTEX_SHADER, vertexshader, &vertexshader_ID); + if (status < 0) { + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + return -1; + } + + GLuint fragmentshader_ID; + status = _shader_compile(GL_FRAGMENT_SHADER, fragmentshader, &fragmentshader_ID); + if (status < 0) { + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + return -1; + } + + GLuint program_ID = glCreateProgram(); + glAttachShader(program_ID, vertexshader_ID); + glAttachShader(program_ID, fragmentshader_ID); + glLinkProgram(program_ID); + GLsizei log_length = 0; + glGetProgramiv(program_ID, GL_INFO_LOG_LENGTH, &log_length); + if ( log_length > 0 ){ + GLchar * log = calloc(log_length, sizeof(GLchar)); /* TODO handle error */ + glGetProgramInfoLog(program_ID, log_length, NULL, log); + fprintf(stderr, "GL shader program linker: %s" "\n", log); + free(log); + } + GLint status_compile = GL_FALSE; + glGetProgramiv(program_ID, GL_LINK_STATUS, &status_compile); + if (status_compile != GL_TRUE) { + fprintf(stderr, "GL shader program linker: ERROR" "\n"); + fprintf(stderr, "\t" "at %s : %d" "\n", __FILE__, __LINE__); + return -1; + } + + glDetachShader(program_ID, vertexshader_ID); + glDetachShader(program_ID, fragmentshader_ID); + + glDeleteShader(vertexshader_ID); + glDeleteShader(fragmentshader_ID); + + (*out_program_ID) = program_ID; + return 0; +} + diff --git a/ogl/ogl_vertex_buffer_load.c b/ogl/ogl_vertex_buffer_load.c @@ -0,0 +1,17 @@ +#include <GL/glew.h> + +int +ogl_vertex_buffer_load( + const GLvoid * data, GLsizeiptr data_size, + GLuint * out_buffer_ID +) { + GLuint buffer_ID; + glGenBuffers(1, &buffer_ID); + glBindBuffer(GL_ARRAY_BUFFER, buffer_ID); + glBufferData(GL_ARRAY_BUFFER, data_size, data, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + (*out_buffer_ID) = buffer_ID; + return 0; +} +