Jump to content
  • Advertisement
DevAndroid

OpenGL ES Render Texture from PNG to Screen not working correctly OpenGL ES 3.0

Recommended Posts

Posted (edited)

Hello everyone,

I'm trying to display a 2D texture to screen but the rendering isn't working correctly.

First of all I did follow this tutorial to be able to render a Text to screen (I adapted it to render with OpenGL ES 2.0) : https://learnopengl.com/code_viewer.php?code=in-practice/text_rendering

So here is the shader I'm using :

const char           gVertexShader[] =
        "#version 320 es\n"
                "layout (location = 0) in vec4 vertex;\n"
                "out vec2 TexCoords;\n"
        "uniform mat4 projection;\n"
                "void main() {\n"
                "  gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);\n"
        "  TexCoords = vertex.zw;\n"
                "}\n";

const char           gFragmentShader[] =
        "#version 320 es\n"
        "precision mediump float;\n"
        "in vec2 TexCoords;\n"
                "out vec4 color;\n"
                "uniform sampler2D text;\n"
        "uniform vec3 textColor;\n"
                "void main() {\n"
                "  vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);\n"
        "  color = vec4(textColor, 1.0) * sampled;\n"
        "}\n";

The render text works very well so I would like to keep those Shaders program to render a texture loaded from PNG.

For that I'm using libPNG to load the PNG to a texture, here is my code :

GLuint Cluster::loadPngFromPath(const char *file_name, int *width, int *height) {
    png_byte header[8];

    FILE *fp = fopen(file_name, "rb");
    if (fp == 0) {
        return 0;
    }

    fread(header, 1, 8, fp);
    if (png_sig_cmp(header, 0, 8)) {
        fclose(fp);
        return 0;
    }

    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png_ptr) {
        fclose(fp);
        return 0;
    }

    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
        fclose(fp);
        return 0;
    }

    png_infop end_info = png_create_info_struct(png_ptr);
    if (!end_info) {
        png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
        fclose(fp);
        return 0;
    }

    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
        fclose(fp);
        return 0;
    }

    png_init_io(png_ptr, fp);
    png_set_sig_bytes(png_ptr, 8);
    png_read_info(png_ptr, info_ptr);

    int bit_depth, color_type;
    png_uint_32 temp_width, temp_height;

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

    png_read_update_info(png_ptr, info_ptr);

    int rowbytes = png_get_rowbytes(png_ptr, info_ptr);

    rowbytes += 3 - ((rowbytes-1) % 4);

    png_byte * image_data;
    image_data = (png_byte *) malloc(rowbytes * temp_height * sizeof(png_byte)+15);
    if (image_data == NULL) {
        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
        fclose(fp);
        return 0;
    }

    png_bytep * row_pointers = (png_bytep *) malloc(temp_height * sizeof(png_bytep));
    if (row_pointers == NULL) {
        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
        free(image_data);
        fclose(fp);
        return 0;
    }

    int i;
    for (i = 0; i < temp_height; i++) {
        row_pointers[temp_height - 1 - i] = image_data + i * rowbytes;
    }

    png_read_image(png_ptr, row_pointers);

    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    glTexImage2D(GL_TEXTURE_2D, GL_ZERO, GL_RGB, temp_width, temp_height, GL_ZERO, GL_RGB, GL_UNSIGNED_BYTE, image_data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
	
    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
    free(image_data);
    free(row_pointers);
    fclose(fp);

    return texture;
}

This code just generates the texture and I store the id on memory

And then I want to display my texture on any position (X, Y) of my screen so I did the following (That's works, at least the positioning).

//MY TEXTURE IS 32x32 pixels !
void Cluster::printTexture(GLuint idTexture, GLfloat x, GLfloat y) {
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(VAO);
 
    GLfloat vertices[6][4] = {
            { x,     	y + 32,		0.0, 0.0 },
            { x,     	y,       	0.0, 1.0 },
            { x + 32, 	y,       	1.0, 1.0 },

            { x,     	y + 32,   	0.0, 0.0 },
            { x + 32, 	y,       	1.0, 1.0 },
            { x + 32,   y + 32,   	1.0, 0.0 }         
     };

     glBindTexture(GL_TEXTURE_2D, idTexture);
     glBindBuffer(GL_ARRAY_BUFFER, VBO);
     glBufferSubData(GL_ARRAY_BUFFER, GL_ZERO, sizeof(vertices), vertices);
     glBindBuffer(GL_ARRAY_BUFFER, GL_ZERO);
     glUniform1i(this->mTextShaderHandle, GL_ZERO);
     glDrawArrays(GL_TRIANGLE_STRIP, GL_ZERO, 6);
}

My .png is a blue square.

The result is that my texture is not loaded correctly. It is not complete and there are many small black spots. I don't know what's going on ? It could be the vertices or the load ? Or maybe I need to add something on the shader. I don't know, I really need help.

Thanks !

Edited by DevAndroid

Share this post


Link to post
Share on other sites
Advertisement

1) If you use the calculation of texture coordinates in vertex shader, you should remove them from Vertex data, 

