1
$\begingroup$

I've been following the OpenGL tutorials from ThinMatrix and I've been able to load and render his stall.obj model along with his stall.png texture just fine. But when I try with my own models and textures from Blender, the texture appears "distorted" and I can't seem to find the problem. I'm guessing the problem is with my texture, but I can't find anything that would cause this problem. What could be the cause of this problem?

Blender

OpenGL

Here's how I load the .obj file:

void OBJLoader::load_file(const std::string& filename, std::vector<float>& vertices, std::vector<float>& uvs, std::vector<float>& normals, std::vector<int>& indices)
{
std::vector<float> temp_uvs;
std::vector<float> temp_normals;

std::ifstream myfile(filename);
std::string line;
if(myfile.is_open())
{
    while(std::getline(myfile, line))
    {
        if(std::string("#os").find(line.front()) != std::string::npos) // Ignore lines which starts with #, o, s
            continue;

        std::vector<std::string> line_split = split(line, ' ');
        std::string data_type = line_split[0];
        line_split.erase(line_split.begin()); // Keep only numerical data

        if(data_type == "v")
        {
            store_float_in_vector(vertices, line_split);
        }
        else if(data_type == "vt")
        {
            store_float_in_vector(temp_uvs, line_split);
        }
        else if(data_type == "vn")
        {
            store_float_in_vector(temp_normals, line_split);
        }

        if(data_type == "f")
        {
            for(std::string& vertex: line_split)
            {
                std::vector<std::string> data_split = split(vertex, '/');
                int v = std::stoi(data_split[0]);
                int vt = std::stoi(data_split[1]);
                int vn = std::stoi(data_split[2]);

                if(uvs.size() < (unsigned)(v-1)*2+2)
                    uvs.resize((v-1)*2+2);
                if(normals.size() < (unsigned)(v-1)*3+3)
                    normals.resize((v-1)*3+3);

                indices.push_back(v-1); // Indices in .obj files start at 1

                uvs[(v-1)*2] =  temp_uvs[(vt-1)*2];
                uvs[(v-1)*2+1] = 1.0f - temp_uvs[(vt-1)*2+1]; // Blender ¯\_(ツ)_/¯

                normals[(v-1)*3] = temp_normals[(vn-1)*3];
                normals[(v-1)*3+1] = temp_normals[(vn-1)*3+1];
                normals[(v-1)*3+2] = temp_normals[(vn-1)*3+2];
            }
        }
    }
    myfile.close();
}
else
    std::cout << "Can't open: " << filename << std::endl;
}

Here's the obj file:

# Blender v2.82 (sub 7) OBJ File: ''
# www.blender.org
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000
vt 0.375000 1.000000
vt 0.375000 0.750000
vt 0.625000 0.000000
vt 0.375000 0.250000
vt 0.375000 0.000000
vt 0.375000 0.500000
vt 0.125000 0.750000
vt 0.125000 0.500000
vt 0.625000 0.250000
vt 0.875000 0.750000
vt 0.625000 1.000000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
s off
f 5/1/1 3/2/1 1/3/1
f 3/2/2 8/4/2 4/5/2
f 7/6/3 6/7/3 8/8/3
f 2/9/4 8/10/4 6/11/4
f 1/3/5 4/5/5 2/9/5
f 5/12/6 2/9/6 6/7/6
f 5/1/1 7/13/1 3/2/1
f 3/2/2 7/14/2 8/4/2
f 7/6/3 5/12/3 6/7/3
f 2/9/4 4/5/4 8/10/4
f 1/3/5 3/2/5 4/5/5
f 5/12/6 1/3/6 2/9/6

And here's the png file I used for the texture: texture

And here's how I load the texture with SDL:

void Texture::load(const std::string& filename)
{
glBindTexture(GL_TEXTURE_2D, texture_id);

GLenum gl_format;
SDL_Surface* surface_texture = IMG_Load(filename.c_str());
if(surface_texture == nullptr)
    std::cout << "IMG_Load error: "  << IMG_GetError() << std::endl;

if(surface_texture->format->BytesPerPixel == 4)
{
    if(surface_texture->format->Rmask == 0x000000FF)
        gl_format = GL_RGBA;
    else
        gl_format = GL_BGRA;
}
else
{
    if(surface_texture->format->Rmask == 0x000000FF)
        gl_format = GL_RGB;
    else
        gl_format = GL_BGR;
}

glTexImage2D(GL_TEXTURE_2D, 0, gl_format, surface_texture->w, surface_texture->h, 0, gl_format, GL_UNSIGNED_BYTE, surface_texture->pixels);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

SDL_FreeSurface(surface_texture); // Free SDL_Surface memory
glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture
}

Here's the code for rendering the model:

void Model::create_EBO(int indices[], size_t s_indices)
{
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, s_indices, indices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // Unbind buffer
}

void Model::create_VBO(float data[], size_t s_data, unsigned int index, unsigned int coord_format)
{
    vbos.push_back(0); // Declare new vbo
    GLuint& vbo = vbos.back();

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, s_data, data, GL_STATIC_DRAW);
    glVertexAttribPointer(index, coord_format, GL_FLOAT, GL_FALSE, 0, (void*)0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind buffer
}

void Model::load(float vertices[], size_t s_vertices, float uvs[], size_t s_uvs, float normals[], size_t s_normals, int indices[], size_t s_indices)
{
    glBindVertexArray(vao);

    create_EBO(indices, s_indices);

    create_VBO(vertices, s_vertices, 0, 3);
    create_VBO(uvs, s_uvs, 1, 2);
    create_VBO(normals, s_normals, 2, 3);

    glBindVertexArray(0); // Unbind VAO and associated VBOs

    vertex_count = s_indices/sizeof(indices[0]);
}

void Model::render()
{
    glBindVertexArray(vao);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glDrawElements(GL_TRIANGLES, vertex_count, GL_UNSIGNED_INT, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // Unbind EBO
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
    glBindVertexArray(0); // Unbind VAO
}
$\endgroup$
2
  • $\begingroup$ It's hard to tell what is going wrong without seeing the code. We need to know how you load your models and how you render them. To me, it looks like one of your problems is messed up texture coordinates. $\endgroup$
    – wychmaster
    Dec 22, 2020 at 23:33
  • $\begingroup$ @wychmaster Thanks for commenting. I followed your suggestion and I added some code. $\endgroup$
    – Russel
    Dec 23, 2020 at 0:07

1 Answer 1

1
$\begingroup$

I exported an example from Blender to replicate the issue, with the most basic project (unwrap, simple image texture in shader editor) and found it looked exactly like what your expected image shows.

I exported it to some simple model-loading code and it textured it correctly. When I used your OBJ file (after adding mtllib cube.mtl and a map_Kd cube.png to cube.mtl), it was showing the similar texture problems. Then,I saw that the working OBJ file was generating less faces (didn't have triangulate vertices checked) and the UVs were generated differently.

Alas, I would say it is an issue with how you process your vertices, whether importing as quads or triangles, and would suggest having a look at that (either leaving triangulate vertices unchecked or fixing the code).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.