Load Texture From File problem Android NDK and OpenGL - android

I am trying to load an image through OpenGL and stbi_load on android ndk. The problem is that it is generating an invalid texture (equal to zero).
LoadTextureFromFile("/storage/emulated/0/Download/BPV/mic_close.jpg", &my_image_texture, &my_image_width, &my_image_height);
bool CGUI::LoadTextureFromFile(const char* filename, GLuint* out_texture, int* out_width, int* out_height)
{
// Load from file
int image_width = 0;
int image_height = 0;
unsigned char* image_data = stbi_load(filename, &image_width, &image_height, NULL, 4);
if (image_data == NULL)
return false;
// Create a OpenGL texture identifier
GLuint image_texture;
glGenTextures(1, &image_texture);
glBindTexture(GL_TEXTURE_2D, image_texture);
// Setup filtering parameters for display
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Upload pixels into texture
glPixelStorei(0x0CF2, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
stbi_image_free(image_data);
*out_texture = image_texture; // Receive zero
*out_width = image_width;
*out_height = image_height;
return true;
}
I use libGLESv2

GLuint image_texture;
glGenTextures(1, &image_texture);
...
*out_texture = image_texture; // Receive zero
glGenTextures() just allocates new object and practically never fails, save when called without active OpenGL context.
On Android platform, OpenGL rendering is usually done in dedicated working thread (different from GUI thread), so most likely you call the method from the wrong thread or before OpenGL context initialization (which cannot be deduced from the given code sample).

Related

creating a framebuffer to take an image from external oes

I am trying to do multi pass rendering with framebuffer the issue is that the source is the camera so the texInput is not of type GL_TEXTURE_2D it is of type GL_TEXTURE_EXTERNAL_OES tho, if I use the function glTexImage2D it gives a black screen when I use that texture in the future after rendering to it without any errors glErros.
On the other hand if I don't use it(because it does not support GL_TEXTURE_EXTERNAL_OES) it gives me framebuffer status of NOT_COMPLETE
here is my code for creating the framebuffer and the texture that I want to associate to it.
fun prepareFBO() {
val intArray = IntArray(1)
glGenFramebuffers(1, intArray, 0)
fboId = intArray.first()
glGenTextures(1, intArray, 0)
texId = intArray.first()
glBindTexture(GL_TEXTURE_2D, texId)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,1080, 1920, 0, GL_RGBA, GL_UNSIGNED_BYTE, null)
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_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glBindFramebuffer(GL_FRAMEBUFFER, fboId)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0)
val status = glCheckFramebufferStatus(GL_FRAMEBUFFER)
Log.e(TAG, "prepareFBO: ${status == GL_FRAMEBUFFER_COMPLETE}")
glBindFramebuffer(GL_FRAMEBUFFER, 0)
glBindTexture(GL_TEXTURE_2D, 0)
}
the rest is just normal glDrawArrays and glActiveTexture that already works fine when I draw directly to the default framebuffer, if needed please let me know and I will share them.
Thanks for help!
UPDATE:
I found that I need instead of using glTexImage2d I can use eglBindTexImage instead so the final code is:
fun prepareFBO() {
val intArray = IntArray(1)
glGenFramebuffers(1, intArray, 0)
fboId = intArray.first()
glBindFramebuffer(GL_FRAMEBUFFER, fboId)
glGenTextures(1, intArray, 0)
texId = intArray.first()
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texId)
checkGlError("Error 1")
eglBindTexImage(eglGetCurrentDisplay(), windowSurface!!.eglSurface, EGL_BACK_BUFFER)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_EXTERNAL_OES, texId, 0)
val status = glCheckFramebufferStatus(GL_FRAMEBUFFER)
Log.e(TAG, "prepareFBO: is status complete? => ${status == GL_FRAMEBUFFER_COMPLETE}")
}
while this looks like it makes sense, still I am getting the status of the framebuffer as not complete :/

Android12 opengles3.0 glTexImage2D 0x502 error

