Can't load .png image using opengl texture - opengl

#include <stdio.h>
#include <GL/gl.h>
#include <GL/glut.h>
#define KEY_ESCAPE 27
void display();
void keyboard(unsigned char,int,int);
GLuint LoadTextureRAW( const char * filename, int wrap );
int main(int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH );
glutInitWindowSize(600,400);
glutCreateWindow("Opengl Test");
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}
void display() {
GLuint texture=LoadTextureRAW("ball.png",1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,texture);
glBegin( GL_QUADS );
glTexCoord2d(0.0,0.0); glVertex2d(0.0,0.0);
glTexCoord2d(1.0,0.0); glVertex2d(1.0,0.0);
glTexCoord2d(1.0,1.0); glVertex2d(1.0,1.0);
glTexCoord2d(0.0,1.0); glVertex2d(0.0,1.0);
glEnd();
glutSwapBuffers();
}
// load a 256x256 RGB .RAW file as a texture
GLuint LoadTextureRAW( const char * filename, int wrap )
{
GLuint texture;
int width, height;
// BYTE * data;
int *data;
FILE * file;
// open texture data
file = fopen( filename, "rb" );
if ( file == NULL ) return 0;
// allocate buffer
width = 256;
height = 256;
data = (int*)malloc( width * height * 3 );
// read texture data
fread( data, width * height * 3, 1, file );
fclose( file );
// allocate a texture name
glGenTextures( 1, &texture );
// select our current texture
glBindTexture( GL_TEXTURE_2D, texture );
// select modulate to mix texture with color for shading
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
// when texture area is small, bilinear filter the closest mipmap
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_NEAREST );
// when texture area is large, bilinear filter the first mipmap
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
// if wrap is true, the texture wraps over at the edges (repeat)
// ... false, the texture ends at the edges (clamp)
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
wrap ? GL_REPEAT : GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
wrap ? GL_REPEAT : GL_CLAMP );
// build our texture mipmaps
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height,
GL_RGB, GL_UNSIGNED_BYTE, data );
// free buffer
free( data );
return texture;
}
void keyboard(unsigned char key, int mousePositionX, int mousePositionY) {
switch ( key ) {
case KEY_ESCAPE:
exit ( 0 );
break;
default:
break;
}
}
I followed this, http://www.nullterminator.net/gltexture.html
What should i do?

"LoadTextureRAW()" is not for PNG files. You will need a third party library like libpng to decode a png file, because they are compressed.
If you don't want to implement libpng yourself, which is kind of advanced, then you can probably find a wrapper library on google somewhere.
You can find a minimal implementation of libpng here:
http://zarb.org/~gc/html/libpng.html

Related

Load a bmp on GLUT to use it as a texture background [duplicate]

