(3.2) Texture coords problem

Hi I’ve written this OpenGL 3.2 / GLSL 1.50 core app to display a triangle with a texture on it but it only renders in one color. Probably because a problem with texture coords? No OpenGL error code is set! So it must be logic.

#define GL3_PROTOTYPES
#include <GL3/gl3.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <glm/gtx.hpp>
#include <iostream>
#include <fstream>
#include <stack>

namespace glm {
	using namespace gtx;

	namespace gtx {
		using namespace transform2;
	}
}

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() {
	//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;

	//Load texture
	SDL_Surface *imagesurface{IMG_Load("Lenna.png")};
	GLuint textureid;

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

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,imagesurface->w,imagesurface->h,0,GL_RGB,GL_UNSIGNED_BYTE,imagesurface->pixels);

	SDL_FreeSurface(imagesurface);

	//Create shaders and shader program
	GLuint vshader{glCreateShader(GL_VERTEX_SHADER)},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);

	GLint matrixloc{glGetUniformLocation(program,"projmat")};
	GLint matrix2loc{glGetUniformLocation(program,"modelmat")};

	//Create geometry
	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);

	glGenBuffers(1,&tris);
	glBindBuffer(GL_ARRAY_BUFFER,tris);
	glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),&vertices[0],GL_STATIC_DRAW);

	const GLint location{glGetAttribLocation(program,"vert")};
	glVertexAttribPointer(location,2,GL_FLOAT,GL_FALSE,0,NULL);
	glEnableVertexAttribArray(location);

	glGenBuffers(1,&coords);
	glBindBuffer(GL_ARRAY_BUFFER,coords);
	glBufferData(GL_ARRAY_BUFFER,sizeof(texcoords),&texcoords[0],GL_STATIC_DRAW);

	const GLint coordloc{glGetAttribLocation(program,"texcoord")};
	glVertexAttribPointer(coordloc,2,GL_FLOAT,GL_FALSE,0,NULL);
	glEnableVertexAttribArray(coordloc);

	GLint texloc{glGetUniformLocation(program,"tex")};

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

	projmat*=glm::perspective(45.0f, float(r_width/r_height), 0.1f, 100.0f);
	modelmat*=glm::translate(glm::vec3{0.0f,5.0f,0.0f});

	glUseProgram(program);

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

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

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

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

	//Quit
	glDeleteShader(vshader);
	glDeleteShader(fshader);
	glDeleteProgram(program);
	glDeleteTextures(1,&textureid);
	SDL_GL_DeleteContext(glcontext);
	SDL_DestroyWindow(window);
	SDL_Quit();
}
#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,0);
}
#version 150

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

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

I don’t quite understand this but I changed the vertex shader line by changing the last 0 to 1:


gl_Position=projmat*modelmat*vec4(vert,0.,1.);

and in the c-code I had to change your perspective/translate code in the render function. I tried SDL_image but it failed with my image (frac.png since I don’t have your Leena.png file) file so I replaced it with devIL image library. Note, the image file has to be a power of 2, my frac.png file is 2^9x2^9pixels. Is your png an even power of 2 also? … It is just easier to give you the code that worked for me


#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.);
}


#version 150

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

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


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

//OpenGL Mathematics (GLM).  A C++ mathematics library for 3D graphics.
#include <glm/glm.h>
#include <glm/glmext.h>
#include <glm/GLM_VIRTREV_address.h> // gives helper function address(X) instead of using &X[0][0]
namespace glm
{
          // read glm/gtx/transform.h for function prototypes
          using GLM_GTX_transform;
          using GLM_GTX_matrix_projection;
          using GLM_GTX_transform2; // 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 */
};
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 

    T->id = ImageNameID;
    T->p = ilGetData(); 
    T->w = ilGetInteger(IL_IMAGE_WIDTH);
    T->h = ilGetInteger(IL_IMAGE_HEIGHT);
    
    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() {
  ilInit();
  assert( LoadImageDevIL ((char*)"Frac.png", &frac) );

	//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;

	//Load texture
//	SDL_Surface *imagesurface(IMG_Load("Frag.png"));
	GLuint textureid;

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

glTexImage2D (GL_TEXTURE_2D, 0, 3, frac.w, frac.h, 0, GL_RGB, GL_UNSIGNED_BYTE, frac.p);
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, 3, frac.w, frac.h, 0, GL_BGR, GL_UNSIGNED_BYTE, frac.p);
//glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,imagesurface->w,imagesurface->h,0,GL_RGB,GL_UNSIGNED_BYTE,imagesurface->pixels);
glGenerateMipmap(GL_TEXTURE_2D); //Generate mipmaps now!!!


