Question

I found a lot cases to create multiple objects by one single vertices array. And then through transform to get different ones, such as Draw 2 cubes in OpenGL using GLM and Cant draw multiple objects in opengl.

But what I want is to create complete diff objects by complete diff vertices arrays. A typical case is http://www.spacesimulator.net/wiki/index.php?title=Tutorials:Matrices_%28OpenGL3.3%29. How to implement it by GLM? I tried to find some example codes.

Was it helpful?

Solution

Next code are a complete example to draw 2 cubes using OpenGL and Linux Debian 9. Also, has a class to see pressed buton and positions if click the mouse. All sources and Makefile are included.

  • To compile: copy all files with correct name in same directory and type make
  • To execute: type ./start

All needed libraries are installed with next commands:

apt-get install libglew-dev
apt-get install freeglut3-dev
apt-get install libglm-dev

The example are created from multiple examples from Internet and changed to Object Oriented code to clarify.

I hope it's useful.

File: cube.hpp

#ifndef CUBE_H
#define CUBE_H

#include <stdio.h>
#include <stdlib.h>
#include <glm/gtc/type_ptr.hpp>
#include <GL/glew.h>
#include <GL/glut.h>

class Cube{

   private:

      GLuint vboCubeVertex, vboCubeColors, vboCubeFaces;
      GLint attribute_coord3d, attribute_v_color;

   public:

      int init_resources(GLuint shaderProgram);
      void draw(GLint uniform_mvp, glm::mat4 mvp);
      void disable();
      void deleteBuffers();
};

#endif

File: cube.cpp

#include "cube.hpp"

int Cube::init_resources(GLuint shaderProgram)
{
   fprintf(stderr, "init_resources\n");

      //**********************************************************
      // VERTEX **************************************************
      //**********************************************************

      // Each vertex has the format x,y,z

      GLfloat arrCubeVertex[] = {
         // front
         -1.0, -1.0,  1.0,
          1.0, -1.0,  1.0,
          1.0,  1.0,  1.0,
         -1.0,  1.0,  1.0,
         // back
         -1.0, -1.0, -1.0,
         1.0, -1.0, -1.0,
         1.0,  1.0, -1.0,
         -1.0,  1.0, -1.0,
      };
      //**********************************************************
      // COLORS OF EACH VERTEX ***********************************
      //**********************************************************

      GLfloat arrCubeColors[] = {
         // front colors
         1.0, 0.0, 0.0,
         0.0, 1.0, 0.0,
         0.0, 0.0, 1.0,
         1.0, 1.0, 1.0,
         // back colors
         1.0, 0.0, 0.0,
         0.0, 1.0, 0.0,
         0.0, 0.0, 1.0,
         1.0, 1.0, 1.0,
      };

      // Faces are squares, each square are maked wit two triangles.
      // See document OpenGL_Charly.pdf.

      GLushort arrCubeFaces[] = {
         // front
         0, 1, 2,
         2, 3, 0,
         // right
         1, 5, 6,
            6, 2, 1,
         // back
         7, 6, 5,
         5, 4, 7,
         // left
         4, 0, 3,
         3, 7, 4,
         // bottom
         4, 5, 1,
         1, 0, 4,
         // up
         3, 2, 6,
         6, 7, 3,
      };


   glGenBuffers(1, &vboCubeVertex);
   glBindBuffer(GL_ARRAY_BUFFER, vboCubeVertex);
   glBufferData(GL_ARRAY_BUFFER, sizeof(arrCubeVertex), arrCubeVertex, GL_STATIC_DRAW);

   // Las mismas operaciones que hicimos con los vertices, las hacemos ahora con los colores.
   glGenBuffers(1, &vboCubeColors);
   glBindBuffer(GL_ARRAY_BUFFER, vboCubeColors);
   glBufferData(GL_ARRAY_BUFFER, sizeof(arrCubeColors), arrCubeColors, GL_STATIC_DRAW);

   // Ahora lo mismo pero con las caras.
   glGenBuffers(1, &vboCubeFaces);
   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboCubeFaces);
   glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(arrCubeFaces), arrCubeFaces, GL_STATIC_DRAW);

   //**********************************************************


   attribute_coord3d = glGetAttribLocation(shaderProgram, "coord3d");
   if (attribute_coord3d == -1)
   {
      fprintf(stderr, "Could not bind attribute %s\n", "coord3d");
      return 0;
   }

   attribute_v_color = glGetAttribLocation(shaderProgram, "v_color");
   if (attribute_v_color == -1)
   {
      fprintf(stderr, "Could not bind attribute %s\n", "v_color");
      return 0;
   }

   return 1;
}