I've been searching all around for a simple solution to add sprites to my OpenGl GLUT simple moon lander game in c++ and it appears I must use bmp's since they're easiest to load and use them as textures on a rectangle.
How exactly can I load the bmp's as textures though?
Look my simple c implementation function to load texture.
GLuint LoadTexture( const char * filename )
{
GLuint texture;
int width, height;
unsigned char * data;
FILE * file;
file = fopen( filename, "rb" );
if ( file == NULL ) return 0;
width = 1024;
height = 512;
data = (unsigned char *)malloc( width * height * 3 );
//int size = fseek(file,);
fread( data, width * height * 3, 1, file );
fclose( file );
for(int i = 0; i < width * height ; ++i)
{
int index = i*3;
unsigned char B,R;
B = data[index];
R = data[index+2];
data[index] = R;
data[index+2] = B;
}
glGenTextures( 1, &texture );
glBindTexture( GL_TEXTURE_2D, texture );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_REPEAT );
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height,GL_RGB, GL_UNSIGNED_BYTE, data );
free( data );
return texture;
}
Above function returns the texture data. Store the texture data in variable
GLuint texture;
texture= LoadTexture( "your_image_name.bmp" );
Now you can bind the texture using glBindTexture
glBindTexture (GL_TEXTURE_2D, texture);
Checkout my the TextureLoader (TextureLoader.h + TextureLoader.cpp) from OpenGL_3_2_Utils:
https://github.com/mortennobel/OpenGL_3_2_Utils
The two files does not depend on any other files and should also work seamless on any version of OpenGL (and any platform). Example usage can be found in the file comment.
You can use library GLAUX and SOIL(Simple OpenGL Image Library) .
There are also other image libriries for OpenGL.
How to load a bmp on GLUT to use it as a texture?
Another very simple solution would be to use STB library, which can be found at GitHub - nothings/stb.
All what is needed is one source file, the header file "stb_image.h". It doesn't require to link any library file or to compile any additional source file.
Include the header file and enable image reading by the setting the preprocessor definition STB_IMAGE IMPLEMENTATION:
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
The image file can be read by the function stbi_load:
const char *filename = .....; // path and filename
int req_channels = 3; // 3 color channels of BMP-file
int width = 0, height = 0, channels = 0;
stbi_uc *image = stbi_load( filename, &width, &height, &channels, 3 );
When the image is loaded to a texture object, then GL_UNPACK_ALIGNMENT has to be set to 1.
By default GL_UNPACK_ALIGNMENT is 4, so each line of an image is assumed to be aligned to 4 bytes. The pixels of a BMP-file in common have a size of 3 bytes and are tightly packed, this would cause a misalignment.
After loading the image, the memory can be freed by stbi_image_free:
GLuint texture_obj = 0;
if ( image != nullptr )
{
glGenTextures(1, &texture_obj);
glBindTexture(GL_TEXTURE_2D, texture_obj);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // default
stbi_image_free( image );
}
GLuint LoadTexture(GLuint tex, const char * filename, int width, int height)
{
//bmp 24 bit
unsigned char * data;
unsigned char R,G,B;
FILE * file;
//open .bmp
file = fopen(filename, "rb");
if(file == NULL)return 0;
//get memory for data
data =(unsigned char *)malloc(width * height * 3);
//data skip offset
fseek(file,128,0);
//read file to data
fread(data, width * height * 3, 1, file);
//close file
fclose(file);
//transpose R,G,B values
int index;
for(int i = 0; i < width * height ; ++i)
{
index = i*3;
B = data[index]; G = data[index+1]; R = data[index+2];
data[index] = R; data[index+1] = G; data[index+2] = B;
}
//create a texture
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height,GL_RGB, GL_UNSIGNED_BYTE, data);
//texture filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
//glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
//free memory
free(data);
return 0;
}
void init(void)
{
//texture loading, bmp 24 bit
LoadTexture(1, "01.bmp", 316, 316);
LoadTexture(2, "02.bmp", 316, 316);
LoadTexture(3, "05.bmp", 316, 316);
LoadTexture(4, "03.bmp", 316, 316);
LoadTexture(5, "06.bmp", 316, 316);
LoadTexture(6, "04.bmp", 316, 316);
. . . . . . . . . . . .
Linux
gcc cube.c -o cube -lglut -lGL -lGLU
Windows
tcc cube.c -o cube.exe -LC:\tcc\lib -lopengl32 -lglu32 -lglut32 -Wl,-subsystem=windows

Fill a polygon with a image opengl

http://coliru.stacked-crooked.com/a/400b648e7442eeb3
You can read a piece of code: the idea is to fullfill a polygon quad with a texture ( the image is repeated inside the quad).
I want that the image would repeat only once: in other words to put the image as background of the polygon. Im in a 800x600 window, and i would like to put a billiards table for example 600x400 ( in a quad defined by the glvertex3i). It could be glvertex2i and implemented in 2D,as is a 2D game.
Any help would be apreciated
Also i have a better code to support any jpg file instead of loadtexture and readjpeg( i think):
GLuint MyLoadTexture(std::string const filename)
{
GLuint texname = 0;
/* this is actually tied to the OpenGL context, so this should
* actually be a map GLcontext -> std::string -> texturename */
static std::map<std::string, GLuint> loaded_textures;
if( loaded_textures.find(filename) != loaded_textures.end() ) {
texname = loaded_textures[filename];
glBindTexture(GL_TEXTURE_2D, texname);
return texname;
}
int width,height;
std::vector<uint8_t> image;
if( ReadJPEG(filename, &image, &width, &height) ) {
std::cerr
<< "error reading JPEG"
<< std::endl;
return 0;
}
glGenTextures(1, &texname);
if( !texname ) {
std::cerr
<< "error generating OpenGL texture name"
<< std::endl;
return 0;
}
glBindTexture(GL_TEXTURE_2D, texname);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
/* glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0,
GL_RGB,
GL_UNSIGNED_BYTE, buffer );
*/
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0,
GL_RGB,
GL_UNSIGNED_BYTE, &image[0]);
loaded_textures[filename] = texname;
return texname;
}
int ReadJPEG(
std::string const filename,
std::vector<uint8_t> *image,
int *width, int *height )
{
if( !image ) {
return -1;
}
FILE * const infile = fopen(filename.c_str(), "rb");
if( !infile ) {
std::cerr
<< "error opening file "
<< filename
<< " : "
<< strerror(errno)
<< std::endl;
return -2;
}
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_calc_output_dimensions(&cinfo);
jpeg_start_decompress(&cinfo);
if( width ) { *width = cinfo.output_width; }
if( height ) { *height = cinfo.output_height; }
size_t const stride = cinfo.output_width * cinfo.output_components;
image->resize(cinfo.output_height * stride);
for(size_t i = 0; i < cinfo.output_height;) {
uint8_t * const row = &(*image)[stride * i];
i += jpeg_read_scanlines(&cinfo, (unsigned char**)&row, 1);
}
jpeg_finish_decompress(&cinfo);
fclose(infile);
return 0;
}
I noticed you are using a deprecated version of OpenGL. Now as for your situation, It is not obvious to me if you are concerned with alpha values within your textures or not. Also how many bytes does each color channel have? This is important to know.
In this section of your code there are a couple things that I see that should be improved:
glGenTextures(1, &texname);
if( !texname ) {
std::cerr
<< "error generating OpenGL texture name"
<< std::endl;
return 0;
}
glBindTexture(GL_TEXTURE_2D, texname);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
/* glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0,
GL_RGB,
GL_UNSIGNED_BYTE, buffer );
*/
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0,
GL_RGB,
GL_UNSIGNED_BYTE, &image[0]);
loaded_textures[filename] = texname;
Before you call glGenTextures(1, &texname); you should call this first:
glGetError(); // This will clear errors
After your call to glGenTextures(1, &texname);instead of checking texname for errors this method is preferred:
GLenum err = glGetError();
if ( err != GL_NO_ERROR ) {
// Generate Error Here, either log message to file, console or throw an error
}
Next is your glBindTexture( GL_TEXTURE_2D, texname ); which is good. Now as for OpenGL's functions that generate and bind the textures the second parameter is an unsigned ID that OpenGL generates automatically that associates it with the filename that it is given. It is easier for a machine to read an unsigned value to check for multiple resources than it is to check and verify a string. It is also a performance increase when an application is working 100s, 1000s or even 1,000,000s of resource files.
After this you are now setting up the parameters on how OpenGL will deal with mipmaps. And this section of your code appears to be fine if this is the behavior you want for mipmapping. However the caller to this function for using a texture has no method to set the quality level of a mipmap. What I normally do here is have an enumeration that deals with the different quality levels of mimpmaps here is snippet of how I handle working with mipmaps:
Note: the enum value is not defined or declared within this function it would be a parameter that would be passed to it.
// This enum I usually have it declared or defined in a CommonStructs header.
enum FilterQuality {
FILTER_NONE = 1,
FILTER_GOOD,
FILTER_BETTER,
FILTER_BEST
}; // FilterQuality
// Now as within side of my function for generating texture files I have something like this for mipmaps:
if ( texture.generateMipMap ) {
switch ( texture.filterQuality ) {
case TextureInfo::FILTER_NONE : {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
break;
}
case TextureInfo::FILTER_GOOD: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR );
break;
}
case TextureInfo::FILTER_BEST: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
break;
}
default: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
}
} // Switch
if ( openglVersion.x < 3 ) {
// In OpenGL v3 GL_GENERATE_MIPMAP Is Deprecated, And In 3.1+ It Was Removed
// So For Those Versions We Use glGenerateMipmap below
static const unsigned int GL_GENERATE_MIPMAP = 0x8191;
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
}
} else { // No MipMaps
switch( texture.filterQuality ) {
case TextureInfo::FILTER_NONE:
case TextureInfo::FILTER_GOOD: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
break;
}
default: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
}
}
}
The only thing that I see that could be irrelevant is your call to glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); for I have never used it, now this may be some type of behavior that you want and for that I do not know.
After you set up your mimpmapping information this is where you want to set the parameters for clamping or wrap repeat which you are missing from your code to generate a texture. They look like this:
bool wrapRepeat; // This variable would not be here inside of the function,
// but would come from this function's definition so that the caller can
// set this parameter or flag to the behavior they want for each texture in use.
// What these two lines of code will do depending on the state of wrapRepeat
// is they will cause the last parameter to be either WRAP in S & T coordinates or
// to clamp to edge in both S & T coordinates.
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );
Finally you have your code that will load the texture into memory and as a reference this is what I have within my function to generate textures:
// Load Texture Into Video Memory
glPixelStorei( GL_UNPACK_ALIGNMENT, texture.hasAlphaChannel ? 4 : 1 );
glTexImage2D( GL_TEXTURE_2D,
0,
( texture.hasAlphaChannel ? GL_RGBA8 : GL_RGB8 ),
texture.uWidth,
texture.uHeight,
0,
( texture.hasAlphaChannel ? GL_RGBA : GL_RGB ),
GL_UNSIGNED_BYTE,
&texture.vPixelData[0] );
if ( texture.generateMipMap && openglVersion.x >= 3 ) {
glGenerateMipmap( GL_TEXTURE_2D );
}
This should help you to resolve your current problem with your textures being wrapped and not clamped.
Now as for my project, I have a derived filehandler class that reads in texture files specifically and it loads in either a TGA or a PNG file and the way that it is designed it can load in any other texture or image file as long as you add a function to that class to parse the file type. My actual image types used in rendering are separate from the file reader. And they are separate from this code here. This code belongs to a class that is an AssetStorage. This class of mine is responsible for storing all assets and managing their memory and that is it. This class does not render anything, but it does load objects into ram and video ram. It is my batch class and batch manager classes that handles all of the rendering of objects. Now my objects are all built off of using GLSL Shaders. But to show you this function in full can be used as a reference into how my framework is designed.
// ----------------------------------------------------------------------------
// add()
// Creates An OpenGL Texture And Returns It's ID Value
// This Can Only Be Called From The Main OpenGL Thread
TextureInfo AssetStorage::add( const Texture& texture, const std::string& strFilename ) {
if ( INVALID_UNSIGNED != getTextureInfo( strFilename ).uTextureId ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " can not store " << strFilename << " multiple times";
throw ExceptionHandler( strStream );
}
TextureInfo textureInfo;
textureInfo.hasTransparency = texture.hasAlphaChannel;
textureInfo.size = glm::uvec2( texture.uWidth, texture.uHeight );
glGetError(); // Clear Errors
glGenTextures( 1, &textureInfo.uTextureId );
GLenum err = glGetError();
if ( err != GL_NO_ERROR ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " failed glGenTextures with error code 0x" << std::hex << err;
throw ExceptionHandler( strStream );
}
glBindTexture( GL_TEXTURE_2D, textureInfo.uTextureId );
// Wrap Textures
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, ( texture.wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, ( texture.wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );
const glm::uvec2& openglVersion = s_pSettings->getOpenglVersion();
if ( texture.generateMipMap ) {
switch ( texture.filterQuality ) {
case TextureInfo::FILTER_NONE : {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
break;
}
case TextureInfo::FILTER_GOOD: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR );
break;
}
case TextureInfo::FILTER_BEST: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
break;
}
default: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
}
} // Switch
if ( openglVersion.x < 3 ) {
// In OpenGL v3 GL_GENERATE_MIPMAP Is Deprecated, And In 3.1+ It Was Removed
// So For Those Versions We Use glGenerateMipmap below
static const unsigned int GL_GENERATE_MIPMAP = 0x8191;
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
}
} else { // No MipMaps
switch( texture.filterQuality ) {
case TextureInfo::FILTER_NONE:
case TextureInfo::FILTER_GOOD: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
break;
}
default: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
}
}
}
// Load Texture Into Video Memory
glPixelStorei( GL_UNPACK_ALIGNMENT, texture.hasAlphaChannel ? 4 : 1 );
glTexImage2D( GL_TEXTURE_2D,
0,
( texture.hasAlphaChannel ? GL_RGBA8 : GL_RGB8 ),
texture.uWidth,
texture.uHeight,
0,
( texture.hasAlphaChannel ? GL_RGBA : GL_RGB ),
GL_UNSIGNED_BYTE,
&texture.vPixelData[0] );
if ( texture.generateMipMap && openglVersion.x >= 3 ) {
glGenerateMipmap( GL_TEXTURE_2D );
}
// Store TextureId
BlockThread blockThread( s_criticalSection );
m_textureInfos.insert( MapTextureInfos::value_type( strFilename, textureInfo ) );
if ( s_pSettings->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
Logger::log( std::string( "Created " ) + strFilename );
}
return textureInfo;
} // add