2) You should setup vertex attributes:

GLfloat vertices[6][2] = {
            { x,     	y + 32	},
            { x,     	y		},
            { x + 32, 	y      	},

            { x,     	y + 32	},
            { x + 32, 	y		},
            { x + 32,   y + 32	}         
     };
	 glBindTexture(GL_TEXTURE_2D, idTexture);
     glBindBuffer(GL_ARRAY_BUFFER, VBO);
	 // first you should call glBufferData instead of glBufferSubData, because OpenGL doesn't know about memory size.
	 // if you call glBufferSubData, then you will get GL_INVALID_ENUM, see please:
	 // https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glBufferSubData.xml
	 // https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glBufferSubData.xhtml
     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC);
	 // you should setup vertex attributes 
	 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), NULL);
     glUniform1i(this->mTextShaderHandle, GL_ZERO);
     glDrawArrays(GL_TRIANGLE_STRIP, GL_ZERO, 6);
     glBindVertexArray(0);

2) Do you set uniforms "texColor", "projection" ?

also you can simplify the pixel shader:

float alpha = texture(text, TexCoords).r);\n"
color = vec4(textColor, alpha);\n"

Try to use my corrections.

And you use OpenGL ES 3.0, not 2.0, because the VertexArray Object for OpenGL ES 2.0 is supported through GL_OES_vertex_array_object extension.

Share this post


Link to post
Share on other sites
Posted (edited)

Hello Andrey,

Thank you very much for your answer and your help.

It seems that your modifications did resolve the problem I had (The texture is well displayed).

For your questions : I don't set texColor for the PNG Texture. Only when I draw a Text to color it.

I set a projection like that one time at the start of the program :

glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(WIDTH), 0.0f, static_cast<GLfloat>(HEIGHT));
glUniformMatrix4fv(this->mProjectionShaderHandle, 1, GL_FALSE, glm::value_ptr(projection));

I simplified the pixel shader.

I have a problem occuring now when I call glBufferData instead of glBufferSubData :

When I try to draw a text after drawing a texture I got an error 0x501 on glBufferSubData. Here is my draw code for the text rendering :

glBindTexture(GL_TEXTURE_2D, ch.TextureID);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, GL_ZERO, sizeof(vertices), vertices); //error 0x501 there
glBindBuffer(GL_ARRAY_BUFFER, GL_ZERO);
glDrawArrays(GL_TRIANGLES, GL_ZERO, 6);

Just so you know, here is my initialization (just one time after surface creation and Shader program loading) here I set the memory allocation with glBufferData that's why I was using subData for my texture rendering.

//Configuration of Vertex Array Objects & Vertex Buffer Objects for texture quads
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW); //Here I have a glBufferData call
glEnableVertexAttribArray(GL_ZERO);
glVertexAttribPointer(GL_ZERO, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), GL_ZERO);
glBindBuffer(GL_ARRAY_BUFFER, GL_ZERO);
glBindVertexArray(GL_ZERO);

Maybe I have to call this kind of code only for the text rendering and not be global to whole program ?

Thank you again for your help.

Edited by DevAndroid

Share this post


Link to post
Share on other sites

Hello, DevAndroid, 

Try to comment binding of zero buffer Id in initialization/rendering code, because it can be saved in VAO state:

// glBindBuffer(GL_ARRAY_BUFFER, GL_ZERO);

 

Share this post


Link to post
Share on other sites
Posted (edited)

Still glBufferSubData of text rendering fails with 0x501 error

 

