1

I'm experimenting with using the GtkGLArea widget at the moment. None of the answers to similar questions seem to pertain to this situation.

glClear() can set the background colour just fine but actually drawing arrays of triangles isn't.

According to this tutorial, the code below should work.

EDIT: 21/5/2015: Added shaders, still getting same results. New code below

Here's the main.c and the SConstruct so you can build with scons:

SOLVED: Working code under the heading SOLUTION: main.c the same SConstruct file can be used to build example

main.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <glib.h>

#include <gdk/gdkx.h>
#include <epoxy/glx.h>
#include <epoxy/gl.h>
#include <gtk/gtk.h>
#include <gtk/gtkglarea.h>

#define IGNORE_VAR(type, identifier) \
{ \
  type IGNORED_VARIABLE_abcd = identifier; \
  identifier = IGNORED_VARIABLE_abcd; \
}

const GLchar *vert_src ="\n" \
"#version 330                                  \n" \
"                                              \n" \
"layout(location = 0) in vec2 in_position;     \n" \
"                                              \n" \
"void main()                                   \n" \
"{                                             \n" \
"      gl_Position = in_position;              \n" \
"}                                             \n";

const GLchar *frag_src ="\n" \
"void main (void)                              \n" \
"{                                             \n" \
"     gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); \n" \
"}                                             \n";

GLuint gl_buffer, gl_program;

static gboolean realise(GtkGLArea *area, GdkGLContext *context)
{
  IGNORE_VAR(GdkGLContext*, context);

  gtk_gl_area_make_current(GTK_GL_AREA(area));
  if (gtk_gl_area_get_error (GTK_GL_AREA(area)) != NULL)
  {
    printf("Failed to initialiize buffers\n");
    return FALSE;
  }

  GLfloat verts[] = 
  {
    +0.0f, +1.0f,
    -1.0f, -1.0f,
    +1.0f, -1.0f,
  };

  GLuint frag_shader, vert_shader;
  frag_shader = glCreateShader(GL_FRAGMENT_SHADER);
  vert_shader = glCreateShader(GL_VERTEX_SHADER);

  glShaderSource(frag_shader, 1, &frag_src, NULL);
  glShaderSource(vert_shader, 1, &vert_src, NULL);

  glCompileShader(frag_shader);
  glCompileShader(vert_shader);

  gl_program = glCreateProgram();
  glAttachShader(gl_program, frag_shader);
  glAttachShader(gl_program, vert_shader);
  glLinkProgram(gl_program);

  glGenBuffers(1, &gl_buffer);
  glBindBuffer(GL_ARRAY_BUFFER, gl_buffer);
  glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);


  return TRUE;
}