void Cube::draw(GLint uniform_mvp, glm::mat4 mvp){

   glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, glm::value_ptr(mvp));

   // Cube vertices
   glEnableVertexAttribArray(attribute_coord3d);
   glBindBuffer(GL_ARRAY_BUFFER, vboCubeVertex);

   // Attribute, elements per vertex (x,y,z), the type of each element, take our values as-is, no extra data between each position, offset of first element
   glVertexAttribPointer(attribute_coord3d, 3, GL_FLOAT, GL_FALSE, 0, 0);

   // Cube colors
   glEnableVertexAttribArray(attribute_v_color);
   glBindBuffer(GL_ARRAY_BUFFER, vboCubeColors);

   // Attribute, elements per vertex (R,G,B), the type of each element, take our values as-is, no extra data between each position, offset of first element
   glVertexAttribPointer(attribute_v_color, 3, GL_FLOAT, GL_FALSE, 0, 0);

   /* Push each element in buffer_vertices to the vertex shader */
   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboCubeFaces);

   int size;
   glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
   glDrawElements(GL_TRIANGLES, size/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);

}

void Cube::disable(){

   glDisableVertexAttribArray(attribute_coord3d);
   glDisableVertexAttribArray(attribute_v_color);

}

void Cube::deleteBuffers(){

   glDeleteBuffers(1, &vboCubeVertex);
   glDeleteBuffers(1, &vboCubeColors);
   glDeleteBuffers(1, &vboCubeFaces);
}

File: mouse.hpp

#ifndef MOUSE_H
#define MOUSE_H

#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>

struct Point {
   GLint x;
   GLint y;
};


class Mouse{

   private:

      Point p1, p2;

   public:

       int show(int button, int state, int x, int y);
};

#endif

File: mouse.cpp

#include "mouse.hpp"

int Mouse::show(int button, int state, int x, int y)
{

   if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
      printf("%s\n", "LEFT DOWN");
   }
   else if(button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
      printf("%s\n", "LEFT UP");
   }

   printf("mouse X: %d, Y: %d\n", x, y);

}

File: shader_utils.hpp

#ifndef _CREATE_SHADER_H
#define _CREATE_SHADER_H
#include <GL/glew.h>
char* file_read(const char* filename);
void print_log(GLuint object);
GLuint create_shader(const char* filename, GLenum type);
GLuint create_program(const char* vertexfile, const char *fragmentfile);
GLuint create_gs_program(const char* vertexfile, const char *geometryfile, const char *fragmentfile, GLint input, GLint output, GLint vertices);
GLint get_attrib(GLuint program, const char *name);
GLint get_uniform(GLuint program, const char *name);
#endif

