使用 ASSIMP 和 OpenGL 加载 3D 模型时纹理不正确

Posted

技术标签:

【中文标题】使用 ASSIMP 和 OpenGL 加载 3D 模型时纹理不正确【英文标题】:Incorrect texture in 3d model loading using ASSIMP & OpenGL 【发布时间】:2011-10-30 03:26:48 【问题描述】:

我正在尝试通过使用 GLUT 而不是 WGL 来修改用于加载 ASSIMP 的 sample code 中包含的 3d 模型的示例代码。但是,我遇到了纹理问题,如下图所示:

虽然应该如下图所示:

以及绘制3d模型的代码如下:

void recursive_render (const struct aiScene *sc, const struct aiNode* nd, float scale)
unsigned int i;
unsigned int n=0, t;
struct aiMatrix4x4 m = nd->mTransformation;
m.Scaling(aiVector3D(scale, scale, scale), m);
// update transform
m.Transpose();
glPushMatrix();
glMultMatrixf((float*)&m);
// draw all meshes assigned to this node
for (; n < nd->mNumMeshes; ++n)
    const struct aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]];
    apply_material(sc->mMaterials[mesh->mMaterialIndex]);
    if(mesh->mNormals == NULL)
        glDisable(GL_LIGHTING);
    
    else 
        glEnable(GL_LIGHTING);
    
    if(mesh->mColors[0] != NULL) 
        glEnable(GL_COLOR_MATERIAL);
    
    else 
        glDisable(GL_COLOR_MATERIAL);
    

    for (t = 0; t < mesh->mNumFaces; ++t) 
        const struct aiFace* face = &mesh->mFaces[t];
        GLenum face_mode;
        switch(face->mNumIndices) 
            case 1: face_mode = GL_POINTS; break;
            case 2: face_mode = GL_LINES; break;
            case 3: face_mode = GL_TRIANGLES; break;
            default: face_mode = GL_POLYGON; break;
        
        glBegin(face_mode);
        for(i = 0; i < face->mNumIndices; i++)
            int vertexIndex = face->mIndices[i];    // get group index for current index
            if(mesh->mColors[0] != NULL)
                Color4f(&mesh->mColors[0][vertexIndex]); 
            if(mesh->mNormals != NULL)
                if(mesh->HasTextureCoords(0))  
                    glTexCoord2f(mesh->mTextureCoords[0][vertexIndex].x, 1- mesh->mTextureCoords[0][vertexIndex].y);                    
                
                glNormal3fv(&mesh->mNormals[vertexIndex].x);
                glVertex3fv(&mesh->mVertices[vertexIndex].x);
        
        glEnd();
    

// draw all children
for (n = 0; n < nd->mNumChildren; ++n)  
    recursive_render(sc, nd->mChildren[n], scale);

glPopMatrix();

apply_material函数,与ASSIMP提供的样例几乎一模一样

void apply_material(const struct aiMaterial *mtl)

float c[4];
GLenum fill_mode;
int ret1, ret2;
struct aiColor4D diffuse;
struct aiColor4D specular;
struct aiColor4D ambient;
struct aiColor4D emission;
float shininess, strength;
int two_sided;
int wireframe;
unsigned int max;   // changed: to unsigned
int texIndex = 0;
aiString texPath;   //contains filename of texture
if(AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE, texIndex, &texPath))    
    unsigned int texId = textureIdMap[texPath.data];
    glBindTexture(GL_TEXTURE_2D, texId);


set_float4(c, 0.8f, 0.8f, 0.8f, 1.0f);
if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &diffuse))
    color4_to_float4(&diffuse, c);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, c);
set_float4(c, 0.2f, 0.2f, 0.2f, 1.0f);
if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &ambient))
    color4_to_float4(&ambient, c);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, c);
set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &specular))
    color4_to_float4(&specular, c);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, c);
set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &emission)) 
    color4_to_float4(&emission, c);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, c);

max = 1;
ret1 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS, &shininess, &max);
max = 1;
ret2 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS_STRENGTH, &strength, &max);
if((ret1 == AI_SUCCESS) && (ret2 == AI_SUCCESS))
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess * strength);
else 
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0f);
    set_float4(c, 0.0f, 0.0f, 0.0f, 0.0f);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, c);


max = 1;
if(AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_ENABLE_WIREFRAME, &wireframe, &max))
    fill_mode = wireframe ? GL_LINE : GL_FILL;
else
    fill_mode = GL_FILL;
glPolygonMode(GL_FRONT_AND_BACK, fill_mode);

max = 1;
if((AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_TWOSIDED, &two_sided, &max)) && two_sided)
    glEnable(GL_CULL_FACE);
else
    glDisable(GL_CULL_FACE);

还有loadGLtextures函数,我不认为它与剔除困难有关。

int LoadGLTextures(const aiScene* scene) 
ILboolean success;
/* initialization of DevIL */
ilInit(); 
/* scan scene's materials for textures */
for (unsigned int m=0; m<scene->mNumMaterials; ++m) 
    int texIndex = 0;
    aiString path;  // filename
    aiReturn texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
    while (texFound == AI_SUCCESS) 
        //fill map with textures, OpenGL image ids set to 0
        textureIdMap[path.data] = 0; 
        // more textures?
        texIndex++;
        texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
    


