OpenGL 3 texturing

Since gl_TexCoord is deprecated and every example I can find uses it, I’m having trouble texturing my quad.

With my C++ code, I am trying to load a texture, bind the texture, send the vertex data, send the texture coordinate data, and finally draw the quad.


    //Load texture
    Texture img;
    if( !img.load_image( "testing.png" ) )
    {
        return 2;
    }

    //Bind
    glBindTexture( GL_TEXTURE_2D, img.get_texture() );

    //Vertex coordinates
    GLfloat vCoords[] = { 0.0f,   0.0f,
                       100.0f,   0.0f,
                       100.0f, 100.0f,
                         0.0f, 100.0f };

    GLuint vBuf = NULL;
    glGenBuffers( 1, &vBuf );
    glBindBuffer( GL_ARRAY_BUFFER, vBuf );
    glBufferData( GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), vCoords, GL_STATIC_DRAW );

    GLint verts = glGetAttribLocation( LEngine::get_shader(), "lVertex" );
    glEnableVertexAttribArray( verts );
    glVertexAttribPointer( verts, 2, GL_FLOAT, GL_FALSE, 0, NULL );

    GLfloat tCoords[] = { 0.0f, 0.0f,
                         1.0f, 0.0f,
                         1.0f, 1.0f,
                         0.0f, 1.0f };

    //Texture coordinates
    GLuint tBuf = NULL;
    glGenBuffers( 1, &tBuf );
    glBindBuffer( GL_ARRAY_BUFFER, tBuf );
    glBufferData( GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), tCoords, GL_STATIC_DRAW );

    GLint texs = glGetAttribLocation( LEngine::get_shader(), "lTexCoord" );
    glEnableVertexAttribArray( texs );
    glVertexAttribPointer( texs, 2, GL_FLOAT, GL_FALSE, 0, NULL );

    //Render
    GLuint iData[] = { 0, 1, 2, 3 };
    glDrawElements( GL_QUADS, 4, GL_UNSIGNED_INT, iData );

Now here’s my incomplete vertex shader. My first question is how do I handle the texture coordinate data so my fragment shader can use it on the texture?


uniform mat4 LProj;
uniform mat4 LMView;

in vec2 lVertex;
in vec2 lTexCoord;

void main()
{
	gl_Position = LProj * LMView * vec4(lVertex.x, lVertex.y, 0.0, 1.0);
}

Here’s my incomplete fragment shader. My questions here are 1) how do I get the texture coordinates from the vertex shader? 2) How to I use the texture that I called glBindTexture on?


uniform sampler2D lTexture;
out vec4 lFrag;

void main()
{
	// Setting Each Pixel To blue
	lFrag = vec4(0.0, 0.0, 1.0, 1.0);
}

Now is there anything else I’m forgetting about rendering a textured quad?

in the vertex shader you can do this:

in vec2 lTexCoord;
out vec2 otexcoord;

void main()
{

otexcoord = lTexCoord;
}

Then in frag shader:


in vec2 otexcoord;

void main()
{
vec4 texResult = texture(lTexture, otexcoord);
lFrag = texResult;
}

This is how you handle passing texcoords and pretty much any incoming data/attribute in the buffer.

When you bind a texture, it’s to a specific texture unit (specified by glActiveTexture before you bind). You stuff this texture unit number onto the uniform via glUniform1i to point it to that texture unit:

Here is a GL3 code to texture a single triangle. The “Frac.png” is not attached here but it is a 512x512 png file in my case – you could use another size if you like singe DevIL determines the size at runtime. The C-code is using GLM and DevIL libraries. You may want to just read the code rather than take the time to compile it if you don’t have those libraries handy. Hopefully this will help to see complete code for a working full example.

“image.vert”


#version 150

uniform mat4 projmat;
uniform mat4 modelmat;
in vec2 vert;
in vec2 texcoord;
out vec2 coord;

void main() {
	coord=texcoord;
	gl_Position=projmat*modelmat*vec4(vert,0.,1.);
}

“image.frag”



#version 150 core

uniform sampler2D tex;
in vec2 coord;
out vec4 color;

void main() {
	color=texture(tex,coord);
}


“main.cpp”


#define GL3_PROTOTYPES
#include <GL3/gl3.h>
#include <SDL.h>
#include <iostream>
#include <fstream>
#include <stack>

//OpenGL Mathematics (GLM).  A C++ mathematics library for 3D graphics.
#include <glm/glm.hpp>
#include <glm/GLM_VIRTREV_address.h> // gives helper function address(X) instead of using &X[0][0]
#include <glm/GLM_GTX_transform.h>
#include <glm/GLM_GTX_matrix_projection.h>
#include <glm/GLM_GTX_transform2.h> // for lookAt
using namespace glm;

