英文:
How can I load Textures in the background in EGL?
问题
我正在编写一个大学作业,需要创建一个使用纹理/材质的OpenGL场景。我的想法是允许用户决定是否使用纹理或材质(请注意,这已经实现了),但是加载纹理图像太长时间,导致在启动时整个场景都会停顿,直到它们全部加载完毕。我想以某种方式从磁盘加载图像(也许使用线程?尽管我读过这是不可能的),同时使用材质渲染场景,以便场景可以更快地加载。我加载纹理图像的代码如下:
Texture imgGold;
imgGold.initTexture("resources/textures/gold/gold.png");
initTexture
函数由老师提供的(我们可以更改它)。Texture.cpp 文件如下所示:
#include "Texture.h"
//------------------------
// 创建纹理
//------------------------
void Texture::initTexture(const char *textureFile) {
// 创建要配置的纹理
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 加载图像
unsigned int w, h;
unsigned char *pixels = loadTexture(textureFile, w, h);
// 创建纹理
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void *)pixels);
glGenerateMipmap(GL_TEXTURE_2D);
// 配置纹理(GL_REPEAT 重复纹理;GL_CLAMP 不重复,GL_LINEAR 和其他详细信息可以参考理论)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
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);
float aniso;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &aniso);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
}
//--------------------------------------------------
// 通过Freeimage库加载纹理
//--------------------------------------------------
unsigned char* Texture::loadTexture(const char *textureFile, unsigned int &w, unsigned int &h) {
FreeImage_Initialise(TRUE);
// 读取纹理
FREE_IMAGE_FORMAT format = FreeImage_GetFileType(textureFile, 0);
if (format == FIF_UNKNOWN) format = FreeImage_GetFIFFromFilename(textureFile);
if ((format == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading(format)) {
std::cout << "不支持纹理格式 " << textureFile << "。" << std::endl;
std::cin.get();
exit(1);
}
FIBITMAP *texture = FreeImage_Load(format, textureFile);
if (texture == NULL) {
std::cout << "无法打开文件 " << textureFile << "。" << std::endl;
std::cin.get();
exit(1);
}
FIBITMAP *temp = texture;
texture = FreeImage_ConvertTo32Bits(texture);
FreeImage_Unload(temp);
// 从BGRA到RGBA
w = FreeImage_GetWidth(texture);
h = FreeImage_GetHeight(texture);
unsigned char *pixelsBGRA = (unsigned char *)FreeImage_GetBits(texture);
unsigned char *pixelsRGBA = new unsigned char[4 * w * h];
for (int j = 0; j < w * h; j++) {
pixelsRGBA[j * 4 + 0] = pixelsBGRA[j * 4 + 2];
pixelsRGBA[j * 4 + 1] = pixelsBGRA[j * 4 + 1];
pixelsRGBA[j * 4 + 2] = pixelsBGRA[j * 4 + 0];
pixelsRGBA[j * 4 + 3] = pixelsBGRA[j * 4 + 3];
}
FreeImage_Unload(texture);
FreeImage_DeInitialise();
return pixelsRGBA;
}
//-----------------------------------------
// 返回纹理的标识符
//-----------------------------------------
unsigned int Texture::getTexture() {
return texture;
}
//-----------------------
// 类的析构函数
//-----------------------
Texture::~Texture() {
glDeleteTextures(1, &texture);
}
Texture.h 如下所示:
#ifndef TEXTURE_H
#define TEXTURE_H
#include <iostream>
#include <GL/glew.h>
#include <FreeImage.h>
class Texture {
public:
void initTexture(const char *textureFile);
unsigned int getTexture();
virtual ~Texture();
private:
unsigned int texture;
unsigned char *loadTexture(const char *textureFile, unsigned int &w, unsigned int &h);
};
#endif /* TEXTURE_H */
注意:加载后,图像用于创建一个 Textures 对象,该对象如下所示:
struct Textures {
unsigned int diffuse;
unsigned int specular;
unsigned int emissive;
unsigned int normal;
float shininess;
};
这也是由老师提供的。
如何实现这一目标?
我尝试在加载纹理图像时调用一个新线程,但是没有加载任何内容。
英文:
I'm coding a college assignment in which I need to create an openGL scene using Textures/Materials. My idea is to allow the user to decide whether to use Textures or Materials (note that this is already implemented) however, the loading of the Texture images takes too long, which makes the whole scene to hault when launching until they are all loaded up. I would like to load the images from the disk in some way (maybe threads? although I read this was not possible) while the scene is rendered with Materials, so the scene can load faster. My code for loading the Texture images looks like this:
Texture imgGold;
imgGold.initTexture("resources/textures/gold/gold.png");
And the initTexture function is provided by the teacher (we can change it tho). The Texture.cpp file looks like this:
#include "Texture.h"
//------------------------
// Crea la textura
//------------------------
void Texture::initTexture(const char *textureFile) {
// Creamos la textura a configurar
glGenTextures(1,&texture);
glBindTexture(GL_TEXTURE_2D, texture);
// Cargamos la imagen
unsigned int w, h;
unsigned char *pixels = loadTexture(textureFile, w, h);
// Creamos la textura
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void *)pixels);
glGenerateMipmap(GL_TEXTURE_2D);
// Configuramos la textura (GL_REPEAT repite textura; GL_CLAMP no, GL_LINEAR y tal mirar la teoria)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
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);
float aniso;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &aniso);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
}
//--------------------------------------------------
// Carga una textura mediante la librería Freeimage
//--------------------------------------------------
unsigned char* Texture::loadTexture(const char *textureFile, unsigned int &w, unsigned int &h) {
FreeImage_Initialise(TRUE);
// Leemos la textura
FREE_IMAGE_FORMAT format = FreeImage_GetFileType(textureFile,0);
if(format==FIF_UNKNOWN) format = FreeImage_GetFIFFromFilename(textureFile);
if((format==FIF_UNKNOWN) || !FreeImage_FIFSupportsReading(format)) {
std::cout << "Formato de la textura " << textureFile << " no está soportado." << std::endl;
std::cin.get();
exit(1);
}
FIBITMAP *texture = FreeImage_Load(format,textureFile);
if(texture==NULL) {
std::cout << "El fichero " << textureFile << " no se puede abrir." << std::endl;
std::cin.get();
exit(1);
}
FIBITMAP *temp = texture;
texture = FreeImage_ConvertTo32Bits(texture);
FreeImage_Unload(temp);
// De BGRA a RGBA
w = FreeImage_GetWidth(texture);
h = FreeImage_GetHeight(texture);
unsigned char *pixelsBGRA = (unsigned char *)FreeImage_GetBits(texture);
unsigned char *pixelsRGBA = new unsigned char[4*w*h];
for(int j=0; j<w*h; j++){
pixelsRGBA[j*4+0] = pixelsBGRA[j*4+2];
pixelsRGBA[j*4+1] = pixelsBGRA[j*4+1];
pixelsRGBA[j*4+2] = pixelsBGRA[j*4+0];
pixelsRGBA[j*4+3] = pixelsBGRA[j*4+3];
}
FreeImage_Unload(texture);
FreeImage_DeInitialise();
return pixelsRGBA;
}
//-----------------------------------------
// Devuelve el identificador de la textura
//-----------------------------------------
unsigned int Texture::getTexture() {
return texture;
}
//-----------------------
// Destructor de la clase
//-----------------------
Texture::~Texture() {
glDeleteTextures(1,&texture);
}
and Texture.h looks like this:
#ifndef TEXTURE_H
#define TEXTURE_H
#include <iostream>
#include <GL/glew.h>
#include <FreeImage.h>
class Texture {
public:
void initTexture(const char *textureFile);
unsigned int getTexture();
virtual ~Texture();
private:
unsigned int texture;
unsigned char *loadTexture(const char *textureFile, unsigned int &w, unsigned int &h);
};
#endif /* TEXTURE_H */
Note: Once loaded, the images are used to create a Textures object, which looks like this:
struct Textures {
unsigned int diffuse;
unsigned int specular;
unsigned int emissive;
unsigned int normal;
float shininess;
};
This was also provided by our teacher.
How can I achieve this?
I tried calling a new thread when I load the Texture images but nothing was loaded.
答案1
得分: 1
为了使用多线程,您将需要将多个上下文绑定(设为当前)到特定的线程。
大多数OpenGL对象(纹理是其中之一)可以在不同的上下文之间共享。
在EGL中,您将使用以下操作:
创建(共享)上下文
EGLContext eglCreateContext(
EGLDisplay display,
EGLConfig config,
EGLContext share_context,
EGLint const * attrib_list
);
share_context
指定另一个EGL渲染上下文,用于共享数据,其定义由与上下文对应的客户端API确定。与share_context共享数据的所有其他上下文也将共享数据。EGL_NO_CONTEXT表示不共享。
使上下文在线程上下文中为当前
EGLBoolean eglMakeCurrent(
EGLDisplay display,
EGLSurface draw,
EGLSurface read,
EGLContext context
);
之后,您可以在一个线程/上下文中创建/加载纹理,然后在另一个线程/上下文中使用它。
如果您无法使用多个上下文,那么可以从另一个线程中加载图像数据(例如从磁盘)然后在上下文绑定的线程中创建纹理并上传数据。
在您的情况下,这意味着您从另一个线程中调用loadTexture
函数,然后将其余部分留在绑定上下文的线程中。
参考链接
英文:
In order to use multiple threads, you'll need multiple contexts bound (make current) to a specific thread.
Most of the OpenGL objects (textures are one of them) can be shared among different contexts.
In EGL you would use the following operations:
To create a (shared) context
EGLContext eglCreateContext(
EGLDisplay display,
EGLConfig config,
EGLContext share_context,
EGLint const * attrib_list
);
> share_context
>
> Specifies another EGL rendering context with which to share data, as defined by the client API corresponding to the contexts. Data is also shared with all other contexts with which share_context shares data. EGL_NO_CONTEXT indicates that no sharing is to take place.
To make a context current on a thread
EGLBoolean eglMakeCurrent(
EGLDisplay display,
EGLSurface draw,
EGLSurface read,
EGLContext context
);
After that you're free to create/load a texture in one thread/context and use it in another thread/context.
If you're not able to use multiple contexts, then load the image data (e.g. from disk) from another thread and then create a texture and upload the data in the thread, where the context is bound to.
In your case, that would mean, that you call the loadTexture
function from another thread and leave the rest in the thread where the context is bound to.
Reference
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论