static gboolean render(GtkGLArea *area, GdkGLContext *context)
{
  IGNORE_VAR(GdkGLContext*, context);
  IGNORE_VAR(GtkGLArea*, area);

  glClearColor(0, 0, 0, 0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glBindBuffer(GL_ARRAY_BUFFER, gl_buffer);
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
  //glUseProgram(gl_program);
  glDrawArrays(GL_TRIANGLES, 0, 3);
  glDisableVertexAttribArray(0);

  return TRUE;
}

int main(int argc, char** argv)
{
  gtk_init(&argc, &argv);

  GtkWidget *window  = gtk_window_new(GTK_WINDOW_TOPLEVEL),
            *gl_area = gtk_gl_area_new();

  g_signal_connect(window,  "delete-event", G_CALLBACK(gtk_main_quit), NULL);
  g_signal_connect(gl_area, "realize",      G_CALLBACK(realise),       NULL);
  g_signal_connect(gl_area, "render",       G_CALLBACK(render),        NULL);

  gtk_container_add(GTK_CONTAINER(window), gl_area);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

Sconstruct

import os

env = Environment(CC='gcc', CCFLAGS='--std=c11', ENV={'PATH':os.environ['PATH']})

env.Append(LIBS = ['GL', 'epoxy'])
env.ParseConfig('pkg-config --cflags --libs gtk+-3.0')
#env.ParseConfig('pkg-config --cflags  gdkglext-1.0')

env.Program(target='gl', source=['main.c'])

# vim: set filetype=python:

SOLUTION: main.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <glib.h>

#include <gdk/gdkx.h>
#include <epoxy/glx.h>
#include <epoxy/gl.h>
#include <gtk/gtk.h>

#define IGNORE_VAR(type, identifier) \
{ \
  type IGNORED_VARIABLE_abcd = identifier; \
  identifier = IGNORED_VARIABLE_abcd; \
}

const GLchar *vert_src ="\n" \
"#version 330                                  \n" \
"#extension GL_ARB_explicit_attrib_location: enable  \n" \
"                                              \n" \
"layout(location = 0) in vec2 in_position;     \n" \
"                                              \n" \
"void main()                                   \n" \
"{                                             \n" \
"  gl_Position = vec4(in_position, 0.0, 1.0);  \n" \
"}                                             \n";

const GLchar *frag_src ="\n" \
"void main (void)                              \n" \
"{                                             \n" \
"  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);    \n" \
"}                                             \n";

GLuint gl_vao, gl_buffer, gl_program;

static gboolean realise(GtkGLArea *area, GdkGLContext *context)
{
  IGNORE_VAR(GdkGLContext*, context);

  gtk_gl_area_make_current(GTK_GL_AREA(area));
  if (gtk_gl_area_get_error (GTK_GL_AREA(area)) != NULL)
  {
    printf("Failed to initialiize buffers\n");
    return FALSE;
  }

  GLfloat verts[] = 
  {
    +0.0f, +1.0f,
    -1.0f, -1.0f,
    +1.0f, -1.0f,
  };

  GLuint frag_shader, vert_shader;
  frag_shader = glCreateShader(GL_FRAGMENT_SHADER);
  vert_shader = glCreateShader(GL_VERTEX_SHADER);

  glShaderSource(frag_shader, 1, &frag_src, NULL);
  glShaderSource(vert_shader, 1, &vert_src, NULL);

  glCompileShader(frag_shader);
  glCompileShader(vert_shader);

  gl_program = glCreateProgram();
  glAttachShader(gl_program, frag_shader);
  glAttachShader(gl_program, vert_shader);
  glLinkProgram(gl_program);

  glGenVertexArrays(1, &gl_vao);
  glBindVertexArray(gl_vao);

  glGenBuffers(1, &gl_buffer);
  glBindBuffer(GL_ARRAY_BUFFER, gl_buffer);
  glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);

  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
  glBindVertexArray(0);

  glDeleteBuffers(1, &gl_buffer);

  return TRUE;
}

static gboolean render(GtkGLArea *area, GdkGLContext *context)
{
  IGNORE_VAR(GdkGLContext*, context);
  IGNORE_VAR(GtkGLArea*, area);

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glClearColor(0.0, 0.0, 0.0, 1.0);

  glUseProgram(gl_program);
  glBindVertexArray(gl_vao);
  glDrawArrays(GL_TRIANGLES, 0, 3);

  glBindVertexArray (0);
  glUseProgram (0);

  glFlush();

  return TRUE;
}

int main(int argc, char** argv)
{
  gtk_init(&argc, &argv);

  GtkWidget *window  = gtk_window_new(GTK_WINDOW_TOPLEVEL),
            *gl_area = gtk_gl_area_new();

  g_signal_connect(window,  "delete-event", G_CALLBACK(gtk_main_quit), NULL);
  g_signal_connect(gl_area, "realize",      G_CALLBACK(realise),       NULL);
  g_signal_connect(gl_area, "render",       G_CALLBACK(render),        NULL);

  gtk_container_add(GTK_CONTAINER(window), gl_area);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
candronikos
  • 159
  • 1
  • 13
  • This contains the full initialization code? I notice that you set the winding order to `GL_CW`, but then draw a counter-clockwise triangle. But if you don't have culling enabled, that won't really matter. – Reto Koradi May 19 '15 at 23:33
  • That was a mistake to leave it in. Doesn't make a difference either way – candronikos May 19 '15 at 23:52
  • I wrote a full example of using OpenGL with GtkGLArea: https://www.bassi.io/articles/2015/02/17/using-opengl-with-gtk/ — the associated code is on GitHub: https://github.com/ebassi/glarea-example – ebassi May 21 '15 at 13:39
  • I was following a similar example but couldn't compile properly. I am getting GtkGLArea undefined. Any idea? – Marvin Dec 03 '16 at 20:05

3 Answers3

2

You don't appear to have any shaders. You need a fragment and vertex shader.

Here's a tutorial on how to write and use them: https://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/loading.php

  • I don't have any shaders. I'll write some up when I get the chance. If that fixes it I'll mark this as the answer. – candronikos May 19 '15 at 23:53
  • According to [this](https://www.youtube.com/watch?v=Dyue3MzJDss&list=PLRwVmtr-pp06qT6ckboaOhnm9FxmzHpbY&index=5) tutorial, it should work as is. – candronikos May 20 '15 at 00:02
  • It depends on your graphics card. Most Nvidia drivers have a default shader (black and white depending on geometry), but other drivers may fail without one. – Cosine May 20 '15 at 00:07
  • That really depends on the context type. Not sure what type of context GTK+ creates. In any case, what typically does not work is using generic vertex attributes without a shader, which is what the OP is doing. See my answer here: http://stackoverflow.com/questions/29426874/blank-screen-while-using-glgenbuffers-in-opengl/29435800#29435800. – Reto Koradi May 20 '15 at 02:51
  • Added fragment and vertex shaders but still getting the same result. Not entirely confident that I didn't introduce more errors into the code so I'd like to know what you think – candronikos May 21 '15 at 03:20
  • This is basically the solution but see the answer from @ebassi which corrects the errors in the render function – candronikos May 21 '15 at 23:24
0

You're using generic vertex attributes. As such you're supposed to use shaders. If you're in a compatibility profile your use of vertex attribute 0 may be interpreted as built-in attribute "vertex position", but that's not a given. So if you want to operate "by the book" you must supply a shader (or stick with the deprecated built-in attributes).

On a side note: Enabling the vertex attribute array and setting the pointer in the initialization code is not recommendable. Anything may happen with the OpenGL context between those two, including binding other vertex arrays, disabling/enabling (other) vertex attribute arrays, setting different buffer pointers, etc.

As a rule of thumb, anything that's directly related to drawing state (i.e. that does not constitute one time data initialization/loading) belongs at the corresponding place of the drawing code. In your case

static gboolean render(GtkGLArea *area, GdkGLContext *context)
{
  glClearColor(0, 0, 0, 0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  //glFrontFace(GL_CW);

  /* added these ---> */
  glBindBuffer(GL_ARRAY_BUFFER, gl_buffer);
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
  /* <--- */

  glDrawArrays(GL_TRIANGLES, 0, 3);

  //gtk_widget_get_realized(area);

  return TRUE;
}

Doing not so will create will confuse you in the long run.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • Made these changes as best as I could. Still getting same result though. Since I'm new I'm sure there are more errors that I have missed. Thanks for your help so far – candronikos May 21 '15 at 03:18
  • @candronikos: Looks good so far. However something your code lacks as well is setting the viewport. You must set it according to your window's size to reflect window size changes. Best done first thing in the drawing code. – datenwolf May 21 '15 at 08:11
  • @datenwolf No, that is not necessary: GtkGLArea will automatically call `glViewport()` for you every time its size changes. The only reason to call `glViewport()` manually would be to change that behaviour. – ebassi May 21 '15 at 13:45
  • @ebassi: Ah, thanks and good to know. Somebody please slap the GtkGLArea developer responsible for the decision to do this with a adamantium reinforced cluebat… doing this is stupid (for several reasons). For example the first steps of rendering may involve drawing to textures/renderbuffers with entirely different viewports than the main window; having the widget call glViewport for convenience reasons adds redundant calls and may trigger heuristics in the OpenGL driver that push into the program into a slow path. – datenwolf May 21 '15 at 15:19
  • As the person that was co-responsible for GtkGLArea I *really* don't like your attitude, since you didn't even bother looking at the implementation before thinking of dispensing your pointless position. Automatically resizing the viewport to the widget size is perfectly fine in the majority of use cases, and the behaviour can be changed if you really need it without causing glViewport() to be called multiple times. Additionally, There's no "slow path" involved in calling glViewport() multiple times anyway. – ebassi May 21 '15 at 16:16
  • 1
    @ebassi: Modern OpenGL drivers employ *a lot* of heuristics to determine the inner workings of a program. Calling glViewport in a particular pattern may be taken as a hint on the usage pattern the program is performing. For example sizing the viewport (0,0,win_width,win_height) may be taken as a hint that the window framebuffers are going to be touched next. – datenwolf May 21 '15 at 17:11
  • 1
    @ebassi: I'm aware that using the gdk functions you can manually do the detached context creation (`gdk_window_create_gl_context`); however `gdk_gl_context_make_current` does not take a GdkWindow parameter which essentially means, that (AFAIK) you can't use the context with a different GdkWindow. It's often useful to be able doing exactly that though. Another problem with GdkGLContext is `gdk_gl_context_get_shared_context()` – this thing should return a `GList` of `GdkGLContext*`, since a context may share its namespace with more than one other context. – datenwolf May 21 '15 at 17:55
0

You are missing the creation of the VAO; GTK+ will create GL core profile contexts, which means you need to create and select the VAO yourself, otherwise your vertex buffer objects won't be used.

When initializing the GL state, add this:

  /* we need to create a VAO to store the other buffers */
  GLuint vao;

  glGenVertexArrays (1, &vao);
  glBindVertexArray (vao);

Before creating the VBOs.

You should check out my blog post about using OpenGL with GTK+, and its related example code.

ebassi
  • 8,648
  • 27
  • 29