I'm new to openGL and I've been googling around for the old and simple textured quad tutorial in openGL but I haven't been able to find one that suits my needs.

  • OpenGL3.X compatible
  • OpenGL|ES 2.0 compatible AND
  • Only uses core openGL library (no GLUT, GLEW...)

I'm creating the window and the openGL context with SDL2.

I only need the most basic stuff to draw quads. I'm not interested in any of the new OpenGL features, just in compatibility and cross platformness (Windows, OSX, Linux, Android and iOS).

  • 1
    \$\begingroup\$ Yeah, you are not going to get OpenGL 3 without a function exposing library like GLEW. Unless ofcourse if you are a hardcore masochist. See this answer on how to do it. \$\endgroup\$
    – Eejin
    Jun 14, 2015 at 22:26
  • \$\begingroup\$ Well, my intention is to use just the most basic stuff to draw textures while being cross platform compatible avoiding extra libraries. I'm not interested in any new features at all. That's why I'm asking. \$\endgroup\$ Jun 14, 2015 at 22:52
  • 1
    \$\begingroup\$ If you can't find one, you should write one up after you figure it out! \$\endgroup\$
    – Alan Wolfe
    Jun 15, 2015 at 0:53
  • \$\begingroup\$ I'll write one up. You say 'textured', so you're going to pull in dependencies with image loading though. \$\endgroup\$ Jun 15, 2015 at 7:06
  • \$\begingroup\$ The textures will always be characters generated from FTGL if it is fully cross platform. I will never load textures from disk. \$\endgroup\$ Jun 15, 2015 at 8:37

1 Answer 1


Seeing as though you said you need to support OSX I'm going to presume you request a 3.3 core context or better.

You said you don't want to pull in any extra dependencies and to keep it cross platform, so I will try to adhere to this as much as possible.

I am using stb_image.h from the Nothing itself nothings.

I'm going to define a basic class for a texture:

#include "stb_image.h"

class texture{
        texture(const char *filepath)
            : image(stbi_load(filepath, &width, &height, &components, STBI_rgba)){
            // check image != nullptr

            glGenTextures(1, &handle);

            GLint curr_tex; // for saving original state
            glGetIntegerv(GL_ACTIVE_TEXTURE, &curr_tex);

            // I think this is good practice incase somebody else uses your code

            glActiveTexture(GL_TEXTURE3); // or some other texture unit
            // I use 3 because reasons

            glBindTexture(GL_TEXTURE_2D, handle);

            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

            GLint gl_internal_format = ((components == 3) ? GL_RGB8 : GL_RGBA8);
            GLenum gl_format = ((components == 3) ? GL_RGB : GL_RGBA);

                GL_TEXTURE_2D, 0,
                width, height, 0, 
                GL_UNSIGNED_BYTE, image

            glActiveTexture(curr_tex); // restore original state

            glDeleteTextures(1, &handle);

        int width, height, components;
        unsigned char *image;
        GLuint handle;

You could expand on this class, adding conversions and such.

Next I'm going to add a basic quad class:

class quad{
            GLfloat vertices[] = { // format = x, y, z, u, v
                -1.0f, -1.0f, 0.0f,  0.0f, 0.0f,     1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
                 1.0f,  1.0f, 0.0f,  1.0f, 1.0f,    -1.0f,  1.0f, 0.0f, 0.0f, 1.0f

            glGenVertexArrays(1, &vao); // vao saves state of array buffer, element array, etc
            glGenBuffers(1, &vbo); // vbo stores vertex data

            GLint curr_vao; // original state
            glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &curr_vao);

            glBindBuffer(GL_ARRAY_BUFFER, vbo);

            glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float)*5, nullptr);
            glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float)*5, (void*)(sizeof(float) * 3));



            glDeleteVertexArrays(1, &vao);
            glDeleteBuffers(1, &vbo);

        GLuint vao, vbo;

Now the most important part: the shaders.


#version 330 core

layout(location = 0) in vec3 vertex;
layout(location = 0) in vec2 uv;

// will be used in fragment shader
out vec2 uv_frag;

void main(){
    uv_frag = uv;
    gl_Position = vec4(vertex, 1.0);


#version 330 core

// has to have same name as vertex shader
in vec2 uv_frag;

// our texture
uniform sampler2D tex;

// actual output
// gl_FragColor is deprecated
out vec4 frag_color;

void main(){
    frag_color = texture(tex, uv_frag);

Lets make a class for loading these in

class shader_program{
        shader_program(const char *vert_path, const char *frag_path)
            : program(glCreateProgram()), vert(glCreateShader(GL_VERTEX_SHADER)), frag(glCreateShader(GL_FRAGMENT_SHADER)){

            std::ifstream vert_file(vert_path), frag_file(frag_path);
            // check both opened fine

            auto lvalue = [](auto &&arg) -> decltype(arg)& {return arg;}

            std::string temp, vert_source, frag_source;
            while(getline(vert_file, temp)) vert_source += temp + '\n';
            while(getline(frag_file, temp)) frag_source += temp + '\n';

            glShaderSource(vert, 1, &lvalue(vert_source.c_str()), nullptr);
            glShaderSource(frag, 1, &lvalue(frag.source.c_str()), nullptr);

            // check shaders compiled. google glGetShaderInfoLog

            glAttachShader(program, vert);
            glAttachShader(program, frag);
            // check program linked correctly. google glGetProgramInfoLog


        GLuint program, vert, frag;

Then we throw it all together:

// set function pointers to no-op functions
// always initialize global function pointers
void(*glGenVertexArrays)(GLsizei n, GLuint *arrays) = [](){};
void(*glGenBuffers)(GLsizei n, GLuint *buffers) = [](){};
void(*glDeleteVertexArrays)(GLsizei n, GLuint *arrays) = [](){};
void(*glDeleteBuffers)(GLsizei n, GLuint *buffers) = [](){};
// more than probably a few more pointers

int main(int argc, char *argv[]){
    // create window and context        

    glGenVertexArrays = reinterpret_cast<decltype(glGenVertexArrays)>(SDL_GL_GetProcAddress("glGenVertexArrays"));
    // do this for every function pointer

    texture t("mytexture.png");
    quad q;

    shader_program program("vertex.glsl", "fragment.glsl");

    glBindTexture(GL_TEXTURE_2D, t.handle);

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    bool running = true;
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
        // we laid our quad out in a fan


    glBindTexture(GL_TEXTURE_2D, 0);

    // destroy window and context
    // make sure they are destroyed after the texture, quad and shaders

I really recommend you don't go through getting all those function pointers yourself though. I can't recommend glLoadGen enough; or something similar (glew).

I Gotta warn you now, I haven't tested this code because I'm not at home, but I've written code similar to that before and it's worked great. If any issues arise just leave a comment and I'll fix anything.


Not the answer you're looking for? Browse other questions tagged .