Trying to render a volume with faces color

Hello, this is my first post here, I am a total beginner with Open GL. I’m currently writing a GTK4 application in C language that unfolds a volume into a 2d pattern. The volume comes from an .obj file that the user chooses. To make my UI clearer I’m trying to make a 3d view of the volume where each piece is shown with its own color. To do so I used the GTK widget called “Gtk.GlArea”. I tried to use a shader example that will make my view look like MeshLab view, shader from a stackOverflow post.

But my result shows a monochrome volume where it is hard to see the faces. I am certainly missing something, maybe lighting.

here’s my result and what I would like to have :

Here’s my code : sadly it is quite long, and maybe out of the scope of this forum.
It is taken from GTK Demos glArea demo code that I used as framework.

#include <stdio.h>
#include <gtk/gtk.h>
#include <epoxy/gl.h>

static GtkWidget *gl_area = NULL;

enum {
  X_AXIS,
  Y_AXIS,
  Z_AXIS,

  N_AXIS
};

#define NBF 60

/* Rotation angles on each axis */
static float rotation_angles[N_AXIS] = { 0.0 };

static void close_window (GtkWidget *widget) {
  /* Reset the state */
  gl_area = NULL;

  rotation_angles[X_AXIS] = 0.0;
  rotation_angles[Y_AXIS] = 0.0;
  rotation_angles[Z_AXIS] = 0.0;
}

/* Initialize the GL buffers */
static void init_buffers (GLuint *vao_out, GLuint *buffer_out, GLuint *normal_buffer_out, GLuint *color_buffer_out) {
  GLuint vao, vertex_buffer, normal_buffer, color_buffer;

// from simple polyhedron made with Wings 3d and saved to .obj file
GLfloat vertex_base[] = {
-0.43035963, -0.10000000, -0.56964037,
-0.43035963, -0.10000000, 0.43035963,
-0.43035963, 0.10000000, -0.56964037,
-0.43035963, 0.10000000, 0.43035963,
0.56964037, -0.10000000, 0.43035963,
0.56964037, 0.10000000, 0.43035963,
6.9640366e-2, -0.10000000, -0.56964037,
6.9640366e-2, 0.10000000, -0.56964037,
0.56964037, -0.10000000, -6.9640366e-2,
0.56964037, 0.10000000, -6.9640366e-2,
0.17821901, -0.10000000, -0.17821901,
0.17821901, 0.10000000, -0.17821901
};

GLfloat normal_base[] = {
-0.57735027, -0.57735027, -0.57735027,
-0.40824829, -0.81649658, 0.40824829,
-0.40824829, 0.81649658, -0.40824829,
-0.57735027, 0.57735027, 0.57735027,
0.57735027, -0.57735027, 0.57735027,
0.40824829, 0.81649658, 0.40824829,
0.51254517, -0.53189952, -0.67407740,
0.57745853, 0.29963205, -0.75944867,
0.67407740, -0.53189952, -0.51254517,
0.75944867, 0.29963205, -0.57745853,
0.46423835, -0.75429803, -0.46423835,
0.28216632, 0.91693202, -0.28216632
};

GLshort indices[] = {
1,4,3,
1,8,7,
1,11,2,
2,4,1,
2,5,4,
2,11,5,
3,8,1,
3,12,8,
4,5,6,
4,12,3,
5,10,6,
5,11,9,
6,12,4,
7,11,1,
8,11,7,
9,10,5,
10,11,12,
10,12,6,
11,10,9,
12,11,8
};


GLfloat color_base[] = { // 1 color per face (all white)
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0
};
/*GLfloat color_base[] = { // 1 color per face (hand created)
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 1.0,
0.0, 1.0, 1.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 1.0,
1.0, 0.0, 1.0,
1.0, 1.0, 0.0,
1.0, 1.0, 0.0,
0.0, 0.0, 0.5,
0.0, 0.0, 0.5,
0.0, 0.5, 0.0,
0.0, 0.5, 0.0,
0.0, 0.5, 0.5,
0.0, 0.5, 0.5,
0.0, 0.5, 1.0,
0.0, 0.5, 1.0,
};*/

  int nbV = NBF *3;

  // POINTS
  GLfloat vertex_data[nbV];

  // NORMALS
  GLfloat normal_data[nbV];

  // COLORS
  GLfloat color_data[nbV];

	// create arrays with unique values
	int n = 0;
	for (int i = 0; i < NBF; i++) {
    int o = (indices[i]-1) * 3;
    for (int j =0; j < 3; j++) {
      vertex_data[n] = vertex_base[o+j];
      normal_data[n] = normal_base[o+j];
      color_data[n]  = color_base[o+j];
      n++;
    }
  }

  // We only use one VAO, so we always keep it bound
  glGenVertexArrays (1, &vao);
  glBindVertexArray (vao);

  glEnableClientState (GL_VERTEX_ARRAY);

  // vertices
  glGenBuffers (1, &vertex_buffer);
  glBindBuffer (GL_ARRAY_BUFFER, vertex_buffer);
  glBufferData (GL_ARRAY_BUFFER, sizeof (vertex_data), vertex_data, GL_STATIC_DRAW);
  glBindBuffer (GL_ARRAY_BUFFER, 0);

  // normals
  glEnableClientState (GL_NORMAL_ARRAY);
  glGenBuffers (1, &normal_buffer);
  glBindBuffer (GL_ARRAY_BUFFER, normal_buffer);
  glBufferData (GL_ARRAY_BUFFER, sizeof (normal_data), normal_data, GL_STATIC_DRAW);
  glBindBuffer (GL_ARRAY_BUFFER, 0);

  // colors
  glEnableClientState (GL_COLOR_ARRAY);
  glGenBuffers (1, &color_buffer);
  glBindBuffer (GL_ARRAY_BUFFER, color_buffer);
  glBufferData (GL_ARRAY_BUFFER, sizeof (color_data), color_data, GL_STATIC_DRAW);
  glBindBuffer (GL_ARRAY_BUFFER, 0);

  if (vao_out != NULL)
    *vao_out = vao;

  if (buffer_out != NULL)
    *buffer_out = vertex_buffer;

  if (normal_buffer_out != NULL)
    *normal_buffer_out = normal_buffer;

  if (color_buffer_out != NULL)
    *color_buffer_out = color_buffer;
}

