SDL2 + OpenGL ES 2.0 using a shader

I currently need to develop a program that will display a simple texture (which is generated by the program by existing SDL2 code) using a shader. For now to see how it works to combine OpenGL ES 2.0 and SDL (on a raspberry pi framebuffer) I created this sample program (sorry, its missing some error checking and serves as an example). It should for now only display a texture that is loaded from the file “land.png” using SDL2_image.

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>

#define GL_GLEXT_PROTOTYPES 1
#include <SDL2/SDL_opengles2.h>

#include <iostream>

static const char * vertex = 
"attribute vec4 position;"
"attribute vec4 inputTextureCoordinate;"
""
"varying vec2 textureCoordinate;"
""
"void main()"
"{"
"    gl_Position = position;"
"    textureCoordinate = inputTextureCoordinate.xy;"
"}"
"";

static const char * fragment =
"varying highp vec2 textureCoordinate;"
""
"uniform sampler2D videoFrame;"
""
"void main()"
"{"
"    gl_FragColor = texture2D(videoFrame, textureCoordinate);"
"}"
"";

unsigned int program_id = 0;

GLuint TextureID  = 0;

bool compile_program()
{
    unsigned int vertex_id = 0;
    unsigned int fragment_id = 0;
    char infoLog[512];
    int success;
    vertex_id = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex_id, 1, &vertex, NULL);
    glCompileShader(vertex_id);
    glGetShaderiv(vertex_id, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(vertex_id, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
        glDeleteShader(vertex_id);
        vertex_id = 0;
        return false;
    }
    std::cout << "Vertex shader compiled successfully! Id = " << vertex_id << std::endl;

    fragment_id = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment_id, 1, &fragment, NULL);
    glCompileShader(fragment_id);
    glGetShaderiv(fragment_id, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(fragment_id, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
        glDeleteShader(vertex_id);
        glDeleteShader(fragment_id);
        vertex_id = 0;
        return false;
    }
    std::cout << "Fragment shader compiled successfully! Id = " << fragment_id << std::endl;


    program_id = glCreateProgram();
    glAttachShader(program_id, vertex_id);
    glAttachShader(program_id, fragment_id);
    glLinkProgram(program_id);

    glGetProgramiv(program_id, GL_LINK_STATUS, &success);
    if(!success)
    {
        glGetProgramInfoLog(program_id, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
        glDeleteShader(vertex_id);
        glDeleteShader(fragment_id);
        return false;
    }

    TextureID = glGetUniformLocation(program_id, "videoFrame");

    glDeleteShader(vertex_id);
    glDeleteShader(fragment_id);

    return true;
}

int main()
{
    if (SDL_Init(SDL_INIT_EVERYTHING | SDL_VIDEO_OPENGL_ES2) != 0){
        std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl;
        return 1;
    }

    SDL_Window *win = SDL_CreateWindow("AnyGlobe", SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED, 480, 480, 0);

    SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2");

    SDL_Renderer *renderer = SDL_CreateRenderer(win, -1,
        SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);

    SDL_RendererInfo rendererInfo;
    SDL_GetRendererInfo(renderer, &rendererInfo);

    std::cout << "Renderer: " << rendererInfo.name << std::endl;
    if(!strncmp(rendererInfo.name, "opengles2", 9)) {
        std::cout << "We have OpenGL ES 2.0" << std::endl;
        compile_program();
    }

    SDL_Texture *land = IMG_LoadTexture(renderer, "land.png");
    if (!land)
    {
        abort();
    }

    int done = 0;
    float position = 0.0;

    SDL_Texture *texTarget = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
        SDL_TEXTUREACCESS_TARGET, 480, 480);

   static const GLfloat squareVertices[] = {
        -1.0f, -1.0f,
        1.0f, -1.0f,
        -1.0f,  1.0f,
        1.0f,  1.0f,
    };

    static const GLfloat textureVertices[] = {
        1.0f, 1.0f,
        1.0f, 0.0f,
        0.0f,  1.0f,
        0.0f,  0.0f,
    };

    while ( ! done ) {
        //Render to the texture
        SDL_SetRenderTarget(renderer, texTarget);
        SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
        SDL_RenderClear(renderer);


        SDL_RenderCopy(renderer, land, NULL, NULL);

        SDL_SetRenderTarget(renderer, NULL);
        SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);
        SDL_RenderClear(renderer);

        GLint old_program = 0;

        if(program_id != 0) {
                glGetIntegerv(GL_CURRENT_PROGRAM, &old_program);
                glUseProgram(program_id);
        }


        glActiveTexture(GL_TEXTURE0);
        SDL_GL_BindTexture(texTarget, NULL, NULL);

        glUniform1i(TextureID, 0);

        glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, squareVertices);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, textureVertices);
        glEnableVertexAttribArray(1);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        SDL_GL_SwapWindow(win);

        if(program_id != 0) {
            glUseProgram(old_program);
        }

        SDL_Event event;
        while ( SDL_PollEvent(&event) ) {
            if ( event.type == SDL_QUIT ) {
                done = 1;
            }
            if ( event.type == SDL_KEYDOWN ) {
                if ( event.key.keysym.sym == SDLK_ESCAPE ) {
                    done = 1;
                }
            }
        }
    }

    return 0;
}

The current status is that only a color background (defined by the RendererDrawColor in SDL) is displayed but not the texture and I cannot put my finger on it why it does not work properly. Can somebody look especially over the OpenGL ES code? I am thankful about every hint.

Comparing your code with a program of mine which works, the only difference I can immediately see is that I assign textureCoordinate in the fragment shader rather than in the vertex shader. That probably isn’t significant, but here’s what I’ve got for comparison:

"void main()"
"{"
"    textureCoordinate = gl_FragCoord.xy;"
"    gl_FragColor = texture2D(videoFrame, textureCoordinate);"
"}"

Is yours working on the raspberry pi? Ive noticed that my code works on a normal PC, but not on the Pi without X11. Could you provide me your program to try it out? Only if possible though.

Mine works on the Raspberry Pi, yes, but I’ve only tried it with the version of SDL2 from the Raspbian repository which is using full OpenGL (and X11) not OpenGL ES. However it runs in Android and iOS too which are OpenGL ES 2.0; the only differences in my shader code in that case are:

"#version 100"
"precision highp float;"