int numTextures = textureIdMap.size();
/* create and fill array with DevIL texture ids */
ILuint* imageIds = new ILuint[numTextures];
ilGenImages(numTextures, imageIds); 
/* create and fill array with GL texture ids */
GLuint* textureIds = new GLuint[numTextures];
glGenTextures(numTextures, textureIds); /* Texture name generation */

/* get iterator */
std::map<std::string, GLuint>::iterator itr = textureIdMap.begin();
printf("TextureIDMap Begin %i\n", textureIdMap.begin());
int i=0;
for (; itr != textureIdMap.end(); ++i, ++itr) 
    //save IL image ID
    std::string filename = (*itr).first;  // get filename
    (*itr).second = textureIds[i];    // save texture id for filename in map
    printf("Texture loaded: %s\n",filename.c_str());
    printf("Texture ID Map End: %i\n",textureIdMap.end());
    ilBindImage(imageIds[i]); /* Binding of DevIL image name */
    ilEnable(IL_ORIGIN_SET);
    ilOriginFunc(IL_ORIGIN_LOWER_LEFT); 
    success = ilLoadImage((ILstring)filename.c_str());

    if (success) 
        /* Convert image to RGBA */
        ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE); 

        /* Create and load textures to OpenGL */
        glBindTexture(GL_TEXTURE_2D, textureIds[i]); 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ilGetInteger(IL_IMAGE_WIDTH),
            ilGetInteger(IL_IMAGE_HEIGHT), 0, GL_RGBA, GL_UNSIGNED_BYTE,
            ilGetData()); 
    
    else 
        printf("Couldn't load Image: %s\n", filename.c_str());

/* Because we have already copied image data into texture data  we can release memory used by image. */
ilDeleteImages(numTextures, imageIds); 
//Cleanup
delete [] imageIds;
delete [] textureIds;
//return success;
return true;

Lighthouse 3D 已经为此提供了an example,但是,目前我无法在我的程序中实现 GLSL 和 VAO。有什么解决办法吗?提前致谢。

【问题讨论】:

我可能错了,但问题可能不在于纹理,而在于材料。 您知道什么可能导致材料出现问题吗?谢谢.. 我尝试过 Lighthouse 3D 的教程,但无法加载模型 【参考方案1】:

我找到了解决方法。我使用以下代码更改了如何在recursive_render 函数中访问纹理:

glTexCoord2f(mesh->mTextureCoords[0][vertexIndex].x,  mesh->mTextureCoords[0][vertexIndex].y);

代替:

glTexCoord2f(mesh->mTextureCoords[0][vertexIndex].x,  1-mesh->mTextureCoords[0][vertexIndex].y);

【讨论】:

我将 Assimp 与 GLSL 一起使用,我提供给 Assimp 的模型中的 UV 纹理仅在我使用 glTexCoord2f(mesh-&gt;mTextureCoords[0][vertexIndex].x, 1-(mesh-&gt;mTextureCoords[0][vertexIndex].y)); 时才有效,否则我会得到纹理的奇怪行为。我认为只有少数模型需要 1-y【参考方案2】:

这不是纹理的问题。您的问题来自背面剔除(至少看起来是这样,因为您可以看到鸭子内部)。您的多边形以错误的顺序缠绕,或者您的背面剔除设置不正确。如果您发布设置背面剔除的代码,我们可以确切地看到问题所在。

您的一些法线也可能朝内(这也可能是多边形缠绕的结果)。这可以解释为什么你的鸭嘴是漆黑的。

【讨论】:

感谢您的回答。启用/禁用剔除设置在函数apply_material 的末尾。我不确定问题是否与剔除有关,因为即使我禁用了剔除,纹理仍然无法正确加载。 (CMIIW)。我已经编辑了代码。 您是否尝试过完全禁用剔除? IE。删除 if 语句,只放 glDisable(GL_CULL_FACE)?【参考方案3】:

我很确定问题是纹理正在沿 Y 轴“翻转”。这就是您的“1-y”有效的原因。它可以通过在加载时沿 Y 翻转纹理来修复。虽然我还不确定为什么今天才偶然发现这个问题。

【讨论】:

【参考方案4】:

我不确定这是否有帮助,但您可以在导入模型时翻转 UV 坐标 const aiScene* scene = importer.ReadFile(path, aiProcess_FlipUVs);

【讨论】:

以上是关于使用 ASSIMP 和 OpenGL 加载 3D 模型时纹理不正确的主要内容,如果未能解决你的问题,请参考以下文章

我的OpenGL学习进阶之旅Assimp库支持哪些3D模型格式?

渲染在 OpenGL 中使用 Assimp 加载的动画模型时折叠的网格

我的OpenGL学习进阶之旅解决使用Assimp和OpenGL进行在屏渲染和离屏渲染FBO时绘制3D模型出现各种诡异的模型渲染画面问题,都是血和泪的教训啊!!!

Assimp 和 D3D 模型加载:网格未在 D3D 中显示

通过 OpenGL Assimp 加载的对象有时会反转法线

带有 Assimp 的 OpenGL (GLSL) 中的骨骼动画