#include <IL/ilut.h> //http://openil.sourceforge.net/tuts/tut_step/index.htm

struct TextureHandle {
 ILubyte *p;  /* pointer to image data loaded into memory */
 ILuint id;   /* unique DevIL id of image */
 ILint w;     /* image width */
 ILint h;     /* image height */
 ILenum DestFormat;             /* see ilConvertImage */
 ILenum DestType;               /* see ilConvertImage */
};
TextureHandle frac;

//---+----3----+----2----+----1----+---><---+----1----+----2----+----3----+----4
//----------------------------   LoadImageDevIL   ------------------------------

// use devIL, cross platform image loading library(openil.sourceforge.net/about.php)

ILuint LoadImageDevIL (char *szFileName, struct TextureHandle *T)
{
    //When IL_ORIGIN_SET enabled, the origin is specified at an absolute 
    //position, and all images loaded or saved adhere to this set origin.
    ilEnable(IL_ORIGIN_SET);
    //sets the origin to be IL_ORIGIN_LOWER_LEFT when loading all images, so 
    //that any image with a different origin will be flipped to have the set 
    //origin.
    ilOriginFunc(IL_ORIGIN_LOWER_LEFT);

    //Now load the image file
    ILuint ImageNameID;
    ilGenImages(1, &ImageNameID);
    ilBindImage(ImageNameID);
    if (!ilLoadImage(szFileName)) return 0; // failure 
    ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE); // set destination image properties

    T->id = ImageNameID;
    T->p = ilGetData(); 
    T->w = ilGetInteger(IL_IMAGE_WIDTH);
    T->h = ilGetInteger(IL_IMAGE_HEIGHT);
    T->DestFormat = ilGetInteger(IL_IMAGE_FORMAT);
    T->DestType = ilGetInteger(IL_IMAGE_TYPE);
    
    printf("%s %d %d %d
",szFileName,T->id,T->w,T->h);
    return 1; // success
}


//---+----3----+----2----+----1----+---<>---+----1----+----2----+----3----+----4
//---------------------------   cleanup_images   -------------------------------

//  Clear out the memory used by loading image files.