/* Create and compile a shader */
static GLuint create_shader (int type, const char *src) {
  GLuint shader;
  int status;

  shader = glCreateShader (type);
  glShaderSource (shader, 1, &src, NULL);
  glCompileShader (shader);

  glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
  if (status == GL_FALSE) {
      int log_len;
      char *buffer;

      glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &log_len);

      buffer = g_malloc (log_len + 1);
      glGetShaderInfoLog (shader, log_len, NULL, buffer);

      g_warning ("Compile failure in %s shader:\n%s",
                 type == GL_VERTEX_SHADER ? "vertex" : "fragment",
                 buffer);

      g_free (buffer);

      glDeleteShader (shader);

      return 0;
   }

  return shader;
}

/* Initialize the shaders and link them into a program */
static void init_shaders (GLuint *program_out, GLuint *m_out, GLuint *v_out, GLuint *p_out) {
  GLuint vertex, fragment;
  GLuint program = 0;
  GLuint m = 0;
  GLuint v = 0;
  GLuint p = 0;
  int status;
/*
  //gl_Position = projection_matrix * view_matrix * model_matrix * vec4(in_position, 1);\
  //vec3 normal_cameraspace = normalize(( view_matrix * model_matrix * vec4(in_normal,0)).xyz);\
  //vec3 cameraVector = normalize(vec3(0, 0, 0) - (view_matrix * model_matrix * vec4(in_position, 1)).xyz);\
  //float cosTheta = clamp( dot( normal_cameraspace, cameraVector ), 0, 1 );\
  //color = vec4(0.3 * in_color.rgb + cosTheta * in_color.rgb, 1);\
*/
  vertex = create_shader (GL_VERTEX_SHADER,
"#version 330 core\n\
uniform mat4 projection_matrix;\
uniform mat4 model_matrix;\
uniform mat4 view_matrix;\
layout(location=0) in vec3 in_position;\
layout(location=1) in vec3 in_normal;\
layout(location=2) in vec3 in_color;\
out vec4 color;\
void main(void){\
  gl_Position = projection_matrix * view_matrix * model_matrix * vec4(in_position, 1);\
  vec3 normal_cameraspace = normalize(( view_matrix * model_matrix * vec4(in_normal,0)).xyz);\
  vec3 cameraVector = normalize(vec3(0, 0, 0) - (view_matrix * model_matrix * vec4(in_position, 1)).xyz);\
  float cosTheta = clamp( dot( normal_cameraspace, cameraVector ), 0, 1 );\
  color = vec4(0.3 * in_color.rgb + cosTheta * in_color.rgb, 1);\
}");

	if (vertex == 0) {
		*program_out = 0;
		return;
	}

fragment = create_shader(GL_FRAGMENT_SHADER,
"#version 330 core\n\
in vec4 color; out vec4 out_frag_color;\
void main(void){ out_frag_color = color;\
}");
  if (fragment == 0) {
    glDeleteShader (vertex);
    *program_out = 0;
    return;
  }

  program = glCreateProgram ();
  glAttachShader (program, vertex);
  glAttachShader (program, fragment);

  glLinkProgram (program);

  glGetProgramiv (program, GL_LINK_STATUS, &status);
  if (status == GL_FALSE) {
    int log_len;
    char *buffer;

    glGetProgramiv (program, GL_INFO_LOG_LENGTH, &log_len);

    buffer = g_malloc (log_len + 1);
    glGetProgramInfoLog (program, log_len, NULL, buffer);

    g_warning ("Linking failure:\n%s", buffer);

    g_free (buffer);

    glDeleteProgram (program);
    program = 0;

    goto out;
  }

  /* Get the location of the "mvp" uniform */
  m = glGetUniformLocation( program, "model_matrix" );
  v = glGetUniformLocation( program, "view_matrix" );
  p = glGetUniformLocation( program, "projection_matrix" );

  glDetachShader (program, vertex);
  glDetachShader (program, fragment);

out:
  glDeleteShader (vertex);
  glDeleteShader (fragment);

  if (program_out != NULL)
    *program_out = program;

  if (m_out != NULL)
    *m_out = m;

  if (v_out != NULL)
    *v_out = v;

  if (p_out != NULL)
    *p_out = p;
}