I have 5 textures, such as diffuse specular normal roughness ao, roughness and ao are 8bit jpg.
I got glError 0x502 while loading rouggness/ao texture after call glTexImage2D, but other three texture is OK.
And this issue just occured with android 12, android 11/10/9/8 is no error. How I can fix this issue?
code:
GLuint textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);
GLenum format;
if (nrComponents == 1) {
format = GL_RED;
} else if (nrComponents == 3) {
format = GL_RGB;
} else {
format = GL_RGBA;
}
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
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_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
According to https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml, GL_RED is not an acceptable value for internalFormat (parameter 3 in glTexImage2D).
I think you need internalFormat=GL_R8 and format=GL_RED. Or you could use GL_LUMINANCE for both.

Render to texture attached to Frame Buffer Object (texture appears black)

I have generated a frame buffer object(FBO), bind it, attached a texture to its GL_COLOR_ATTACHMENT0. Then ensure that I get its status as GL_FRAMEBUFFER_COMPLETE. I have another texture(which is displaying on display), let's call it workingTexture. Now I want to render this workingTexture onto FBO, so I bind this FBO and then render workingTexture.
Then I bind default frame buffer(reserved for display) and try to render the texture attached to FBO, thinking that I will get my texture onto display, but I get black texture.
Relevant code
code to generate workingTexture...
int[] workingTexture = new int[1];
glGenTextures(1, workingTexture, 0); // generate workingTexture
glActiveTexture(GL_TEXTURE0); // attach it to TEXTURE_UNIT0
glBindTexture(GL_TEXTURE_2D, workingTexture[0]);
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_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
code to generate fbo
int[] fboName = new int[1];
int[] fboTextureName = new int[1];
final int fboTextureUnit = 1; // attach this texture to texture unit GL_TEXTURE1
glGenFramebuffers(1, fboName, 0);
int generatedTextureNameForFBO =
generateTextureForFBO(fboTextureUnit,
width, height);
fboTextureName[0] = generatedTextureNameForFBO;
glBindFramebuffer(GL_FRAMEBUFFER, fboName[0]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, generatedTextureNameForFBO, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
// created FBO is not complete
throw new RuntimeException("FBO status INCOMPLETE 😔😔");
}
method used to generate texture attached to fbo
private int generateTextureForFBO(#IntRange(from = 0) final int textureUnit,
#IntRange(from = 1) final int width,
#IntRange(from = 1) final int height) {
int[] textureName = new int[1];
glGenTextures(1, textureName, 0);
glActiveTexture(GL_TEXTURE0 + textureUnit);
glBindTexture(GL_TEXTURE_2D, textureName[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
// glBindTexture(GL_TEXTURE_2D, 0);
// 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_MIRRORED_REPEAT);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
// glBindTexture(GL_TEXTURE_2D, 0);
return textureName[0];
}
Now coming to drawing part...
#Override
public void onDrawFrame(GL10 unused) {
glClear(GL_COLOR_BUFFER_BIT);
//renderTex(mViewMatrix, 0, texProgram); // this texture is drawing on screen
glBindFramebuffer(GL_FRAMEBUFFER, fboName[0]);
renderTex(mViewMatrix, 0, texProgram); // rendering workingTexture onto FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);
renderTex(mViewMatrix, fboTextureUnit[0], texProgram); // texture appears black
}
renderTex is a simple method to render texture. It is working fine.
Also I have checked for GL_ERROR but there is no error.
My understanding of application created FBO is that every read and write glCall will happen on currently bound FBO, so is my understanding of FBO incorrect, or is there some error in my code.
Platform OpenGL|es 20 on Android
The texture which is generated in generateTextureForFBO is mipmap incomplete.
If you do not generate mipmaps (by glGenerateMipmap), then setting the GL_TEXTURE_MIN_FILTER is important. Since the default filter is GL_NEAREST_MIPMAP_LINEAR the texture would be mipmap incomplete, if you don not change the minifying function to GL_NEAREST or GL_LINEAR.
Set the texture minifying function (GL_TEXTURE_MIN_FILTER) by glTexParameter:
glBindTexture(GL_TEXTURE_2D, textureName[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

Failed to initialize FBO on Android SDK emulator device

So this is how FBO is initialized:
/**
* Initializes Renderbuffer.
*/
static GLuint init_renderbuffer(GLuint width, GLuint height, GLenum format) {
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
checkGlError("init_renderbuffer: glGenRenderbuffers");
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
checkGlError("init_renderbuffer: glBindRenderbuffer");
glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
checkGlError("init_renderbuffer: glRenderbufferStorage");
glBindRenderbuffer(GL_RENDERBUFFER, 0);
checkGlError("init_renderbuffer: glBindRenderbuffer");
return renderbuffer;
}
/**
* Initializes FBO.
*/
static void engine_init_fbo(struct engine* engine, GLuint width, GLuint height) {
// create renderable texture
glGenTextures(1, &engine->renderableTexture);
checkGlError("engine_init_fbo: glGenTextures");
glBindTexture(GL_TEXTURE_2D, engine->renderableTexture);
checkGlError("engine_init_fbo: glBindTexture");
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
checkGlError("engine_init_fbo: glTexImage2D");
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);
checkGlError("engine_init_fbo: glTexParameteri");
// create render buffers
engine->depthRenderBuffer = init_renderbuffer(width, height, GL_DEPTH_COMPONENT16);
engine->stencilRenderBuffer = init_renderbuffer(width, height, GL_STENCIL_INDEX8);
LOGI("****************************** FBO: T: %d, D: %d, S: %d", engine->renderableTexture,
engine->depthRenderBuffer, engine->stencilRenderBuffer);
// create framebuffer object
glGenFramebuffers(1, &engine->framebufferObject);
checkGlError("engine_init_fbo: glGenFramebuffers");
glBindFramebuffer(GL_FRAMEBUFFER, engine->framebufferObject);
checkGlError("engine_init_fbo: glBindFramebuffer");
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, engine->renderableTexture, 0);
checkGlError("engine_init_fbo: glFramebufferTexture2D");
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, engine->depthRenderBuffer);
checkGlError("engine_init_fbo: glFramebufferRenderbuffer");
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, engine->stencilRenderBuffer);
checkGlError("engine_init_fbo: glFramebufferRenderbuffer");
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
checkGlError("engine_init_fbo: glCheckFramebufferStatus");
if(status != GL_FRAMEBUFFER_COMPLETE) {
switch(status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
LOGI("****************************** engine_init_fbo: FBO error: FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
LOGI("****************************** engine_init_fbo: FBO error: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
LOGI("****************************** engine_init_fbo: FBO error: FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
LOGI("****************************** engine_init_fbo: FBO error: FRAMEBUFFER_UNSUPPORTED");
break;
default:
LOGI("****************************** engine_init_fbo: Unknown FBO error");
}
}
else {
LOGI("****************************** engine_init_fbo: FBO has been successfully initialized");
}
glBindTexture(GL_TEXTURE_2D, 0);
checkGlError("engine_init_fbo: glBindTexture");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
checkGlError("engine_init_fbo: glBindFramebuffer");
}
As you can see, everything here is done according to OpenGL ES standard: 2D texture with RGB565 internal format is used as color; 2 renderbuffers are used as depth and stencil surfaces; dimensions are 32x32.
It works fine on real device (Alcatel OT 918D, prints "FBO has been successfully initialized" in adb logcat), but fails with FRAMEBUFFER_UNSUPPORTED on virtual device.
My virtual device's CPU is atom, GPU emulation is enabled, OpenGL ES 2.0 works fine - checked with another applications (which do not use FBOs).
Entire project (based on native-activity sample from NDK), which can be compiled with ndk-build, is here:
https://docs.google.com/file/d/0Byy41LxMuTKUZVJxSUtBZDVDXzA/edit?usp=sharing
My question is: Is this a bug in android-sdk emulator? Because everything seems to meet the minimum requerements to the OpenGL ES 2.0 implementation.
Thank you!
EDIT:
Found the solution. It appears, that AVD does not support this configuration of attached objects. I had to remove stencil attachment to make it work.
EDIT2:
It appears to be the context creation problem. I did not require stencil bits, so probably AVD created context without stencil support, and Alcatel phone - with stencil.
Anyway, this is not a bug, but my mistake.

How to use NPOT images as textures with libpng and OpenGL ES 1.1?

I'm trying to use NPOT-sized PNG images as textures in OpenGL ES 1.1 (so no GL_arb_texture_rectangle) using libpng 1.5. With SDL, I could just blit the NPOT image onto a NPOT texture, but I can't figure out how to do something similar with libpng.
When I'm using the original texture (1280x720), all I get is a white surface. When I resize it to 1024x512 on the file system, it displays fine.
For some reason, the NPOT texture works on a 4.2 AVD with -gpu on.
Here's the code. It's pretty much this, with some changes to read from an APK using libzip instead of fopening directly.
void png_zip_read(png_structp png, png_bytep data, png_size_t size)
{
zip_file* file = static_cast<zip_file*>(png_get_io_ptr(png));
zip_fread(file, data, size);
}
GLuint load_png_texture(const std::string& path, unsigned int& width,
unsigned int& height)
{
if (!apk_path.length())
throw "APK Path not set";
zip* apk = zip_open(apk_path.c_str(), 0, NULL);
if (!apk)
throw "Error loading APK";
zip_file* file = zip_fopen(apk, path.c_str(), 0);
if (file == 0)
throw path + ": " + strerror(errno);
png_byte header[8];
zip_fread(file, header, 8);
if (png_sig_cmp(header, 0, 8)) {
zip_fclose(file);
zip_close(apk);
throw path + " is not a PNG";
}
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
if (!png) {
zip_fclose(file);
zip_close(apk);
throw "Failed to create png struct";
}
png_infop info = png_create_info_struct(png);
if (!info) {
png_destroy_read_struct(&png, (png_infopp) NULL, (png_infopp) NULL);
zip_fclose(file);
zip_close(apk);
throw "Failed to create png info struct";
}
png_infop info_end = png_create_info_struct(png);
if (!info_end) {
png_destroy_read_struct(&png, &info, (png_infopp) NULL);
zip_fclose(file);
zip_close(apk);
throw "Failed to create png info struct";
}
if (setjmp(png_jmpbuf(png))) {
png_destroy_read_struct(&png, &info, &info_end);
zip_fclose(file);
zip_close(apk);
throw "Error from libpng";
}
png_set_read_fn(png, file, png_zip_read);
png_set_sig_bytes(png, 8);
png_read_info(png, info);
int bit_depth, color_type;
unsigned int temp_width, temp_height;
png_get_IHDR(png, info, &temp_width, &temp_height, &bit_depth,
&color_type, NULL, NULL, NULL);
width = temp_width;
height = temp_height;
png_read_update_info(png, info);
int row_bytes = png_get_rowbytes(png, info);
png_byte* image_data = new png_byte[row_bytes * height];
if (!image_data) {
png_destroy_read_struct(&png, &info, &info_end);
zip_fclose(file);
zip_close(apk);
throw "Could not allocate memory for PNG image data";
}
png_bytep* row_pointers = new png_bytep[height];
if (!row_pointers) {
png_destroy_read_struct(&png, &info, &info_end);
delete[] image_data;
zip_fclose(file);
zip_close(apk);
throw "Could not allocate memory for PNG row pointers";
}
for (int i = 0; i < height; i++)
row_pointers[height - 1 - i] = image_data + i * row_bytes;
png_read_image(png, row_pointers);
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
GLenum format = GL_RGBA;
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0,
format, GL_UNSIGNED_BYTE, image_data);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
png_destroy_read_struct(&png, &info, &info_end);
delete[] image_data;
delete[] row_pointers;
zip_fclose(file);
zip_close(apk);
return texture;
}
After some more trial and error, I went back to frantic googling and finally found a code sample that does exactly what I'm looking for.
The secret is basically to create the OpenGL texture like this:
int texture_width = next_power_of_two(width);
int texture_height = next_power_of_two(height);
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(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);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if (texture_width != width || texture_height != height) {
glTexImage2D(GL_TEXTURE_2D, 0, format, texture_width,
texture_height, 0, format, GL_UNSIGNED_BYTE, NULL);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format,
GL_UNSIGNED_BYTE, image_data);
} else
glTexImage2D(GL_TEXTURE_2D, 0, format, texture_width,
texture_height, 0, format, GL_UNSIGNED_BYTE,
image_data);
To render the image at its original size, the x/y texture coordinates have to be calculated as follows (instead of just using 1):
float x_coordinate = (width - 0.5f) / texture_width;
float y_coordinate = (height - 0.5f) / texture_height;
ES 1.1 officially does not support NPOT textures without extensions. It might accidentally work on some implementations, but that's just "luck".
The usual approach is to pack your NPOT images in larger POT textures and adjust texture coordinates accordingly. You can do this programmatically at runtime ("packing"), or just do it in the toolchain (most likely your art program).

Categories

Resources