void cleanup_images(void) {
  printf("cleanup image memory:{");

  if (frac.id) {
    printf(" %d",frac.id); 
    ilDeleteImages(1, &frac.id);
  } 

  printf(" }
");
}


void glError(const char *file, int line) {
  GLenum err(glGetError());

  while(err!=GL_NO_ERROR) {
    std::string error;

    switch(err) {
      case GL_INVALID_OPERATION:  error="INVALID_OPERATION";  break;
      case GL_INVALID_ENUM:    error="INVALID_ENUM";    break;
      case GL_INVALID_VALUE:    error="INVALID_VALUE";    break;
      case GL_OUT_OF_MEMORY:    error="OUT_OF_MEMORY";    break;
    }

    std::cerr<<"GL_"<<error<<" - "<<file<<':'<<line<<std::endl;
    err=glGetError();
  }
}

int main() {
  //SDL Initialization
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3);
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,2);
  SDL_Init(SDL_INIT_VIDEO);

  const int r_width(640),r_height(480);
  SDL_WindowID window(SDL_CreateWindow("Foo",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,r_width,r_height,SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN));
  SDL_GLContext glcontext(SDL_GL_CreateContext(window));

  std::cout<<"OpenGL "<<glGetString(GL_VERSION)<<" (GLSL "<<glGetString(GL_SHADING_LANGUAGE_VERSION)<<')'<<std::endl;

  // Read in textures from disk (bind to openGL next though ...)
  ilInit();
  assert( LoadImageDevIL ((char*)"Frac.png", &frac) );
  atexit(cleanup_images);

  //Bind texture to opengl
  GLuint textureid;

  glGenTextures(1,&textureid);
  glBindTexture(GL_TEXTURE_2D,textureid);

  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
  glTexImage2D (GL_TEXTURE_2D, 0, frac.DestFormat, frac.w, frac.h, 0, frac.DestFormat, frac.DestType, frac.p);
  glGenerateMipmap(GL_TEXTURE_2D); //Generate mipmaps now!!!

  //Create shaders and shader program
  GLuint vshader(glCreateShader(GL_VERTEX_SHADER));
  GLuint fshader(glCreateShader(GL_FRAGMENT_SHADER));
  GLuint program(glCreateProgram());

  {
    std::ifstream source_file("image.vert");

    std::string data;
    std::getline(source_file,data,'\0');

    const GLchar *vshader_source(data.c_str());

    glShaderSource(vshader,1,&vshader_source,NULL);
  }

  {
    std::ifstream source_file("image.frag");

    std::string data;
    std::getline(source_file,data,'\0');

    const GLchar *fshader_source(data.c_str());

    glShaderSource(fshader,1,&fshader_source,NULL);
  }

  glCompileShader(vshader);
  glCompileShader(fshader);

  glAttachShader(program,vshader);
  glAttachShader(program,fshader);
  glLinkProgram(program);

  //Get handles to shader uniforms
  GLint matrixloc(glGetUniformLocation(program,"projmat")); //uniform mat4 projmat;
  GLint matrix2loc(glGetUniformLocation(program,"modelmat")); //uniform mat4 modelmat;
  GLint texloc(glGetUniformLocation(program,"tex")); //uniform sampler2D tex;

  //Create geometry vertex array
  GLuint tris,coords,vao;
  const GLfloat vertices[3*2]= {0.0f,0.0f,  0.0f,1.0f,   1.0f,1.0f};
  const GLfloat texcoords[3*2]={0.0f,0.0f,  0.0f,1.0f,   1.0f,1.0f};

  glGenVertexArrays(1,&vao);
  glBindVertexArray(vao);

  //in vec2 vert;
  glGenBuffers(1,&tris);
  glBindBuffer(GL_ARRAY_BUFFER,tris);
  glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);
  const GLint location(glGetAttribLocation(program,"vert"));
  glVertexAttribPointer(location,2,GL_FLOAT,GL_TRUE,0,NULL);
  glEnableVertexAttribArray(location);

  //in vec2 texcoord;
  glGenBuffers(1,&coords);
  glBindBuffer(GL_ARRAY_BUFFER,coords);
  glBufferData(GL_ARRAY_BUFFER,sizeof(texcoords),texcoords,GL_STATIC_DRAW);
  const GLint coordloc(glGetAttribLocation(program,"texcoord"));
  glVertexAttribPointer(coordloc,2,GL_FLOAT,GL_TRUE,0,NULL);
  glEnableVertexAttribArray(coordloc);

  //Render
  glm::mat4 projmat;
  glm::mat4 modelmat;

  projmat=glm::mat4(1.0);  //loadIdentity
  projmat*=glm::perspective(45.0f, float(r_width/r_height), 0.1f, 100.0f);

  modelmat=glm::mat4(1.0);  //loadIdentity
  modelmat*=glm::lookAt( glm::vec3(0.5f,0.5f,0.5f), 
                         glm::vec3(0.5f,0.5f,0.0f), 
                         glm::vec3(0.0f,1.0f,0.0f));
  modelmat*=glm::translate(glm::vec3(0.0,0.0,-1.0f));

  glUseProgram(program);

  glUniformMatrix4fv(matrixloc,1,GL_FALSE,&projmat[0][0]);
  glUniformMatrix4fv(matrix2loc,1,GL_FALSE,&modelmat[0][0]);

  glUniform1i(texloc,0); // 0 for GL_TEXTURE0
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D,textureid);

  glBindVertexArray(vao);
  glDrawArrays(GL_TRIANGLES,0,3);

  glError(__FILE__,__LINE__);
  SDL_GL_SwapWindow(window);
  SDL_Delay(6000);

  //Quit
  glDeleteShader(vshader);
  glDeleteShader(fshader);
  glDeleteProgram(program);
  glDeleteTextures(1,&textureid);
  SDL_GL_DeleteContext(glcontext);
  SDL_DestroyWindow(window);
  SDL_Quit();
}

That worked great.

Also I noticed you’re using SDL. Is SDL 1.3 stable enough to use?

Glad that example helped. As for SDL 1.3 I have not tried it extensively so I can not comment on its stability. Now that you mention it though I should give it a look when I get a chance …

I don’t think SDL1.3 API is very stable yet, just 2 months or so ago they changed the API for handling multimouse, see SDL Forum: multi-mouse/keyboard support in 1.3

I think as of right now, the current SDL1.3 does not have the multimouse support… atleast browsing the header filers leads me to believe so.

However, since the last change, SDL 1.2 and SDL 1.3 are mostly source compatible (the biggest issue is that they change the function prototype definitions for SDL_rwops). There are a few others… though you will also need to build your own SDL1.3 utility libraries yourself (SDL_mixer, SDL_image, SDL_net and [maybe]SDL_Sound are likely the ones you will use… some use SDL_ttf, though I do not care for it).