<p>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.</p>
<p>I exported it to some simple model-loading code and it textured it correctly.
When I used your OBJ file (after adding <code>mtllib cube.mtl</code> and a <code>map_Kd cube.png</code> to <code>cube.mtl</code>), 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.</p>
<p>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).</p>

CC BY-SA 4.0
2021-01-06T02:43:18.757Z
2021-01-06T02:43:18.757Z
jackw11111
<p>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 &quot;distorted&quot; 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?</p>
<p><a href="https://i.sstatic.net/wuDZL.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/wuDZL.png" alt="Blender" /></a></p>
<p><a href="https://i.sstatic.net/4MD8Z.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/4MD8Z.png" alt="OpenGL" /></a></p>
<p>Here's how I load the .obj file:</p>
<pre><code>void OBJLoader::load_file(const std::string&amp; filename, std::vector&lt;float&gt;&amp; vertices, std::vector&lt;float&gt;&amp; uvs, std::vector&lt;float&gt;&amp; normals, std::vector&lt;int&gt;&amp; indices)
{
std::vector&lt;float&gt; temp_uvs;
std::vector&lt;float&gt; temp_normals;

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

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

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

        if(data_type == &quot;f&quot;)
        {
            for(std::string&amp; vertex: line_split)
            {
                std::vector&lt;std::string&gt; 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() &lt; (unsigned)(v-1)*2+2)
                    uvs.resize((v-1)*2+2);
                if(normals.size() &lt; (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 &lt;&lt; &quot;Can't open: &quot; &lt;&lt; filename &lt;&lt; std::endl;
}
</code></pre>
<p>Here's the obj file:</p>
<pre><code># 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
</code></pre>
<p>And here's the png file I used for the texture:
<a href="https://i.sstatic.net/yoCbp.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/yoCbp.png" alt="texture" /></a></p>
<p>And here's how I load the texture with SDL:</p>
<pre><code>void Texture::load(const std::string&amp; 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 &lt;&lt; &quot;IMG_Load error: &quot;  &lt;&lt; IMG_GetError() &lt;&lt; std::endl;

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

glTexImage2D(GL_TEXTURE_2D, 0, gl_format, surface_texture-&gt;w, surface_texture-&gt;h, 0, gl_format, GL_UNSIGNED_BYTE, surface_texture-&gt;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
}
</code></pre>
<p>Here's the code for rendering the model:</p>
<pre><code>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&amp; vbo = vbos.back();

    glGenBuffers(1, &amp;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
}
</code></pre>

CC BY-SA 4.0
2020-12-22T23:33:37.017Z
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.
wychmaster
CC BY-SA 4.0
2020-12-23T00:07:53.127Z
@wychmaster Thanks for commenting. I followed your suggestion and I added some code.
Russel
<opengl><texture>
PNG textures render correctly in Blender, but not in OpenGL program
Russel
# PNG textures render correctly in Blender, but not in OpenGL program

(Asked by: username_0 on 2020-12-22)

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](https://i.sstatic.net/wuDZL.png)](https://i.sstatic.net/wuDZL.png)

[![OpenGL](https://i.sstatic.net/4MD8Z.png)](https://i.sstatic.net/4MD8Z.png)

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](https://i.sstatic.net/yoCbp.png)](https://i.sstatic.net/yoCbp.png)

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
}

```

**Tags:** opengl, texture

> **Comments:**
> **username_1**: 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.  
> **username_0**: @username_1 Thanks for commenting. I followed your suggestion and I added some code.  

---

## 1 answers

### Answer by username_2 on 2021-01-06. Score: 1

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).


---