File: shader_utils.cpp

    #include <stdio.h>
    #include <stdlib.h>
    #include "shader_utils.hpp"

    char* file_read(const char* filename)
    {
       FILE* in = fopen(filename, "rb");
       if (in == NULL) return NULL;

      int res_size = BUFSIZ;
      char* res = (char*)malloc(res_size);
      int nb_read_total = 0;

      while (!feof(in) && !ferror(in)) {
        if (nb_read_total + BUFSIZ > res_size) {
          if (res_size > 10*1024*1024) break;
          res_size = res_size * 2;
          res = (char*)realloc(res, res_size);
        }
        char* p_res = res + nb_read_total;
        nb_read_total += fread(p_res, 1, BUFSIZ, in);
      }

      fclose(in);
      res = (char*)realloc(res, nb_read_total + 1);
      res[nb_read_total] = '\0';
      return res;
    }

    void print_log(GLuint object)
    {
      GLint log_length = 0;
      if (glIsShader(object))
        glGetShaderiv(object, GL_INFO_LOG_LENGTH, &log_length);
      else if (glIsProgram(object))
        glGetProgramiv(object, GL_INFO_LOG_LENGTH, &log_length);
      else {
        fprintf(stderr, "printlog: Not a shader or a program\n");
        return;
      }

      char* log = (char*)malloc(log_length);

      if (glIsShader(object))
        glGetShaderInfoLog(object, log_length, NULL, log);
      else if (glIsProgram(object))
        glGetProgramInfoLog(object, log_length, NULL, log);

      fprintf(stderr, "%s", log);
      free(log);
    }

   GLuint create_shader(const char* filename, GLenum type)
    {
      const GLchar* source = file_read(filename);
      if (source == NULL) {
        fprintf(stderr, "Error opening %s: ", filename); perror("");
        return 0;
      }
      GLuint res = glCreateShader(type);
      const GLchar* sources[] = {
        // Define GLSL version
    #ifdef GL_ES_VERSION_2_0
        "#version 100\n"
    #else
        "#version 120\n"
    #endif
        ,
        // GLES2 precision specifiers
    #ifdef GL_ES_VERSION_2_0

        // Define default float precision for fragment shaders:
        (type == GL_FRAGMENT_SHADER) ?
        "#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
        "precision highp float;           \n"
        "#else                            \n"
        "precision mediump float;         \n"
        "#endif                           \n"
        : ""
        // Note: OpenGL ES automatically defines this:
        // #define GL_ES
    #else
        // Ignore GLES 2 precision specifiers:
        "#define lowp   \n"
        "#define mediump\n"
        "#define highp  \n"
    #endif
        ,
        source };
      glShaderSource(res, 3, sources, NULL);
      free((void*)source);

      glCompileShader(res);
      GLint compile_ok = GL_FALSE;
      glGetShaderiv(res, GL_COMPILE_STATUS, &compile_ok);
      if (compile_ok == GL_FALSE) {
        fprintf(stderr, "%s:", filename);
        print_log(res);
        glDeleteShader(res);
        return 0;
      }

      return res;
    }

    GLuint create_program(const char *vertexfile, const char *fragmentfile) {
        GLuint program = glCreateProgram();
        GLuint shader;

        if(vertexfile) {
            shader = create_shader(vertexfile, GL_VERTEX_SHADER);
            if(!shader)
                return 0;
            glAttachShader(program, shader);
        }

        if(fragmentfile) {
            shader = create_shader(fragmentfile, GL_FRAGMENT_SHADER);
            if(!shader)
                return 0;
            glAttachShader(program, shader);
        }

        glLinkProgram(program);
        GLint link_ok = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
        if (!link_ok) {
            fprintf(stderr, "glLinkProgram:");
            print_log(program);
            glDeleteProgram(program);
            return 0;
        }

        return program;
    }

    #ifdef GL_GEOMETRY_SHADER
    GLuint create_gs_program(const char *vertexfile, const char *geometryfile, const char *fragmentfile, GLint input, GLint output, GLint vertices) {
        GLuint program = glCreateProgram();
        GLuint shader;

        if(vertexfile) {
            shader = create_shader(vertexfile, GL_VERTEX_SHADER);
            if(!shader)
                return 0;
            glAttachShader(program, shader);
        }

        if(geometryfile) {
            shader = create_shader(geometryfile, GL_GEOMETRY_SHADER);
            if(!shader)
                return 0;
            glAttachShader(program, shader);

            glProgramParameteriEXT(program, GL_GEOMETRY_INPUT_TYPE_EXT, input);
            glProgramParameteriEXT(program, GL_GEOMETRY_OUTPUT_TYPE_EXT, output);
            glProgramParameteriEXT(program, GL_GEOMETRY_VERTICES_OUT_EXT, vertices);
        }

        if(fragmentfile) {
            shader = create_shader(fragmentfile, GL_FRAGMENT_SHADER);
            if(!shader)
                return 0;
            glAttachShader(program, shader);
        }

        glLinkProgram(program);
        GLint link_ok = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
        if (!link_ok) {
            fprintf(stderr, "glLinkProgram:");
            print_log(program);
            glDeleteProgram(program);
            return 0;
        }

        return program;
    }
    #else
    GLuint create_gs_program(const char *vertexfile, const char *geometryfile, const char *fragmentfile, GLint input, GLint output, GLint vertices) {
        fprintf(stderr, "Missing support for geometry shaders.\n");
        return 0;
    }
    #endif

    GLint get_attrib(GLuint program, const char *name) {
        GLint attribute = glGetAttribLocation(program, name);
        if(attribute == -1)
            fprintf(stderr, "Could not bind attribute %s\n", name);
        return attribute;
    }

    GLint get_uniform(GLuint program, const char *name) {
        GLint uniform = glGetUniformLocation(program, name);
        if(uniform == -1)
            fprintf(stderr, "Could not bind uniform %s\n", name);
        return uniform;
    }

