本博客以.glb格式为例,加载glb格式的3d模型,网上找了一圈,基本上都是根据OpenGL官方示例,加载.obj格式的3d模型。
下面以.obj和.glb格式的3D模型简单介绍一下。
常见的.obj格式的3D模型如下所示:纹理都已经被剥离出来了。所以在使用Assimp库加载的时候,加载了指定的路径即可。
但是.glb格式的3D模型如下所示,就只有一个glb文件,纹理嵌入到模型当中,假如我们使用Assimp库去加载的时候,能够加载出模型,但是加载出来的效果全是黑的,加载不了纹理。
加载的效果如下图所示,黑的一片。
原因分析:找不到纹理路径。
将纹理分离,保存到本地文件,加载本地纹理文件。
首先通过ReadFile,读取本地文件,返回aiScene。
获取纹理数量:scene->mNumTextures。
获取当前的纹理:aiTexture* texture = scene->mTextures[i]。
然后看一下aiTexture官网文档介绍。
/** Width of the texture, in pixels
*
* If mHeight is zero the texture is compressed in a format
* like JPEG. In this case mWidth specifies the size of the
* memory area pcData is pointing to, in bytes.
*/
unsigned int mWidth;
/** Height of the texture, in pixels
*
* If this value is zero, pcData points to an compressed texture
* in any format (e.g. JPEG).
*/
unsigned int mHeight;
/** Data of the texture.
*
* Points to an array of mWidth * mHeight aiTexel's.
* The format of the texture data is always ARGB8888 to
* make the implementation for user of the library as easy
* as possible. If mHeight = 0 this is a pointer to a memory
* buffer of size mWidth containing the compressed texture
* data. Good luck, have fun!
*/
C_STRUCT aiTexel* pcData;
char achFormatHint[ HINTMAXTEXTURELEN ]
mWidth:纹理的像素宽,如果高度为0,mWidth指定pcData指向的内存区域,以字节为单位。
mHeigth:纹理的像素高,像jpeg类型,该值为0。
pcData:纹理数据,数据的rgba值保存到这个数据里面。
achFormatHint:图片的格式,png或者jpg或者别的。
再进去看一下aiTexel结构,结构如下:包含r、g、b、a值,代表每一个像素的r、g、b、a值。
struct aiTexel {
unsigned char b,g,r,a;
#ifdef __cplusplus
//! Comparison operator
bool operator== (const aiTexel& other) const
{
return b == other.b && r == other.r &&
g == other.g && a == other.a;
}
//! Inverse comparison operator
bool operator!= (const aiTexel& other) const
{
return b != other.b || r != other.r ||
g != other.g || a != other.a;
}
//! Conversion to a floating-point 4d color
operator aiColor4D() const
{
return aiColor4D(r/255.f,g/255.f,b/255.f,a/255.f);
}
#endif // __cplusplus
} PACK_STRUCT;
?根据上面的条件,将图片保存到本地。
const aiScene *scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
qDebug() << "ERROR::ASSIMP::" << import.GetErrorString();
return;
}
directory = path.substr(0, path.find_last_of('/'));
for (int i = 0; i < scene->mNumTextures; i++)
{
aiTexture* texture = scene->mTextures[i];
char fileName[100];
sprintf(fileName, "%s/%s.%s", modelDirectory.c_str(), texture->mFilename.C_Str(), texture->achFormatHint);
QFile file(fileName);
if (file.exists())
{
m_mapPath.insert(i, QString(fileName));
continue;
}
if (!file.open(QIODevice::WriteOnly))
break;
unsigned char* buffer = new unsigned char[texture->mWidth * 4];
memset(buffer, 0, texture->mWidth * 4);
for (int x = 0; x < texture->mWidth; ++x)
{
//拷贝RGBA数据到缓冲区
int index = x * 4;
buffer[index] = texture->pcData[x].b; // Blue
buffer[index + 1] = texture->pcData[x].g; // Green
buffer[index + 2] = texture->pcData[x].r; // Red
buffer[index + 3] = texture->pcData[x].a; // Alpha
}
file.write((char*)buffer, texture->mWidth * 4);
file.close();
m_mapPath.insert(i, QString(fileName));
delete[]buffer;
}
如下图所示,将glb格式的纹理图片剥离出来保存到了本地。?
最后加载对应的纹理即可。