pizzaboy150
Posts: 18
Joined: Sat Sep 05, 2015 11:03 am

An example project for beginner's of OpenGL ES 2

Sat Sep 19, 2015 7:42 pm

Hi Everyone,

I have been learning OpenGL ES 2 for the last few weeks and its been a steep learning curve to say the least.

Lots of hours spent looking at code and reading documentation to where I am now.

I thought it might be useful to anyone else learning to see more example code to make life a little easier.

so here it is for a basic spinning triangle with vertex colours.

Hope someone finds it useful?

tar file
https://drive.google.com/file/d/0B78wUP ... sp=sharing

main.c

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include "bcm_host.h"
#include "egl.h"
#include "gl2.h"
#include "glMath.h"

EGLDisplay egldisplay;
EGLConfig eglconfig;
EGLSurface eglsurface;
EGLContext eglcontext;
uint32_t width, height;

void Init()
{
    //BCM host bit used to get a nativewindow to pass to EGL
    bcm_host_init();
    int s;
    static EGL_DISPMANX_WINDOW_T nativewindow;
    DISPMANX_ELEMENT_HANDLE_T dispman_element;
    DISPMANX_DISPLAY_HANDLE_T dispman_display;
    DISPMANX_UPDATE_HANDLE_T dispman_update;
    VC_RECT_T dst_rect;
    VC_RECT_T src_rect;
    s = graphics_get_display_size(0 /* LCD */, &width, &height);
    dst_rect.x = 0;
    dst_rect.y = 0;
    dst_rect.width = width;
    dst_rect.height = height;
    src_rect.x = 0;
    src_rect.y = 0;
    src_rect.width = width << 16;
    src_rect.height = height << 16;
    dispman_display = vc_dispmanx_display_open( 0 /* LCD */);
    dispman_update = vc_dispmanx_update_start( 0 );
    dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display,
    1/*layer*/, &dst_rect, 0/*src*/,
    &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/);
    nativewindow.element = dispman_element;
    nativewindow.width = width;
    nativewindow.height = height;
    vc_dispmanx_update_submit_sync( dispman_update );
    NativeWindowType *win = &nativewindow;
    
    //Lets fire up EGL for our context
    static const EGLint s_configAttribs[] =
    {
        EGL_RED_SIZE,        8,
        EGL_GREEN_SIZE,     8,
        EGL_BLUE_SIZE,        8,
        EGL_ALPHA_SIZE,     8,
        EGL_LUMINANCE_SIZE, EGL_DONT_CARE,
        EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,
        EGL_SAMPLES,        1,
        EGL_NONE
    }
    ;
    
    //This seems important for using ES2
    static const EGLint context_attributes[] =
    {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    }
    ;
    
    //Finish off the startup
    EGLint numconfigs;
    egldisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(egldisplay, NULL, NULL);
    eglBindAPI(EGL_OPENGL_ES_API);
    eglChooseConfig(egldisplay, s_configAttribs, &eglconfig, 1, &numconfigs);
    eglsurface = eglCreateWindowSurface(egldisplay, eglconfig, win, NULL);
    eglcontext = eglCreateContext(egldisplay, eglconfig, EGL_NO_CONTEXT, context_attributes);
    eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglcontext);
}


void Deinit()
{
    //clean up functions to clear memory
    eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglTerminate(egldisplay);
    eglReleaseThread();
}


//Function to read a file and return its contents in a character string
char * ReadFile(const char *url)
{
    //open the file for reading
    FILE *file = fopen(url, "rb");
    if(!file)
    {
        printf("unable to open file : %s", url);
        exit(1);
    }
    
    
    //read the contents of a file
    //seek to the end of the file to get the size
    long lSize;
    fseek (file, 0L, SEEK_END);
    lSize = ftell(file);
    rewind(file);
    
    //lets allocate the memory for the file
    char *buffer = calloc(1, lSize+1);
    if(!buffer)
    {
        printf("Unable to allocate memory for file buffer!n");
        fclose(file);
        free(buffer);
        exit(1);
    }
    
    
    //lets copy the data into the buffer
    if(fread(buffer, lSize, 1, file) == 0)
    {
        //nothing read
        printf("Nothing read from file : %s", url);
        fclose(file);
        free(buffer);
        exit(1);
    }
    
    
    return buffer;
    
    //close the file now we are finished with it
    fclose(file);
}