//My print Text function
void Cluster::printText(const std::string &text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color) {
    glUniform3f(this->mColorShaderHandle, color.x/255.0f, color.y/255.0f, color.z/255.0f);
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(VAO);

    std::string::const_iterator c;
    for (c = text.begin(); c != text.end(); c++) {
        Character ch = this->mCharacters[*c];

        GLfloat xpos = x + ch.Bearing.x * scale;
        GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale;

        GLfloat w = ch.Size.x * scale;
        GLfloat h = ch.Size.y * scale;

        GLfloat vertices[6][4] = {
            { xpos,     ypos + h,   	0.0, 0.0 },
            { xpos,     ypos,       	0.0, 1.0 },
            { xpos + w, ypos,       	1.0, 1.0 },

            { xpos,     ypos + h,   	0.0, 0.0 },
            { xpos + w, ypos,       	1.0, 1.0 },
            { xpos + w, ypos + h,   	1.0, 0.0 }          
        };

        glBindTexture(GL_TEXTURE_2D, ch.TextureID);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);        
	    glBufferSubData(GL_ARRAY_BUFFER, GL_ZERO, sizeof(vertices), vertices);
        glBindBuffer(GL_ARRAY_BUFFER, GL_ZERO);
        glDrawArrays(GL_TRIANGLES, GL_ZERO, 6);
        x += (ch.Advance >> 6) * scale;
    }
    glBindVertexArray(GL_ZERO);
    glBindTexture(GL_TEXTURE_2D, GL_ZERO);
}

//my printTexture function
void Cluster::printTexture(GLuint idTexture, GLfloat x, GLfloat y) {
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(VAO);

    GLfloat vertices[6][2] = {
            { x,     	y + 32	},
            { x,     	y	},
            { x + 32, 	y      	},

            { x,     	y + 32	},
            { x + 32, 	y	},
            { x + 32,   y + 32	}
     };

     glBindTexture(GL_TEXTURE_2D, idTexture);
     glBindBuffer(GL_ARRAY_BUFFER, VBO);

     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
     glVertexAttribPointer(GL_ZERO, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), GL_ZERO);
     glUniform1i(this->mTextShaderHandle, GL_ZERO);
     glDrawArrays(GL_TRIANGLES, GL_ZERO, 6);
     glBindVertexArray(0);
}

Here you have more details of my functions to print a text and print a texture.

Edited by DevAndroid
Adding some details of code

Share this post


Link to post
Share on other sites

GL_INVALID_VALUE is generated if offset or size is negative, or if together they define a region of memory that extends beyond the buffer object's allocated data store.

Try to check size of vertex data in glBufferData(2 parameter) /glBufferSubData (3 parameter)

if error still reproduced, you can create the Desktop version of project with OpenGL ES emulation and attach it to forum.

Share this post


Link to post
Share on other sites
Posted (edited)

Yes, in fact, please just check my last post I edited with the full code.

for the text print I have 6 * 4 values. That's why I was initializing the global program with:

glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_STATIC_DRAW);

but for the texture I have 6 * 2 values and I make a glBufferData so I think it reduces the size of the VBO and so it fails when I try to set an other text..

I cannot really make this work on desktop easily because, I'm running this program on an android AOSP. So i'm using some custom things to create the surface (using HWC of Android)

 

EDIT:

By putting glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_STATIC_DRAW); at the end of printTexture I don't have the error but the texture is no longer displayed... But I have all texts.

Edited by DevAndroid
more details

Share this post


Link to post
Share on other sites
Quote

Still glBufferSubData of text rendering fails with 0x501 error

try to remove 

// glBindBuffer(GL_ARRAY_BUFFER, GL_ZERO);

in 

Quote

 

but for the texture I have 6 * 2 values and I make a glBufferData so I think it reduces the size of the VBO and so it fails when I try to set an other text..


 

 

if you use

glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices, GL_STATIC_DRAW);

instead of glBufferData in printTexture with reduced size, is error reproduced ?

Try to use separate VBO for text and print texture, because using the same buffer for different size is strange decision.

Also for dynamic data you should use GL_DYNAMIC_DRAW

 

Share this post


Link to post
Share on other sites
Posted (edited)
20 minutes ago, Andrey OGL_D3D said:

Try to use separate VBO for text and print texture, because using the same buffer for different size is strange decision.

Excellent, that is what I'm doing just right now.

Like that :