static void compute_i (float *res) {
  /* initialize to the identity matrix */
  res[0] = 1.f; res[4] = 0.f;  res[8] = 0.f; res[12] = 0.f;
  res[1] = 0.f; res[5] = 1.f;  res[9] = 0.f; res[13] = 0.f;
  res[2] = 0.f; res[6] = 0.f; res[10] = 1.f; res[14] = 0.f;
  res[3] = 0.f; res[7] = 0.f; res[11] = 0.f; res[15] = 1.f;
}


static void compute_mvp (float *res, float  phi, float  theta, float  psi) {
  float x = phi * (G_PI / 180.f);
  float y = theta * (G_PI / 180.f);
  float z = psi * (G_PI / 180.f);
  float c1 = cosf (x), s1 = sinf (x);
  float c2 = cosf (y), s2 = sinf (y);
  float c3 = cosf (z), s3 = sinf (z);
  float c3c2 = c3 * c2;
  float s3c1 = s3 * c1;
  float c3s2s1 = c3 * s2 * s1;
  float s3s1 = s3 * s1;
  float c3s2c1 = c3 * s2 * c1;
  float s3c2 = s3 * c2;
  float c3c1 = c3 * c1;
  float s3s2s1 = s3 * s2 * s1;
  float c3s1 = c3 * s1;
  float s3s2c1 = s3 * s2 * c1;
  float c2s1 = c2 * s1;
  float c2c1 = c2 * c1;

  compute_i(res);

  /* apply all three rotations using the three matrices:
   *
   * ⎡  c3 s3 0 ⎤ ⎡ c2  0 -s2 ⎤ ⎡ 1   0  0 ⎤
   * ⎢ -s3 c3 0 ⎥ ⎢  0  1   0 ⎥ ⎢ 0  c1 s1 ⎥
   * ⎣   0  0 1 ⎦ ⎣ s2  0  c2 ⎦ ⎣ 0 -s1 c1 ⎦
   */
  res[0] = c3c2;  res[4] = s3c1 + c3s2s1;  res[8] = s3s1 - c3s2c1; res[12] = 0.f;
  res[1] = -s3c2; res[5] = c3c1 - s3s2s1;  res[9] = c3s1 + s3s2c1; res[13] = 0.f;
  res[2] = s2;    res[6] = -c2s1;         res[10] = c2c1;          res[14] = 0.f;
  res[3] = 0.f;   res[7] = 0.f;           res[11] = 0.f;           res[15] = 1.f;

}