OpenGL Texturing, no error but grey

Trying to colour terrain points based on texture colour (currently hard coded to vec2(0.5, 0.5) for test purposes - which should be light blue) but all the points are grey. glGetError returns 0 throughout the whole process. I think I might be doing the render process wrong or have a problem with my shaders(?)
Vertex Shader:
void main(){
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
Fragment Shader:
uniform sampler2D myTextureSampler;
void main(){
gl_FragColor = texture2D(myTextureSampler, vec2(0.5, 0.5));
}
Terrain Class:
class Terrain
{
public:
Terrain(GLuint pProgram, char* pHeightmap, char* pTexture){
if(!LoadTerrain(pHeightmap))
{
OutputDebugString("Loading terrain failed.\n");
}
if(!LoadTexture(pTexture))
{
OutputDebugString("Loading terrain texture failed.\n");
}
mProgram = pProgram;
mTextureLocation = glGetUniformLocation(pProgram, "myTextureSampler");
};
~Terrain(){};
void Draw()
{
glEnableClientState(GL_VERTEX_ARRAY); // Uncommenting this causes me to see nothing at all
glBindBuffer(GL_ARRAY_BUFFER, mVBO);
glVertexPointer(3, GL_FLOAT, 0, 0);
glEnable( GL_TEXTURE_2D );
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mBMP);
glProgramUniform1i(mProgram, mTextureLocation, 0);
GLenum a = glGetError();
glPointSize(5.0f);
glDrawArrays(GL_POINTS, 0, mNumberPoints);
a = glGetError();
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisable( GL_TEXTURE_2D );
glDisableClientState(GL_VERTEX_ARRAY);
}
private:
GLuint mVBO, mBMP, mUV, mTextureLocation, mProgram;
int mWidth;
int mHeight;
int mNumberPoints;
bool LoadTerrain(char* pFile)
{
/* Definitely no problem here - Vertex data is fine and rendering nice and dandy */
}
// TEXTURES MUST BE POWER OF TWO!!
bool LoadTexture(char *pFile)
{
unsigned char header[54]; // Each BMP file begins by a 54-bytes header
unsigned int dataPos; // Position in the file where the actual data begins
unsigned int width, height;
unsigned int imageSize;
unsigned char * data;
FILE * file = fopen(pFile, "rb");
if(!file)
return false;
if(fread(header, 1, 54, file) != 54)
{
fclose(file);
return false;
}
if ( header[0]!='B' || header[1]!='M' )
{
fclose(file);
return false;
}
// Read ints from the byte array
dataPos = *(int*)&(header[0x0A]);
imageSize = *(int*)&(header[0x22]);
width = *(int*)&(header[0x12]);
height = *(int*)&(header[0x16]);
// Some BMP files are misformatted, guess missing information
if (imageSize==0) imageSize=width*height*3; // 3 : one byte for each Red, Green and Blue component
if (dataPos==0) dataPos=54; // The BMP header is done that way
// Create a buffer
data = new unsigned char [imageSize];
// Read the actual data from the file into the buffer
fread(data,1,imageSize,file);
//Everything is in memory now, the file can be closed
fclose(file);
// Create one OpenGL texture
glGenTextures(1, &mBMP);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, mBMP);
// Give the image to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
delete [] data;
data = 0;
return true;
}
};
Answering own question incase anyone has a similar problem:
I had tested this with multiple images - but it turns out theres a bug in my graphics application of choice; which has been exporting 8-bit Bitmap's even though I explicitally told it to export 24-bit Bitmap's. So basically - reverting back to MS Paint solved my solution. 3 cheers for MS Paint.