//Configuration of Vertex Array Objects & Vertex Buffer Objects for texture quads
	glGenVertexArrays(2, this->VAO);
    	glGenBuffers(2, this->VBO);

	//init font rendering VAO VBO
    	glBindVertexArray(this->VAO[0]);
    	glBindBuffer(GL_ARRAY_BUFFER, this->VBO[0]);
    	glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, GL_ZERO, GL_DYNAMIC_DRAW);
    	glEnableVertexAttribArray(GL_ZERO);
    	glVertexAttribPointer(GL_ZERO, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), GL_ZERO);
    	glBindVertexArray(GL_ZERO);

	//init texture rendering VAO VBO
    	glBindVertexArray(this->VAO[1]);
    	glBindBuffer(GL_ARRAY_BUFFER, this->VBO[1]);
    	glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 2, GL_ZERO, GL_STATIC_DRAW);
    	glEnableVertexAttribArray(GL_ZERO);
    	glVertexAttribPointer(GL_ZERO, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), GL_ZERO);
    	glBindVertexArray(GL_ZERO);
 

And here is my new printTexture and my new printText

void Cluster::printText(const std::string &text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color) {
    glUniform3f(this->mColorShaderHandle, color.x/255.0f, color.y/255.0f, color.z/255.0f);
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(this->VAO[0]);

    std::string::const_iterator c;
    for (c = text.begin(); c != text.end(); c++) {
        Character ch = this->mCharacters[*c];

        GLfloat xpos = x + ch.Bearing.x * scale;
        GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale;

        GLfloat w = ch.Size.x * scale;
        GLfloat h = ch.Size.y * scale;

        GLfloat vertices[6][4] = {
            { xpos,     ypos + h,   	0.0, 0.0 },
            { xpos,     ypos,       	0.0, 1.0 },
            { xpos + w, ypos,       	1.0, 1.0 },

            { xpos,     ypos + h,   	0.0, 0.0 },
            { xpos + w, ypos,       	1.0, 1.0 },
            { xpos + w, ypos + h,   	1.0, 0.0 }          
        };

        glBindTexture(GL_TEXTURE_2D, ch.TextureID);
        glBindBuffer(GL_ARRAY_BUFFER, this->VBO[0]);
     	glBufferSubData(GL_ARRAY_BUFFER, GL_ZERO, sizeof(vertices), vertices);
        glBindBuffer(GL_ARRAY_BUFFER, GL_ZERO);
        glDrawArrays(GL_TRIANGLES, GL_ZERO, 6);
    	x += (ch.Advance >> 6) * scale;
    }
    glBindVertexArray(GL_ZERO);
    glBindTexture(GL_TEXTURE_2D, GL_ZERO);
}

void Cluster::printTexture(GLuint idTexture, GLfloat x, GLfloat y) {
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(this->VAO[1]);

    GLfloat vertices[6][2] = {
            { x,     	y + 32	},
            { x,     	y	},
            { x + 32, 	y      	},

            { x,     	y + 32	},
            { x + 32, 	y	},
            { x + 32,   y + 32	}
    };

    glBindTexture(GL_TEXTURE_2D, idTexture);
    glBindBuffer(GL_ARRAY_BUFFER, this->VBO[1]);

    glBufferSubData(GL_ARRAY_BUFFER, GL_ZERO, sizeof(vertices), vertices);
    glDrawArrays(GL_TRIANGLE_STRIP, GL_ZERO, 6);
    glBindVertexArray(GL_ZERO);
    glBindTexture(GL_TEXTURE_2D, GL_ZERO);
}

And the main is like that :

I load every characters for font redering then I load my PNG's using libPNG.

Then I call my printText and my printTexture function like that for example :

glClearColor(GL_ZERO, GL_ZERO, GL_ZERO, GL_ZERO);
glClear(GL_COLOR_BUFFER_BIT);
printText("HELLO", 50.0f, 1000.0f, 0.5f, glm::vec3(47.0f, 239.0f, 236.0f));
printText("WORLD", 200.0f, 1000.0f, 0.4f, glm::vec3(125.0f, 185.0f, 184.0f));
printTexture(idOfTextureLoaded, 50.0f, 380.0f);
eglSwapBuffers(this->mDisplay, this->mSurface);

Result is that the text "Hello World" is displayed at the right position but the texture still not..

 

What do you think ? Thank you for your help.

Edited by DevAndroid

Share this post


Link to post
Share on other sites
Posted (edited)
12 minutes ago, DevAndroid said:

at the right position but the texture still not..

1) Problem with Z coordinates for textured quad ? You can use the same float vertices[6][4] instead of float vertices[6][2]

2) vertex shader is waiting

in vec4 vertex

attribute, but you set

glVertexAttribPointer(GL_ZERO, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), GL_ZERO);

 

You can remove duplicate code and use the common function for preparing VBO/VAO for textured quad and text symbol with different data.

Edited by Andrey OGL_D3D

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!