//	SDL_FreeSurface(imagesurface);

	//Create shaders and shader program
	GLuint vshader(glCreateShader(GL_VERTEX_SHADER)),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);

	GLint matrixloc(glGetUniformLocation(program,"projmat"));
	GLint matrix2loc(glGetUniformLocation(program,"modelmat"));

	//Create geometry
	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);

	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);

	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);

	GLint texloc(glGetUniformLocation(program,"tex"));

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

	projmat=glm::mat4(1.0);

	modelmat=glm::mat4(1.0);
	modelmat*=glm::scale(glm::vec3(0.5f,0.5f,0.5f));

	glUseProgram(program);

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

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

	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();
}

There are some changes to the code I posted previously. Some better use of perspective and model matrices


//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,-2.0f));

Also you should not transpose the GLM matrices ie changed so GL_FALSE used instead of GL_TRUE as follows


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

And with my changes to DevIL I forgot to cleanup after exiting. To be complete the first line after LoadImageDevIL should set up cleanup on exit as


  assert( LoadImageDevIL ((char*)"Frac.png", &frac) );
  atexit(cleanup_images);

Also in image.frag the added “smooth” qualifier was not really necessary.

Oh so it was a projection error not OpenGL error! Thanks a bunch

In addition to the projection error. I think you also needed the line to generate a complete set of mipmaps for your texture object


  glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, frac.w, frac.h, 0, GL_RGB, GL_UNSIGNED_BYTE, frac.p);
  glGenerateMipmap(GL_TEXTURE_2D); //Generate mipmaps now!!!

Note, replaced GL_INVALID_ENUM “3” with correct GL_RGB.

No it worked fine without it, but thanks for the hint. I was looking for how to do this is OpenGL 3.2 core

That is interesting. I am using Linux/OpenGL 3.2.0 NVIDIA 190.18.04 (GLSL 1.50 NVIDIA via Cg compiler) and it would fail to show anything but a black screen if I commented out the glGenerateMipmap(GL_TEXTURE_2D); line! So I definitely needed it.

glGenerateMipmap(GL_TEXTURE_2D) is not deprecated so I think it is still ok with ogl3.2.

But if you are using SDL_image that may generate the mipmaps for you whereas DevIL does not the way I am using it in the above code. Or maybe the size of your image is different than mine (512x512) and that is causing the difference.

Okay I am using 190.18.05 there is a newer version available FYI http://developer.nvidia.com/object/opengl_3_driver.html

Thanks, I upgraded to 190.18.05.

However I realized there was no problem it worked fine before. It was a false alarm on my part – if I changed two things:
1.commented out glGenerateMipmap
2.GL_LINEAR_MIPMAP_LINEAR -> GL_LINEAR
everything worked fine.

ie from:


  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
  glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, frac.w, frac.h, 0, GL_RGB, GL_UNSIGNED_BYTE, frac.p);
  glGenerateMipmap(GL_TEXTURE_2D); //Generate mipmaps now!!!

to:


  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
  glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, frac.w, frac.h, 0, GL_RGB, GL_UNSIGNED_BYTE, frac.p);
  //glGenerateMipmap(GL_TEXTURE_2D); //Generate mipmaps now!!!

Both work.