Texture Mapping a square image onto a circle OpenGl

1.1k views Asked by At

I am trying to map a square image of a clock face onto a circle GL_POLYGON that I have created. I am currently using the following code:

        float angle, radian, x, y, xcos, ysin, tx, ty;       

        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, an_face_texture1);

        glBegin(GL_POLYGON);

        for (angle=0.0; angle<360.0; angle+=2.0)
        {
            radian = angle * (pi/180.0f);

            xcos = (float)cos(radian);
            ysin = (float)sin(radian);
            x = xcos * radius;
            y = ysin * radius;
            tx = (x/radius + 1)*0.5;
            ty = (y/radius + 1)*0.5;

            glTexCoord2f(tx, ty);
            glVertex2f(x, y);           
        }

        glEnd();
        glDisable(GL_TEXTURE_2D);

However when I do it I end up with a weird overlapping image effect. As shown here:enter image description here The original texture image is enter image description here however the corners are cut out and it is png format. This way of generating the texture coordinates and is took from a previous answer: HERE

Below is the code used to load the image:

#ifndef PNGLOAD_H

#include <png.h>
#include <stdlib.h>

int png_load(const char* file_name, 
             int* width, 
             int* height, 
             char** image_data_ptr)
{
png_byte header[8];

FILE* fp = fopen(file_name, "rb");
if (fp == 0)
{
    fprintf(stderr, "erro: could not open PNG file %s\n", file_name);
    perror(file_name);
    return 0;
}

// read the header
fread(header, 1, 8, fp);

if (png_sig_cmp(header, 0, 8))
{
    fprintf(stderr, "error: %s is not a PNG.\n", file_name);
    fclose(fp);
    return 0;
}

png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
{
    fprintf(stderr, "error: png_create_read_struct returned 0.\n");
    fclose(fp);
    return 0;
}

// create png info struct
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
    fprintf(stderr, "error: png_create_info_struct returned 0.\n");
    png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
    fclose(fp);
    return 0;
}

// create png info struct
png_infop end_info = png_create_info_struct(png_ptr);
if (!end_info)
{
    fprintf(stderr, "error: png_create_info_struct returned 0.\n");
    png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
    fclose(fp);
    return 0;
}

// the code in this if statement gets called if libpng encounters an error
if (setjmp(png_jmpbuf(png_ptr))) {
    fprintf(stderr, "error from libpng\n");
    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
    fclose(fp);
    return 0;
}

// init png reading
png_init_io(png_ptr, fp);

// let libpng know you already read the first 8 bytes
png_set_sig_bytes(png_ptr, 8);

// read all the info up to the image data
png_read_info(png_ptr, info_ptr);

// variables to pass to get info
int bit_depth, color_type;
png_uint_32 temp_width, temp_height;

// get info about png
png_get_IHDR(png_ptr, info_ptr, &temp_width, &temp_height, &bit_depth, &color_type,
    NULL, NULL, NULL);

if (width) { *width = temp_width; }
if (height){ *height = temp_height; }

// Update the png info struct.
png_read_update_info(png_ptr, info_ptr);

// Row size in bytes.
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);

// glTexImage2d requires rows to be 4-byte aligned
rowbytes += 3 - ((rowbytes-1) % 4);

// Allocate the image_data as a big block, to be given to opengl
png_byte* image_data;
image_data = (png_byte*)malloc(rowbytes * temp_height * sizeof(png_byte)+15);
if (image_data == NULL)
{
    fprintf(stderr, "error: could not allocate memory for PNG image data\n");
    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
    fclose(fp);
    return 0;
}

// row_pointers is for pointing to image_data for reading the png with libpng
png_bytep* row_pointers = (png_bytep*)malloc(temp_height * sizeof(png_bytep));
if (row_pointers == NULL)
{
    fprintf(stderr, "error: could not allocate memory for PNG row pointers\n");
    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
    free(image_data);
    fclose(fp);
    return 0;
}

// set the individual row_pointers to point at the correct offsets of image_data
int i;
for (i = 0; i < temp_height; i++)
{
    row_pointers[temp_height - 1 - i] = image_data + i * rowbytes;
}

// read the png into image_data through row_pointers
png_read_image(png_ptr, row_pointers);

// clean up
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);

//free(image_data);
*image_data_ptr = (char*)image_data; // return data pointer

free(row_pointers);
fclose(fp);

fprintf(stderr, "\t texture image size is %d x %d\n", *width, *height);

return 1;
}

#endif

and:

unsigned int load_and_bind_texture(const char* filename)
{
char* image_buffer = NULL; // the image data
int width = 0;
int height = 0;

// read in the PNG image data into image_buffer
if (png_load(filename, &width, &height, &image_buffer)==0)
{
    fprintf(stderr, "Failed to read image texture from %s\n", filename);
    exit(1);
}

unsigned int tex_handle = 0;

// request one texture handle
glGenTextures(1, &tex_handle); 

// create a new texture object and bind it to tex_handle
glBindTexture(GL_TEXTURE_2D, tex_handle);

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

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

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

glTexImage2D(GL_TEXTURE_2D, 0, 
            GL_RGB, width, height, 0,
            GL_RGB, GL_UNSIGNED_BYTE, image_buffer);


free(image_buffer); // free the image buffer memory

return tex_handle;
}

these are then called from the init() method:

background_texture = load_and_bind_texture("images/office-wall.png");
an_face_texture1 = load_and_bind_texture("images/clock.png");
1

There are 1 answers

3
Nicol Bolas On BEST ANSWER

the image is loaded in the same way the background is loaded.

Yes, and that is almost certainly the problem. While both images are PNGs, they are almost certainly not the same format.

Let's actually debug what you see in the loaded texture. You see 2 overlapping with 10. 3 overlapped with 9. 8 overlapped with 4. All interlaced with each other. And this pattern repeats 3 times.

It's as if you took the original image, folded it over itself vertically, and then repeated it. 3 times.

The repetition of "3" in this strongly suggests a mismatch between what libPNG actually read and what you told OpenGL the texel data actually was. You told OpenGL that the texture was in the RGB format, 3 bytes per pixel.

But not every PNG is formatted that way. Some PNGs are greyscale; one byte per pixel. And because you used the low-level libPNG reading interface, you read the exact format of the pixel data from the PNG. Yes, it decompresses it. But you're reading exactly what the PNG stored conceptually.

So if the PNG is a greyscale PNG, your call to png_read_image can read data that isn't 3-bytes per pixel. But you told OpenGL that the data was 3 bytes per pixel. So if the libPNG wrote 1 byte per pixel, you will be giving OpenGL the wrong texel data.

That's bad.

If you're going to use libPNG's low-level reading routines, then you must actually check the format of the PNG being read and adjust your OpenGL code to match.

It would be much easier to use the higher-level reading routines and explicitly telling it to translate grayscale to RGB.