static GLuint position_buffer;
static GLuint normal_buffer;
static GLuint color_buffer;
static GLuint program;
static GLuint m_location;
static GLuint v_location;
static GLuint p_location;

/* We need to set up our state when we realize the GtkGLArea widget */
static void realize (GtkWidget *widget) {
  gtk_gl_area_make_current (GTK_GL_AREA (widget));

  if (gtk_gl_area_get_error (GTK_GL_AREA (widget)) != NULL)
    return;

  init_buffers (NULL, &position_buffer, &normal_buffer, &color_buffer);
  init_shaders (&program, &m_location, &v_location, &p_location);
}

/* We should tear down the state when unrealizing */
static void unrealize (GtkWidget *widget) {
  gtk_gl_area_make_current (GTK_GL_AREA (widget));

  if (gtk_gl_area_get_error (GTK_GL_AREA (widget)) != NULL)
    return;

  glDeleteBuffers (1, &position_buffer);
  glDeleteBuffers (1, &normal_buffer);
  glDeleteBuffers (1, &color_buffer);
  glDeleteProgram (program);
}

static void draw_object (void) {
  float m[16];
  float v[16];
  float p[16];

  /* Compute the model view projection matrix using the
   * rotation angles specified through the GtkRange widgets
   */
  compute_mvp (p, rotation_angles[X_AXIS], rotation_angles[Y_AXIS], rotation_angles[Z_AXIS]);
  compute_i(m);
  compute_i(v);

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

  /* Use our shaders */
  glUseProgram (program);

  /* Update the "mvp" matrix we use in the shader */
  glUniformMatrix4fv (m_location, 1, GL_FALSE, &m[0]);
  glUniformMatrix4fv (v_location, 1, GL_FALSE, &v[0]);
  glUniformMatrix4fv (p_location, 1, GL_FALSE, &p[0]);

  /* Use the vertices in our buffer */
  glEnableVertexAttribArray (0);
  glBindBuffer (GL_ARRAY_BUFFER, position_buffer);
  glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

  // normales
  glEnableVertexAttribArray (1);
  glBindBuffer (GL_ARRAY_BUFFER, normal_buffer);
  glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);


  // couleurs
  glEnableVertexAttribArray (2);
  glBindBuffer (GL_ARRAY_BUFFER, color_buffer);
  glVertexAttribPointer (2, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

  glEnable(GL_DEPTH_TEST);
  glRenderMode(GL_RENDER);

  glDrawArrays (GL_TRIANGLES, 0, NBF);

  /* We finished using the buffers and program */
  glDisableVertexAttribArray (0);
  glDisableVertexAttribArray (1);
  glDisableVertexAttribArray (2);
  glBindBuffer (GL_ARRAY_BUFFER, 0);
  glUseProgram (0);
}

