Page Example

GLViewCube Sample Overview

Mobile native

This sample demonstrates how to render with OpenGL ES 2.0 using an Elementary GLView component. It goes through the creation of a simple UI and how to draw a rotating cube using simple vertex and fragment shaders.

Note
The application presented in this overview is not a full Tizen application, since it does not use the Application Framework. The application simply starts and runs.

Figure: GLViewCube

GLViewCube

Elementary_GL_Helpers

The <Elementary_GL_Helpers.h> header file provides a set of convenient functions and macros. To use these functions and macros, include this header file in the application.

#include <app.h>
#include <Elementary.h>
#include <Elementary_GL_Helpers.h>
#include <efl_extension.h>

#include "glviewcube_utils.h"

ELEMENTARY_GLVIEW_GLOBAL_DEFINE();

static bool app_create(void *data) 
{
   // Create and initialize GLView
   gl = elm_glview_add(ad->conform);
   ELEMENTARY_GLVIEW_GLOBAL_USE(gl);
}

Setting up Callbacks

  1. Set the initialization callback.

    The initialization callback is called when the GLView is first created, after a valid OpenGL ES context and surface have been created.

    This callback function initializes shaders using the init_shaders(obj) function.

    static void init_gl(Evas_Object *obj) 
    {
       appdata_s *ad = evas_object_data_get(obj, "ad");
    
       if (!ad->initialized) 
       {
          init_shaders(obj);
          glEnable(GL_DEPTH_TEST);
          ad->initialized = EINA_TRUE;
       }
    }
    
  2. Initialize the shaders.
    1. The shader instances for the fragment and vertex shader are compiled and created by passing the shader type as a parameter to the glCreateShader() function.

      Load the shader objects, use the GLES2 Shader compiler to read and compile the shader source code for both the fragment and the vertex shader.

      static void init_shaders(Evas_Object *obj) 
      {
         appdata_s *ad = evas_object_data_get(obj, "ad");
         const char *p;
         p = vertex_shader;
         ad->vtx_shader = glCreateShader(GL_VERTEX_SHADER);
         glShaderSource(ad->vtx_shader, 1, &p, NULL);
         glCompileShader(ad->vtx_shader);
      
         p = fragment_shader;
         ad->fgmt_shader = glCreateShader(GL_FRAGMENT_SHADER);
         glShaderSource(ad->fgmt_shader, 1, &p, NULL);
         glCompileShader(ad->fgmt_shader);
      
    2. Once the shader objects have been loaded, a shader program is created. The program attaches and links the shader objects to the shader program.
         ad->program = glCreateProgram();
         glAttachShader(ad->program, ad->vtx_shader);
         glAttachShader(ad->program, ad->fgmt_shader);
         glLinkProgram(ad->program);
      
    3. Get the location for each vertex or pixel attribute in the shader program using the glGetAttribLocation() and glGetUniformLocation() functions. If the shader program attributes a_position, a_color, and u_mvpMatrix are valid, these functions return an integer value that represents the index location of the attributes. For invalid attributes, the return value is -1.

      The glGetAttribLocation() function retrieves the location of the attributes that have an effect on a vertex or pixel, such as position vector, normal vector, or vertex color. The glGetUniformLocation() retrieves the location of the attributes that have an effect on groups of vertices or pixels, such as model view matrix, projection matrix, or light position.

         ad->idx_position = glGetAttribLocation(ad->program, "a_position");
         ad->idx_color = glGetAttribLocation(ad->program, "a_color");
         ad->idx_mvp = glGetUniformLocation(ad->program, "u_mvpMatrix");
      	
    4. Install the shader program and enable the GPU to execute the shader operations in the frame buffer.
         glUseProgram(ad->program);
      }
      
  3. Set the resize callback.

    The resize callback is called whenever the GLView component is resized. It resets the viewport.

    static void resize_gl(Evas_Object *obj) 
    {
       appdata_s *ad = evas_object_data_get(obj, "ad");
       elm_glview_size_get(obj, &ad->glview_w, &ad->glview_h);
    }
    
  4. Set the draw callback.

    The draw callback is called whenever a new frame has to be drawn.

    The application can now draw anything using GL primitives when this callback is triggered.

    1. The glViewport() function specifies the affine transformation of the x and y values from normalized device coordinates to window coordinates as specified by the elm_glview_size_get() function.
      static void draw_gl(Evas_Object *obj) 
      {
         glViewport(0, 0, ad->glview_w, ad->glview_h);
      
    2. The glVertexAttribPointer() function is used to define an array of generic vertex attribute data. These attributes are parameters associated with each vertex of the cube. In this case, the parameters are position and color. In both instances, the final parameter (vertices and colors) represent the previously defined arrays - vertices and colors.

      The glEnableVertexAttribArray() function enables the generic vertex attribute data arrays. The enabled attributes can be accessed and used to render the scene.

         glVertexAttribPointer(ad->idx_position, 3, GL_FLOAT, GL_FALSE,
               3 * sizeof(float), cube_vertices);
         glVertexAttribPointer(ad->idx_color, 4, GL_FLOAT, GL_FALSE,
               4 * sizeof(float), cube_colors);
      
         glEnableVertexAttribArray(ad->idx_position);
         glEnableVertexAttribArray(ad->idx_color);
      
    3. The glUniformMatrix4fv() function modifies the values of the 4x4 MVP (Model View/Projection) uniform matrix according to the vertices information of the cube. An MVP matrix is created with data after each vertex of the cube has passed through 2 transformation stages: The first transformation state is the model view transformation, which includes translation, rotation, and scaling of objects. The second state is projection, which includes changes in the perspective or orthography.
         glUniformMatrix4fv(ad->idx_mvp, 1, GL_FALSE, ad->mvp);
    4. Once the vertex arrays have been enabled, the actual process of rendering graphics primitives from the array data occurs using the glDrawElements() function. The final parameter in this function is the indices array.
         glDrawElements(GL_TRIANGLES, cube_indices_count, 				
                        GL_UNSIGNED_SHORT, cube_indices);
      }
      
  5. Add an animator.

    The application regularly triggers updates of the GLView using the elm_glview_changed_set() function.

    The animator callback function is also triggered when the display is off. Use the ecore_animator_freeze() and ecore_animator_thaw() functions in the app_pause_cb and app_resume_cb callbacks for power saving.

    static Eina_Bool anim(void *data) 
    {
       elm_glview_changed_set(data);
    
       return EINA_TRUE;
    }
    
    static bool app_create(void *data) 
    {
       ad->ani = ecore_animator_add(anim, gl);
    }
    
    static void
    app_pause(void *data)
    {
       appdata_s *ad = data;
       ecore_animator_freeze(ad->ani);
    }
    
    static void
    app_resume(void *data)
    {
       appdata_s *ad = data;
       ecore_animator_thaw(ad->ani);
    }
    
  6. Set event callbacks.

    The event callbacks receive touch events and allow you to rotate the cube in a vertical or horizontal direction.

    static void mouse_down_cb(void *data, Evas *e , Evas_Object *obj , void *event_info)
    {
       appdata_s *ad = data;
       ad->mouse_down = EINA_TRUE;
    }
    
    static void mouse_move_cb(void *data, Evas *e , Evas_Object *obj , void *event_info)
    {
       Evas_Event_Mouse_Move *ev;
       ev = (Evas_Event_Mouse_Move *)event_info;
       appdata_s *ad = data;
       float dx = 0, dy = 0;
    
       if (ad->mouse_down) 
       {
          dx = ev->cur.canvas.x - ev->prev.canvas.x;
          dy = ev->cur.canvas.y - ev->prev.canvas.y;
          ad->xangle += dy;
          ad->yangle += dx;
       }
    }
    
    static void mouse_up_cb(void *data, Evas *e , Evas_Object *obj , void *event_info)
    {
       appdata_s *ad = data;
       ad->mouse_down = EINA_FALSE;
    }
    
  7. Set the delete callback.

    The delete callback is triggered when the GLView is destroyed from the main loop.

    The glDeleteShader() and glDeleteProgram() functions are used to free memory.

    static void del_gl(Evas_Object *obj) 
    {
       appdata_s *ad = evas_object_data_get(obj, "ad");
    
       glDeleteShader(ad->vtx_shader);
       glDeleteShader(ad->fgmt_shader);
       glDeleteProgram(ad->program);
    
       evas_object_data_del((Evas_Object*) obj, "ad");
    }