GLuint InitShader()
{
    GLchar *vShaderStr = ReadFile("vertex.shader");
    GLchar *fShaderStr = ReadFile("fragment.shader");
    
    GLint linked;
    GLuint vShader;
    GLuint fShader;
    GLint vCompiled;
    GLint fCompiled;
    GLuint programObject;
    
    //Create shader objects
    vShader = glCreateShader(GL_VERTEX_SHADER);
    if(vShader == 0) {
        printf("Unable to create vertex shadern");
        return;
    }
    
    
    fShader = glCreateShader(GL_FRAGMENT_SHADER);
    if(fShader == 0) {
        printf("Unable to create fragment shadern");
        return;
    }
    
    
    //load vertex shader source
    glShaderSource(vShader, 1, &vShaderStr, NULL);
    
    //Compile vertex shader
    glCompileShader(vShader);
    
    // Check the compile status
    glGetShaderiv(vShader, GL_COMPILE_STATUS, &vCompiled);
    if(!vCompiled)
    {
        printf("Unable to compile vertex shader!n");
        return;
    }
    
    
    //load fragment shader source
    glShaderSource(fShader, 1, &fShaderStr, NULL);
    
    //compile fragment shader
    glCompileShader(fShader);
    
    // Check the compile status
    glGetShaderiv(fShader, GL_COMPILE_STATUS, &fCompiled);
    if(!fCompiled)
    {
        printf("Unable to compile fragment shader!n");
        return;
    }
    
    
    // Create the program object
    programObject = glCreateProgram();
    
    if(programObject == 0) {
        printf("Unable to create Program Objectn");
        return;
    }
    
    
    //Attach shaders to the program object
    glAttachShader(programObject, vShader);
    glAttachShader(programObject, fShader);
    
    // Link the program
    glLinkProgram(programObject);
    
    // Check the link status and print the errors
    glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
    if(!linked) {
        GLint infoLen = 0;
        glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);
        
        if(infoLen > 1)
        {
            char* infoLog = malloc(sizeof(char) * infoLen);
            glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
            printf("Error linking program:n%sn", infoLog);
            
            free(infoLog);
        }
        
        glDeleteProgram(programObject);
        return;
    }
    
    return programObject;
}


int main(int argc, char *argv[])
{
    //Setup the display
    Init();
    
    //make and compile the shaders
    GLuint program = InitShader();
    
    //use the compiled shaders
    glUseProgram(program);
    
    //Triangle data 3*float and pixel color 4*float x,y,z, r,g,b,a
    GLfloat vTriangle[] = {
        -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0, 1.0f,
        0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
        0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
    }
    ;
    
    //showing off now by using the index of vTriangle to build my
    //primitives instead of declaring vertices mutiple times
    GLushort triIndices [] = {
        0, 1, 2
    }
    ;
    
    //Buffer to store the data in sexy GPU memory
    GLuint triBuffer;
    glGenBuffers(1, &triBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, triBuffer);
    
    //Buffer to store indices
    GLuint triIndicesBuffer;
    glGenBuffers(1, &triIndicesBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triIndicesBuffer);
    
    //set Buffer Data and copy into GPU Memory
    glBufferData(GL_ARRAY_BUFFER, sizeof(vTriangle), vTriangle, GL_STATIC_DRAW);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(triIndices), triIndices, GL_STATIC_DRAW);
    
    
    //create the link between the shader attributes and variables in here!
    GLint vPosition = glGetAttribLocation(program, "vPosition");
    GLint vColor = glGetAttribLocation(program, "vColor");
    GLint rotX = glGetUniformLocation(program, "rotX");
    GLint rotY = glGetUniformLocation(program, "rotY");
    GLint rotZ = glGetUniformLocation(program, "rotZ");
    GLint scale = glGetUniformLocation(program, "scale");
    
    //lets create some matrx'is to store our changing information
    mat4 rotXmat;
    mat4 rotYmat;
    mat4 rotZmat;
    mat4 scaleMat;
    GLfloat xrot = 0.0f;
    GLfloat yrot = 0.0f;
    GLfloat zrot = 10.0f;
    GLfloat scaleX = 0.8f; //on my display the height is 80% of the width
    GLfloat scaleY = 1.0f;
    GLfloat scaleZ = 1.0f;
    
    //define vertex attribute data arrays
    glVertexAttribPointer(vPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*7, 0);
    glVertexAttribPointer(vColor, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*7, (GLvoid*)(sizeof(GLfloat)*3));
    
    //enable the attribute arrays
    glEnableVertexAttribArray(vPosition);
    glEnableVertexAttribArray(vColor);
    
    //set stuff
    //glEnable(GL_DEPTH_TEST);
    //glEnable(GL_CULL_FACE);
    glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
    
    //60 frames per second at 600 = 10 seconds
    //Assuming vsync 60hz here
    int count = 0;
    while (count < 600) {
        
        //clear the buffer
        glClear(GL_COLOR_BUFFER_BIT);
        
        //perform rotation
        matrixRotateZ(zrot, rotZmat);
        
        //so it would be nice to scale everything given the ratio of the display otherwise it will stretch
        matrixScale(scaleX, scaleY, scaleZ, scaleMat);
        
        //well we want it to spin so best change the angle
        zrot += 1;
        
        //well we have done the rotation so now we need to give it to the shader drrr
        glUniformMatrix4fv(rotZ, 1, GL_FALSE, rotZmat);
        glUniformMatrix4fv(scale, 1, GL_FALSE, scaleMat);
        
        //Draw the triangles from looking at the indices and refering to the vertex array data
        //the last param is special being 0 because we are using buffers, I do not understand why
        //we do not just pass in a pointer but hey ho it works
        glDrawElements(GL_TRIANGLES, sizeof(triIndices) / sizeof(triIndices[0]), GL_UNSIGNED_SHORT, 0);
        
        //actually draw the stuff to the screen
        eglSwapBuffers(egldisplay, eglsurface);
        
        //Increment timer
        count++;
    }
    
    Deinit();
    return 0;
}
glMath.h

