Draw a Triangle
We initialize an OpenGL context in the window and draw a triangle
Published
Liked the article? Share it!
The next step is a little long. We will simply more in the next step, but first we write a triangle by defining and compiling shaders directly in the source code.
main.c
#include <epoxy/gl.h>
#include <epoxy/glx.h>
#include <gtk/gtk.h>
static void on_realize(GtkGLArea *area);
static void on_render(GtkGLArea *area, GdkGLContext *context);
GLuint program;
GLuint vao, vbo_triangle;
GLint attribute_coord2d;
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *glArea;
gtk_init(&argc, &argv);
// Initialize Window
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Brickout Tutorial");
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_UTILITY);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// Initialize GTK GL Area
glArea = gtk_gl_area_new();
gtk_widget_set_vexpand(glArea, TRUE);
gtk_widget_set_hexpand(glArea, TRUE);
g_signal_connect(glArea, "realize", G_CALLBACK(on_realize), NULL);
g_signal_connect(glArea, "render", G_CALLBACK(on_render), NULL);
gtk_container_add(GTK_CONTAINER(window), glArea);
// Show widgets
gtk_widget_show_all(window);
gtk_main();
return 0;
}
static void on_realize(GtkGLArea *area) {
// Debug Message
g_print("on realize\
");
gtk_gl_area_make_current(area);
if(gtk_gl_area_get_error(area) != NULL) {
fprintf(stderr, "Unknown error\
");
return;
}
const GLubyte *renderer = glGetString(GL_RENDER);
const GLubyte *version = glGetString(GL_VERSION);
printf("Renderer: %s\
", renderer);
printf("OpenGL version supported %s\
", version);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLfloat triangle_vertices[] = {
0.0, 0.8,
-0.8, -0.8,
0.8, -0.8
};
glGenBuffers(1, &vbo_triangle);
glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle);
glBufferData(
GL_ARRAY_BUFFER,
sizeof(triangle_vertices),
triangle_vertices,
GL_STATIC_DRAW
);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glDisableVertexAttribArray(0);
GLint compile_ok = GL_FALSE;
GLint link_ok = GL_FALSE;
const char *vs_source =
"#version 120\
"
"attribute vec2 coord2d; \
"
"void main (void) {\
"
" gl_Position = vec4(coord2d, 0.0, 1.0);\
"
"}";
const char *fs_source =
"#version 120\
"
"void main (void) {\
"
" gl_FragColor[0] = 0.0;\
"
" gl_FragColor[1] = 0.0;\
"
" gl_FragColor[2] = 1.0;\
"
"}";
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fs_source, NULL);
glCompileShader(fs);
glGetShaderiv(fs, GL_COMPILE_STATUS, &compile_ok);
if(!compile_ok) {
fprintf(stderr, "Error in fragment shader\
");
return;
}
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vs_source, NULL);
glCompileShader(vs);
glGetShaderiv(vs, GL_COMPILE_STATUS, &compile_ok);
if(!compile_ok) {
fprintf(stderr, "Error in vertex shader\
");
return;
}
program = glCreateProgram();
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
if(!link_ok) {
fprintf(stderr, "Error when linking program\
");
return;
}
const char *attribute_name = "coord2d";
attribute_coord2d = glGetAttribLocation(program, attribute_name);
if(attribute_coord2d == -1) {
fprintf(stderr, "Could not bind attribute %s\
", attribute_name);
return;
}
}
static void on_render(GtkGLArea *area, GdkGLContext *context) {
g_print("on render\
");
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(program);
glBindVertexArray(vao);
glEnableVertexAttribArray(attribute_coord2d);
glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle);
glVertexAttribPointer(
attribute_coord2d,
2,
GL_FLOAT,
GL_FALSE,
0,
0
);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(attribute_coord2d);
}
Compile
$ gcc `pkg-config --cflags gtk+-3.0` main.c `pkg-config --libs gtk+-3.0` -lepoxy
$ ./a.out