static gboolean render (GtkGLArea * area, GdkGLContext * context) {
  if (gtk_gl_area_get_error (area) != NULL)
    return FALSE;

  draw_object ();

  glFlush ();

  return TRUE;
}

static void on_axis_value_change (GtkAdjustment *adjustment, gpointer data) {
  int axis = GPOINTER_TO_INT (data);

  g_assert (axis >= 0 && axis < N_AXIS);

  /* Update the rotation angle */
  rotation_angles[axis] = gtk_adjustment_get_value (adjustment);

  /* Update the contents of the GL drawing area */
  gtk_widget_queue_draw (gl_area);
}

static GtkWidget * create_axis_slider (int axis) {
  GtkWidget *box, *label, *slider;
  GtkAdjustment *adj;
  const char *text;

  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);

  switch (axis) {
    case X_AXIS:
      text = "X";
      break;

    case Y_AXIS:
      text = "Y";
      break;

    case Z_AXIS:
      text = "Z";
      break;

    default:
      g_assert_not_reached ();
	}

  label = gtk_label_new (text);
  gtk_box_append (GTK_BOX (box), label);
  gtk_widget_show (label);

  adj = gtk_adjustment_new (0.0, 0.0, 360.0, 1.0, 12.0, 0.0);
  g_signal_connect (adj, "value-changed",
                    G_CALLBACK (on_axis_value_change),
                    GINT_TO_POINTER (axis));
  slider = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adj);
  gtk_box_append (GTK_BOX (box), slider);
  gtk_widget_set_hexpand (slider, TRUE);
  gtk_widget_show (slider);

  gtk_widget_show (box);

  return box;
}

static void activate(GtkApplication *app, gpointer user_data)
{
  GtkWidget *window, *box, *button, *controls;
  int i;

  window = gtk_application_window_new(app);
  gtk_window_set_default_size(GTK_WINDOW(window), 400, 600);
  gtk_window_set_title (GTK_WINDOW (window), "Volume");
  g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);

  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
  gtk_widget_set_margin_start (box, 12);
  gtk_widget_set_margin_end (box, 12);
  gtk_widget_set_margin_top (box, 12);
  gtk_widget_set_margin_bottom (box, 12);
  gtk_box_set_spacing (GTK_BOX (box), 6);
  gtk_window_set_child (GTK_WINDOW (window), box);

  gl_area = gtk_gl_area_new ();
  gtk_widget_set_hexpand (gl_area, TRUE);
  gtk_widget_set_vexpand (gl_area, TRUE);
  gtk_widget_set_size_request (gl_area, 100, 200);
  gtk_box_append (GTK_BOX (box), gl_area);

  // We need to initialize and free GL resources, so we use
  // the realize and unrealize signals on the widget
  //
  g_signal_connect (gl_area, "realize", G_CALLBACK (realize), NULL);
  g_signal_connect (gl_area, "unrealize", G_CALLBACK (unrealize), NULL);

  // The main "draw" call for GtkGLArea
  g_signal_connect (gl_area, "render", G_CALLBACK (render), NULL);

  controls = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
  gtk_box_append (GTK_BOX (box), controls);
  gtk_widget_set_hexpand (controls, TRUE);

  for (i = 0; i < N_AXIS; i++)
    gtk_box_append (GTK_BOX (controls), create_axis_slider (i));

  button = gtk_button_new_with_label ("Fermer");
  gtk_widget_set_hexpand (button, TRUE);
  gtk_box_append (GTK_BOX (box), button);
  g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_destroy), window);

  gtk_widget_show(window);
}

int main(int argc, char **argv)
{
  GtkApplication *app;
  int res;

  app = gtk_application_new(NULL, G_APPLICATION_DEFAULT_FLAGS);
  g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
  res = g_application_run(G_APPLICATION(app), argc, argv);
  g_object_unref(app);

  return res;
}