File: start.cpp

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/glut.h>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include "shader_utils.hpp"
#include "mouse.hpp"
#include "cube.hpp"

int scrWidth = 600;
int scrHeight = 600;
GLuint shaderProgram;
GLint uniform_mvp;

Cube cube1;
Cube cube2;
glm::mat4 mvpCube1;
glm::mat4 mvpCube2;
Mouse theMouse;


void onIdle()
{

   glm::vec3 rotationCube1(0, 1, 0);                                  // Rotate cube 1 over Y axis
   float angleCube1 = glutGet(GLUT_ELAPSED_TIME) / 1000.0 * 5;        // 5° per second
   glm::vec3 translationCube1 (0.0, 0.0, -2.0);                       // Translate cube 1 over Z axis

   glm::vec3 rotationCube2(1, 0, 0);                                  // Rotate cube 2 over X axis
   float angleCube2 = glutGet(GLUT_ELAPSED_TIME) / 1000.0 * 3;        // 3° per second
   glm::vec3 translationCube2 (1.0, 0.0, 0.0);                        // Translate cube 2 over X axis

   // Frustum parameters
   float angleVision = 45.0f;     // Also called Fovy
   float aspect = 1.0f * scrWidth / scrHeight;
   float zNear = 0.1f;
   float zFar = 10.f;

   // Camera parameters
   glm::vec3 cameraEye(0.0, 2.0, -10.0);
   glm::vec3 cameraCenter(0.0, 0.0, -5.0);
   glm::vec3 cameraUp(0.0, 1.0, 0.0);

   // Method lookAt has 3 parameters: eye, center, up
   glm::mat4 view = glm::lookAt(cameraEye, cameraCenter, cameraUp);

   // Create the projection matrix
   glm::mat4 projection = glm::perspective(angleVision, aspect, zNear, zFar);

   glm::mat4 modelCube1 = glm::mat4();                                 // Carga model con la identity matrix
   modelCube1 = glm::translate(modelCube1, translationCube1);          // El modelo queda trasladado.
   modelCube1 = glm::rotate(modelCube1, angleCube1, rotationCube1);    // El modelo ahora queda trasladado y rotado.
   mvpCube1 = projection * view * modelCube1;

   glm::mat4 modelCube2 = glm::mat4();                                 // Carga model con la identity matrix
   modelCube2 = glm::translate(modelCube2, translationCube2);          // El modelo queda trasladado.
   modelCube2 = glm::rotate(modelCube2, angleCube2, rotationCube2);    // El modelo ahora queda trasladado y rotado.
   mvpCube2 = projection * view * modelCube2;

   glUseProgram(shaderProgram);
   glutPostRedisplay();
}

void onDisplay()
{
   glClearColor(0.0, 0.0, 0.0, 1.0);   // Set color (0, 0, 0 = Black)
                                       // R,G,B,A

   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
   glUseProgram(shaderProgram);        // Program with loaded shaders

   cube1.draw(uniform_mvp, mvpCube1);
   cube1.disable();

   cube2.draw(uniform_mvp, mvpCube2);
   cube2.disable();

   glutSwapBuffers();
}


void onReshape(int width, int height)
{
   fprintf(stderr, "onReshape\n");

   scrWidth = width;
   scrHeight = height;
   glViewport(0, 0, scrWidth, scrHeight);
}

void free_resources()
{
   fprintf(stderr, "free_resources\n");
   glDeleteProgram(shaderProgram);

   cube1.deleteBuffers();
   cube2.deleteBuffers();
}