OpenGL Applying Texture to Tessellation

I'm trying to take a concave polygon and apply an image to it as a texture. The polygon can have multiple contours, both internal holes and external "islands". It can be any shape, but will be smaller than the image and will fit inside it. It does not necessarily touch the edges of the image.
I've successfully displayed the tessellated polygon, and textured a simple square, but can't get the two to work together.
Here's how I'm loading the texture:
GLuint texture;
int width, height;
BYTE * data;
FILE * file;
// open texture data
file = fopen( filename, "rb" );
if ( file == NULL ) return 0;
// allocate buffer
width = 256;
height = 256;
data = (BYTE *)malloc( width * height * 3 );
// read texture data
fread( data, width * height * 3, 1, file );
fclose( file );
glGenTextures( 1, &texture );
glBindTexture( GL_TEXTURE_2D, texture );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap ? GL_REPEAT : GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap ? GL_REPEAT : GL_CLAMP );
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width,height, GL_RGB, GL_UNSIGNED_BYTE, data );
free( data );
return texture;
Here's the tessellation function:
GLuint tessellate1()
{
GLuint id = glGenLists(1); // create a display list
if(!id) return id; // failed to create a list, return 0
GLUtesselator *tess = gluNewTess(); // create a tessellator
if(!tess) return 0; // failed to create tessellation object, return 0
GLdouble quad1[4][3] = { {-1,3,0}, {0,0,0}, {1,3,0}, {0,2,0} };
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, GLTexture::LoadTextureRAW("texture.raw", true));
// register callback functions
gluTessCallback(tess, GLU_TESS_BEGIN, (void (CALLBACK *)())tessBeginCB);
gluTessCallback(tess, GLU_TESS_END, (void (CALLBACK *)())tessEndCB);
gluTessCallback(tess, GLU_TESS_ERROR, (void (CALLBACK *)())tessErrorCB);
gluTessCallback(tess, GLU_TESS_VERTEX, (void (CALLBACK *)())tessVertexCB);
glNewList(id, GL_COMPILE);
glColor3f(1,1,1);
gluTessBeginPolygon(tess, 0); // with NULL data
gluTessBeginContour(tess);
gluTessVertex(tess, quad1[0], quad1[0]);
gluTessVertex(tess, quad1[1], quad1[1]);
gluTessVertex(tess, quad1[2], quad1[2]);
gluTessVertex(tess, quad1[3], quad1[3]);
gluTessEndContour(tess);
gluTessEndPolygon(tess);
glEndList();
gluDeleteTess(tess); // delete after tessellation
glDisable(GL_TEXTURE_2D);
setCamera(0, 0, 5, 0, 0, 0);
return id; // return handle ID of a display list
}
Here's the tessellation vertex callback function:
// cast back to double type
const GLdouble *ptr = (const GLdouble*)data;
double dImageX = -1, dImageY = -1;
//hardcoded extents of the polygon for the purposes of testing
int minX = 607011, maxX = 616590;
int minY = 4918219, maxY = 4923933;
//get the % coord of the texture for a poly vertex. Assumes image and poly bounds are the same for the purposes of testing
dImageX = (ptr[0] - minX) / (maxX - minX);
dImageY = (ptr[1] - minY) / (maxY - minY);
glTexCoord2d(dImageX, dImageY);
glVertex2d(ptr[0], ptr[1]);
And here's the display callback:
void displayCB()
{
// clear buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// save the initial ModelView matrix before modifying ModelView matrix
glPushMatrix();
// tramsform camera
glTranslatef(0, 0, cameraDistance);
glRotatef(cameraAngleX, 1, 0, 0); // pitch
glRotatef(cameraAngleY, 0, 1, 0); // heading
// draw meshes
glCallList(listId1); //id of the tessellated poly
// draw info messages
showInfo();
glPopMatrix();
glutSwapBuffers();
}
The results of this are a correctly drawn polygon with no texture applied.
// init
glGenTextures( 1, &texture );
// vertex callback
glBindTexture(GL_TEXTURE_2D, 1);
I don't think the first ID returned by glGenTextures() is required to be 1.
Try using texture instead of 1 in your glBindTexture() call.
Also, there's really no reason to enable texturing and re-bind the texture for every vertex. Just do it once before you call into the tesselator.
You're not capturing the texture binding and Enable inside the display list, so it's not going to be taken into account when you replay it. So, either:
Capture the BindTexture and Enable inside the display list, or
BindTexture and Enable(TEXTURE_2D) before calling CallList
The problem was the glDisable(GL_TEXTURE_2D) call in the tessellation function. After removing it, the texture was applied correctly.