Code: Select all

#include <math.h>

// Pre-calculated value of PI / 180.
#define kPI180     0.017453

// Pre-calculated value of 180 / PI.
#define k180PI    57.295780

// Converts degrees to radians.
#define degreesToRadians(x) (x * kPI180)

// Converts radians to degrees.
#define radiansToDegrees(x) (x * k180PI)

/*	=======================================================
	For example, to translate, rotate and scale an object 
	you must create each matrix separately and then perform
	the multiplication ((Scale * Rotation) * Translation) 
	to get the final transformation matrix.
	=======================================================
*/

typedef float mat4[16];

void matrixIdentity(mat4 m)  
{
    m[0] = m[5] = m[10] = m[15] = 1.0;
    m[1] = m[2] = m[3] = m[4] = 0.0;
    m[6] = m[7] = m[8] = m[9] = 0.0;
    m[11] = m[12] = m[13] = m[14] = 0.0;
}

void matrixTranslate(float x, float y, float z, mat4 matrix)  
{
    matrixIdentity(matrix);

    // Translate slots.
    matrix[12] = x;
    matrix[13] = y;
    matrix[14] = z;   
}

void matrixScale(float sx, float sy, float sz, mat4 matrix)  
{
    matrixIdentity(matrix);

    // Scale slots.
    matrix[0] = sx;
    matrix[5] = sy;
    matrix[10] = sz;
}

void matrixRotateX(float degrees, mat4 matrix)  
{
    float radians = degreesToRadians(degrees);

    matrixIdentity(matrix);

    // Rotate X formula.
    matrix[5] = cosf(radians);
    matrix[6] = -sinf(radians);
    matrix[9] = -matrix[6];
    matrix[10] = matrix[5];
}

void matrixRotateY(float degrees, mat4 matrix)  
{
    float radians = degreesToRadians(degrees);

    matrixIdentity(matrix);

    // Rotate Y formula.
    matrix[0] = cosf(radians);
    matrix[2] = sinf(radians);
    matrix[8] = -matrix[2];
    matrix[10] = matrix[0];
}

void matrixRotateZ(float degrees, mat4 matrix)  
{
    float radians = degreesToRadians(degrees);

    matrixIdentity(matrix);

    // Rotate Z formula.
    matrix[0] = cosf(radians);
    matrix[1] = sinf(radians);
    matrix[4] = -matrix[1];
    matrix[5] = matrix[0];
}

