Page Example

GLViewShader Sample Overview

Mobile native

The GLViewShader sample demonstrates how to render more complex geometries with OpenGL ES 2.0, using slightly more advanced vertex and fragment shaders to draw a rotating teapot. It also demonstrates how to use Elementary GLView helper macros to port existing code easily to Tizen.

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: GLViewShader



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"


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

Setting up Callbacks

To set 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.

    A Vertex Buffer Object (VBO) allows vertex array data to be stored in a high-performance graphics memory on the server to maximize data transfer efficiency.

    A VBO is created in the application for the teapot vertices. Additionally, an Index Buffer Object (IBO) is created for the indices. The glGenBuffers() function specifies an array in which the buffer object name is stored.

    The glBindBuffer() function binds the buffer object to a specified target. In this case, the target is GL_ARRAY_BUFFER.

    The glBufferData() function is used to create a new data storage for the current buffer object. The parameters of the function are the buffer object target, the data store size, the array of teapot vertices, and the usage function of the data store (in this case, GL_STATIC_DRAW).

    static void init_gl(Evas_Object *obj) 
       appdata_s *ad = evas_object_data_get(obj, "ad");
       if (!ad->initialized) 
          glGenBuffers(1, &ad->idx_vbo);
          glBindBuffer(GL_ARRAY_BUFFER, ad->idx_vbo);
          glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float) * MAX_V_COUNT,
          glGenBuffers(1, &ad->idx_ibo);
          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ad->idx_ibo);
                3 * sizeof(unsigned short) * MAX_F_COUNT, TEAPOT_INDICES,
          ad->initialized = EINA_TRUE;
  2. Initialize the shaders:
    1. Vertex and fragment shaders are created and compiled by passing the shader parameters 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);
         p = fragment_shader;
         ad->fgmt_shader = glCreateShader(GL_FRAGMENT_SHADER);
         glShaderSource(ad->fgmt_shader, 1, &p, NULL);
    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);
    3. Get the location for each attribute in the shader program using the glGetUniformLocation() and glGetAttribLocation() functions. If the shader program attributes, such as u_mvpMatrix and a_position 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 glGetUniformLocation() function retrieves the location of the uniform related to groups of vertices or pixels, such as the model view matrix, projection matrix, or light position. The glGetAttribLocation() function retrieves the location of the attributes related to a vertex or pixel, such as position vector, normal vector, or vertex color.

         ad->idx_light_dir = glGetUniformLocation(ad->program, "u_light_dir");
         ad->idx_mvp = glGetUniformLocation(ad->program, "u_mvpMatrix");
         ad->idx_time_stamp = glGetUniformLocation(ad->program, "u_time_stamp");
         ad->idx_vposition = glGetAttribLocation(ad->program, "a_position");
         ad->idx_vnormal = glGetAttribLocation(ad->program, "a_normal");
    4. Install the shader program and enable the GPU to execute the shader operations in the frame buffer.
  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) 
         elm_glview_size_get(obj, &ad->glview_w, &ad->glview_h);
         glViewport(0, 0, ad->glview_w, ad->glview_h);
    2. The glUniform4fv() function modifies the light direction in the graphic based on the light-related information of the teapot.

      The glUniformMatrix4fv() modifies the values of the 4x4 MVP (Model View/Projection) uniform matrix according to the vertices information of the teapot. An MVP matrix is created with data after each vertex of the teapot 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.

      The glUniform1f() function modifies the timer-related information while drawing the teapot on the screen.

         glUniform4fv(ad->idx_light_dir, 1, ad->light_dir);
         glUniformMatrix4fv(ad->idx_mvp, 1, GL_FALSE, ad->mvp);
         glUniform1f(ad->idx_time_stamp, ad->time_stamp);
    3. The glVertexAttribPointer() function is used to define an array of generic vertex attribute data. These attributes are parameters associated with each vertex of the teapot. In this case, they are position and normal.

      A normal tells you in which direction a surface is facing. This helps in identifying the proper lighting of a surface.

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

         glBindBuffer(GL_ARRAY_BUFFER, ad->idx_vbo);
         glVertexAttribPointer(ad->idx_vposition, 3, GL_FLOAT, GL_FALSE,
               sizeof(float) * 6, 0);
         glVertexAttribPointer(ad->idx_vnormal, 3, GL_FLOAT, GL_FALSE,
               sizeof(float) * 6, (void*) (sizeof(float) * 3));
         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ad->idx_ibo);
    4. Once the vertex arrays have been enabled, the actual process of rendering graphics primitives from the array data occurs using the glDrawElements() function.
         glDrawElements(GL_TRIANGLES, MAX_F_COUNT * 3,
                        GL_UNSIGNED_SHORT, 0);
    5. The glFlush() function empties all buffers causing all issued commands to be executed as quickly as they are accepted by the rendering engine.

      Finally, the vertex attribute arrays are disabled using the glDisableVertexAttribArray() function, and the buffer object binding is released by calling the glBindBuffer() function. The buffer is reset at 0.

         glBindBuffer(GL_ARRAY_BUFFER, 0);
         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  5. Add an animator.

    The application regularly triggers an update 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) 
       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;
    static void
    app_resume(void *data)
       appdata_s *ad = data;
  6. Set the delete callback.

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

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

    static void del_gl(Evas_Object *obj) 
       appdata_s *ad = evas_object_data_get(obj, "ad");
       glDeleteBuffers(1, &ad->idx_vbo);
       glDeleteBuffers(1, &ad->idx_ibo);
       evas_object_data_del((Evas_Object*) obj, "ad");