void myMouseFunc(int button, int state, int x, int y)
{
   theMouse.show(button, state, x, y);
}

int init(){

   uniform_mvp = glGetUniformLocation(shaderProgram, "mvp");
   if (uniform_mvp == -1)
   {
      fprintf(stderr, "Could not bind uniform %s\n", "mvp");
      return 0;
   }
   return 1;
}

int main(int argc, char* argv[])
{
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_RGBA|GLUT_ALPHA|GLUT_DOUBLE|GLUT_DEPTH);
   glutInitWindowSize(scrWidth, scrHeight);
   glutCreateWindow("Two Cubes");

   GLenum glew_status = glewInit();

   if (glew_status != GLEW_OK)
   {
      fprintf(stderr, "Error: %s\n", glewGetErrorString(glew_status));
      return 1;
   }

   if (!GLEW_VERSION_2_0)
   {
      fprintf(stderr, "Error: your graphic card does not support OpenGL 2.0\n");
      return 1;
   }

   GLint link_ok = GL_FALSE;

   GLuint vs, fs;

   if ((vs = create_shader("cube_vertex.glsl", GL_VERTEX_SHADER))   == 0) return 0;
   if ((fs = create_shader("cube_fragment.glsl", GL_FRAGMENT_SHADER)) == 0) return 0;

   shaderProgram = glCreateProgram();
   glAttachShader(shaderProgram, vs);
   glAttachShader(shaderProgram, fs);
   glLinkProgram(shaderProgram);
   glGetProgramiv(shaderProgram, GL_LINK_STATUS, &link_ok);
   if (!link_ok)
   {
      fprintf(stderr, "glLinkProgram:");
      print_log(shaderProgram);
      return 0;
   }

   cube1 = Cube();
   cube2 = Cube();

   if (init() && cube1.init_resources(shaderProgram) && cube2.init_resources(shaderProgram))
   {

      glutMouseFunc(myMouseFunc);

      glutDisplayFunc(onDisplay);      // Set the display function for the current window.
      glutReshapeFunc(onReshape);      // Set the reshape function for the current window.
      glutIdleFunc(onIdle);            // Set the global idle callback (perform background proccessing)

      // Enable server side capabilities
      glEnable(GL_BLEND);              // GL_BLEND: If enabled, blend the computed fragment color values with the
                                       // values in the color buffers. See glBlendFunc.

      glEnable(GL_DEPTH_TEST);         // GL_DEPTH_TEST: If enabled, do depth comparisons and update the depth buffer.
                                       // Note that even if the depth buffer exists and the depth mask is non-zero,
                                       // the depth buffer is not updated if the depth test is disabled. See glDepthFunc
                                       // and glDepthRange.


      //glDepthFunc(GL_LESS);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      glutMainLoop();
   }

   free_resources();
   return 0;
}

File: cube_fragment.glsl

varying vec3 f_color;

void main(void) {
  gl_FragColor = vec4(f_color.x, f_color.y, f_color.z, 1.0);
}

File: cube_vertex.glsl

attribute vec3 v_color;
uniform mat4 mvp;
varying vec3 f_color;

void main(void)
{
  gl_Position = mvp * vec4(coord3d, 1.0);
  f_color = v_color;
}

File: Makefile

LDLIBS=-lglut -lGLEW -lGL -lm
all: start
clean:
    rm -f *.o start
start: mouse.o shader_utils.o cube.o
.PHONY: all clean

The result are two rotating cubes:

enter image description here

OTHER TIPS

Firstly, GLM is a maths library that provides vectors, matrices etc that behave and look like (as much as possible) vectors and matrices used in GLSL - that is, shader code. GLM is useful when writing applications that use OpenGL, but it actually isn't necessary, and it's got nothing to do with rendering objects.

If you want to draw different objects using different vertex arrays, read a tutorial such as the one mentioned or check out http://www.arcsynthesis.org/gltut/. See how it loads data into a vertex array and renders it, then do this twice for your different arrays. Bear in mind that switching vertex arrays is slow - you might want to load multiple objects into one vertex buffer and then use a render command with index offsets (e.g. glMultiDrawElementsBaseVertex).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top