void matrixMultiply(mat4 m1, mat4 m2, mat4 result)  
{
    // Fisrt Column
    result[0] = m1[0]*m2[0] + m1[4]*m2[1] + m1[8]*m2[2] + m1[12]*m2[3];
    result[1] = m1[1]*m2[0] + m1[5]*m2[1] + m1[9]*m2[2] + m1[13]*m2[3];
    result[2] = m1[2]*m2[0] + m1[6]*m2[1] + m1[10]*m2[2] + m1[14]*m2[3];
    result[3] = m1[3]*m2[0] + m1[7]*m2[1] + m1[11]*m2[2] + m1[15]*m2[3];

    // Second Column
    result[4] = m1[0]*m2[4] + m1[4]*m2[5] + m1[8]*m2[6] + m1[12]*m2[7];
    result[5] = m1[1]*m2[4] + m1[5]*m2[5] + m1[9]*m2[6] + m1[13]*m2[7];
    result[6] = m1[2]*m2[4] + m1[6]*m2[5] + m1[10]*m2[6] + m1[14]*m2[7];
    result[7] = m1[3]*m2[4] + m1[7]*m2[5] + m1[11]*m2[6] + m1[15]*m2[7];

    // Third Column
    result[8] = m1[0]*m2[8] + m1[4]*m2[9] + m1[8]*m2[10] + m1[12]*m2[11];
    result[9] = m1[1]*m2[8] + m1[5]*m2[9] + m1[9]*m2[10] + m1[13]*m2[11];
    result[10] = m1[2]*m2[8] + m1[6]*m2[9] + m1[10]*m2[10] + m1[14]*m2[11];
    result[11] = m1[3]*m2[8] + m1[7]*m2[9] + m1[11]*m2[10] + m1[15]*m2[11];

    // Fourth Column
    result[12] = m1[0]*m2[12] + m1[4]*m2[13] + m1[8]*m2[14] + m1[12]*m2[15];
    result[13] = m1[1]*m2[12] + m1[5]*m2[13] + m1[9]*m2[14] + m1[13]*m2[15];
    result[14] = m1[2]*m2[12] + m1[6]*m2[13] + m1[10]*m2[14] + m1[14]*m2[15];
    result[15] = m1[3]*m2[12] + m1[7]*m2[13] + m1[11]*m2[14] + m1[15]*m2[15];
}
vertex.shader

Code: Select all

attribute vec4 vPosition;
attribute vec4 vColor;
uniform mat4 rotX;
uniform mat4 rotY;
uniform mat4 rotZ;
uniform mat4 scale;
varying vec4 color;

void main()

{
	gl_Position = scale * rotZ * vPosition;
	color = vColor;
}
fragment.shader

Code: Select all

varying vec4 color;

void main()

{	
	gl_FragColor = color;
}
makefile

Code: Select all

IN=main.c
OUT=main.bin
FLAGS=-w

INC=-I/opt/vc/include/ -I/opt/vc/include/EGL -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/GLES2/
LIBS=-L/opt/vc/lib/ -L./ -lbcm_host -lEGL -lGLESv2 -lvcos -lvchiq_arm -lpthread -lrt -lm
COMP=gcc

all:
	
	$(COMP) $(FLAGS) $(IN) -o $(OUT) $(INC) $(LIBS)
Last edited by pizzaboy150 on Sat Sep 19, 2015 9:16 pm, edited 1 time in total.

User avatar
PeterO
Posts: 5156
Joined: Sun Jul 22, 2012 4:14 pm

Re: An example project for beginner's of OpenGL ES 2

Sat Sep 19, 2015 7:57 pm

Can you make a tar ball of your code to make it easier to try ?
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson


User avatar
Paeryn
Posts: 2749
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: An example project for beginner's of OpenGL ES 2

Sat Sep 19, 2015 10:28 pm

pizzaboy150 wrote:

Code: Select all

        //Draw the triangles from looking at the indices and refering to the vertex array data
        //the last param is special being 0 because we are using buffers, I do not understand why
        //we do not just pass in a pointer but hey ho it works
        glDrawElements(GL_TRIANGLES, sizeof(triIndices) / sizeof(triIndices[0]), GL_UNSIGNED_SHORT, 0);
The reason you pass 0 as the last parameter to glDrawElements when using buffers is that the pointer to the data is set according to the buffer that was bound with glBindBuffer. When you used glBufferData earlier gl allocated its own memory and copied your vertex data into it. The glBindBuffer lets gl know which buffer you want to use and so that last parameter to glDrawElements isn't needed. It's there for compatibility with the older method before there were buffers so you have just the one function call that works whether you use buffers or not. If you weren't using buffers then you would have to pass the pointer to the vertex data as the last parameter to glDrawElements.
She who travels light — forgot something.

User avatar
PeterO
Posts: 5156
Joined: Sun Jul 22, 2012 4:14 pm

Re: An example project for beginner's of OpenGL ES 2

Sun Sep 20, 2015 6:48 am

pizzaboy150 wrote:I have attached a tar file to my gmail account

https://drive.google.com/file/d/0B78wUP ... sp=sharing
Thanks.
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

optimist
Posts: 1
Joined: Fri Nov 06, 2015 7:25 pm

Re: An example project for beginner's of OpenGL ES 2

Fri Nov 06, 2015 8:25 pm

Hello Pizzaboy!

Thanks a lot for your work!

Early this week I got my brand new Rasberry Pi with the nice new touch screen and now would like to port a some OpenGL code I have running in Vizual Studio Express to the RasPi. I'd like to use Geany, but come into issues as the program does not find the relevant header file as egl.h ; gl2.h and gl2ext.h which Compiler do you use or better - do you know how I can tell Geany how to find the header files in /opt/vc/include/..

Thank you for a hint!

Hansjoerg

Return to “Graphics programming”