How to load a bmp on GLUT to use it as a texture?

I've been searching all around for a simple solution to add sprites to my OpenGl GLUT simple moon lander game in c++ and it appears I must use bmp's since they're easiest to load and use them as textures on a rectangle.
How exactly can I load the bmp's as textures though?
Look my simple c implementation function to load texture.
GLuint LoadTexture( const char * filename )
{
GLuint texture;
int width, height;
unsigned char * data;
FILE * file;
file = fopen( filename, "rb" );
if ( file == NULL ) return 0;
width = 1024;
height = 512;
data = (unsigned char *)malloc( width * height * 3 );
//int size = fseek(file,);
fread( data, width * height * 3, 1, file );
fclose( file );
for(int i = 0; i < width * height ; ++i)
{
int index = i*3;
unsigned char B,R;
B = data[index];
R = data[index+2];
data[index] = R;
data[index+2] = B;
}
glGenTextures( 1, &texture );
glBindTexture( GL_TEXTURE_2D, texture );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_REPEAT );
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height,GL_RGB, GL_UNSIGNED_BYTE, data );
free( data );
return texture;
}
Above function returns the texture data. Store the texture data in variable
GLuint texture;
texture= LoadTexture( "your_image_name.bmp" );
Now you can bind the texture using glBindTexture
glBindTexture (GL_TEXTURE_2D, texture);
Checkout my the TextureLoader (TextureLoader.h + TextureLoader.cpp) from OpenGL_3_2_Utils:
https://github.com/mortennobel/OpenGL_3_2_Utils
The two files does not depend on any other files and should also work seamless on any version of OpenGL (and any platform). Example usage can be found in the file comment.
You can use library GLAUX and SOIL(Simple OpenGL Image Library) .
There are also other image libriries for OpenGL.
How to load a bmp on GLUT to use it as a texture?
Another very simple solution would be to use STB library, which can be found at GitHub - nothings/stb.
All what is needed is one source file, the header file "stb_image.h". It doesn't require to link any library file or to compile any additional source file.
Include the header file and enable image reading by the setting the preprocessor definition STB_IMAGE IMPLEMENTATION:
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
The image file can be read by the function stbi_load:
const char *filename = .....; // path and filename
int req_channels = 3; // 3 color channels of BMP-file
int width = 0, height = 0, channels = 0;
stbi_uc *image = stbi_load( filename, &width, &height, &channels, 3 );
When the image is loaded to a texture object, then GL_UNPACK_ALIGNMENT has to be set to 1.
By default GL_UNPACK_ALIGNMENT is 4, so each line of an image is assumed to be aligned to 4 bytes. The pixels of a BMP-file in common have a size of 3 bytes and are tightly packed, this would cause a misalignment.
After loading the image, the memory can be freed by stbi_image_free:
GLuint texture_obj = 0;
if ( image != nullptr )
{
glGenTextures(1, &texture_obj);
glBindTexture(GL_TEXTURE_2D, texture_obj);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // default
stbi_image_free( image );
}
GLuint LoadTexture(GLuint tex, const char * filename, int width, int height)
{
//bmp 24 bit
unsigned char * data;
unsigned char R,G,B;
FILE * file;
//open .bmp
file = fopen(filename, "rb");
if(file == NULL)return 0;
//get memory for data
data =(unsigned char *)malloc(width * height * 3);
//data skip offset
fseek(file,128,0);
//read file to data
fread(data, width * height * 3, 1, file);
//close file
fclose(file);
//transpose R,G,B values
int index;
for(int i = 0; i < width * height ; ++i)
{
index = i*3;
B = data[index]; G = data[index+1]; R = data[index+2];
data[index] = R; data[index+1] = G; data[index+2] = B;
}
//create a texture
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height,GL_RGB, GL_UNSIGNED_BYTE, data);
//texture filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
//glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
//free memory
free(data);
return 0;
}
void init(void)
{
//texture loading, bmp 24 bit
LoadTexture(1, "01.bmp", 316, 316);
LoadTexture(2, "02.bmp", 316, 316);
LoadTexture(3, "05.bmp", 316, 316);
LoadTexture(4, "03.bmp", 316, 316);
LoadTexture(5, "06.bmp", 316, 316);
LoadTexture(6, "04.bmp", 316, 316);
. . . . . . . . . . . .
Linux
gcc cube.c -o cube -lglut -lGL -lGLU
Windows
tcc cube.c -o cube.exe -LC:\tcc\lib -lopengl32 -lglu32 -lglut32 -Wl,-subsystem=windows