2
\$\begingroup\$

I have a working cube in 3d space that has a texture wrapped to it. now when I say working it maps two of the 6 faces correctly. from what I can tell is that the texture coordinates are moved up "a block spaced (face space)". As the two textures is shows are fine but all of the other sides are white so I am thinking that it is reading the coordinates from the wrong side (aka the texture coordinates are reading 0,0 as bottom left and opengl is reading it as 0,0 is top left). I feel like that is not a thing but that is what seems to happen.

NOTE: I used blender for my creation of square with textures and and the mapping and exporting. Also note that I am using a depth buffer it that helps.

.obj file:

# Blender v2.78 (sub 0) OBJ File: 'cube.blend'
# www.blender.org
mtllib cube.mtl
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 -0.999999
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.5000 0.2500
vt 0.7500 0.5000
vt 0.5000 0.5000
vt 0.0001 0.5000
vt 0.2500 0.2500
vt 0.2500 0.5000
vt 0.9999 0.0001
vt 0.7500 0.2500
vt 0.7500 0.0001
vt 0.9999 0.5000
vt 0.7500 0.7500
vt 0.9999 0.7500
vt 0.0001 0.2500
vt 0.9999 0.2500
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 0.0000
vn 1.0000 -0.0000 0.0000
vn 0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn 0.0000 0.0000 -1.0000
usemtl cube
s off
f 2/1/1 4/2/1 1/3/1
f 8/4/2 6/5/2 5/6/2
f 5/6/3 2/1/3 1/3/3
f 6/7/4 3/8/4 2/9/4
f 3/8/5 8/10/5 4/2/5
f 1/11/6 8/10/6 5/12/6
f 2/1/1 3/8/1 4/2/1
f 8/4/2 7/13/2 6/5/2
f 5/6/3 6/5/3 2/1/3
f 6/7/4 7/14/4 3/8/4
f 3/8/5 7/14/5 8/10/5
f 1/11/6 4/2/6 8/10/6

cube texture: cube texture

texture loader:

void sky::Texture::load (const char *path) {
    if (m_loaded)
        return;
    std::cout << "load texture" << std::endl;
    int width, height, components;
    // load image to loader
    unsigned char* image_data = stbi_load(path, &width, &height, &components, 4);
    if (image_data == nullptr)
        std::cerr << "Texture loading failed: " << path << std::endl;

    // configure and bind texture to opengl
    glGenTextures(1, &m_texture);
    glBindTexture(GL_TEXTURE_2D, m_texture);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);

    stbi_image_free(image_data);
    m_loaded = true;
}

draw function

void sky::Mesh::draw () {
    if (configured) {
        m_texture.bind(0);
        glBindVertexArray(m_vao);
        glDrawArrays(GL_TRIANGLES, 0, m_vertices.size() * sizeof(Vertex));
        glBindVertexArray(0);
    }
}

vertex shader:

#version 330 core

layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 textureCoord;

out vec2 textCoord;

uniform mat4 MVP;

void main() {
    textCoord = textureCoord;
    gl_Position = MVP * vec4(position, 1);
}

fragment shader:

#version 330 core

in vec2 textCoord;
uniform sampler2D diffuse;

void main() {
    gl_FragColor = texture2D(diffuse, textCoord);
}

data initialization:

void sky::Mesh::initialize () {
    //m_vertice_count = m_vertices.size() * sizeof(m_vertices[0][0]);
    //m_normal_count = normals.size() * 3;
    std::cout << "vert size: " << m_vertices.size() * sizeof(glm::vec3) << std::endl;

    // generate the buffers/arrays
    glGenVertexArrays(1, &m_vao);
    glGenBuffers(1, &m_vbo);

    glBindVertexArray(m_vao);

    // load data to vbo
    glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
    // structs memory is sequential so that we can pack all the data per vertex into one single array and
    // inform opengl of the layout
    glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(Vertex), &m_vertices[0], GL_STATIC_DRAW);
    // set the layout of the data
    // vertex position
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
    // vertex normals
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid *)offsetof(Vertex, normal));
    // vertex texture
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid *)offsetof(Vertex, texture));

    glBindVertexArray(0);

    configured = true;
}
\$\endgroup\$
1
  • \$\begingroup\$ So i have figured out that I need to flip the v coordinate (aka y). not to sure as to the reason. I would love an explanation as to the reason. link answering problem: gamedev.stackexchange.com/questions/26601/… \$\endgroup\$
    – Matthew
    Commented May 7, 2017 at 0:55

2 Answers 2

2
\$\begingroup\$

As the previous answer correctly stated, the vertical dimensions for images in memory is inverted, compared to what you need for OpenGL.

I would suggest that you perform a row by row swap of the pixels in the image data in main memory before you send it to gpu ram.

Consider the following image:

enter image description here

If we want to access the bottom row, we go to:

pixels+( (sizeof(pixel) * w) * row_number)

And to access the final pixel of that row we use:

pixels+( (sizeof(pixel) * w) * row_number) + w * sizeof(pixel)

so, we can iterate through every row until we get halfway through the image.

we take our index and compute two start points (memory addresses)

upper_index_start = pixels+( (sizeof(pixel) * w) * row_number);

row_index = H - row_number;
lower_index_start = pixels+( (sizeof(pixel) * w) * row_index);

We now have all the information we need. Now, using memcpy we can do a row for row swap:

temp = new pixel[w];

memcpy(temp, upper_row_start, w*sizeof(pixel));

Then copy the lower row into the upper row:

memcpy(upper_row_start, lower_row_start, w*sizeof(pixel));

Then finally, copy the contents of temp to the lower row:

memcpy(lower_row_start, temp, w*sizeof(pixel));

Then clean up your temp with delete[] temp;

The following image will illustrate my point: enter image description here

In this way, you can do this row for row swap on load, and don't have to muck about with changing UV's.

\$\endgroup\$
2
  • \$\begingroup\$ that is a very interesting way of flipping an image. so from what I understand is that you want to flip the image ahead of time and not touch the uvs at all? \$\endgroup\$
    – Matthew
    Commented May 9, 2017 at 14:07
  • \$\begingroup\$ yes, that's right. Load image from disk, flip rows, send to gpu. \$\endgroup\$
    – Ian Young
    Commented May 9, 2017 at 14:45
2
\$\begingroup\$

OpenGL texture are loaded left to right, bottom to top.

Many image loaders however will store the image in memory left to right, top to bottom.

First pixel from memory is bottom-left for OpenGL.

For example:

uint8_t image_data[] = {
    0, 0, 0, 0
    1, 1, 1, 1
    2, 2, 2, 2
    3, 3, 3, 3
};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);

Will load the texture as:

top-left         top-right
{2, 2, 2, 2}, {3, 3, 3, 3}
{0, 0, 0, 0}, {1, 1, 1, 1}
bottom-left   bottom-right

Where bottom left is UV=(0,0) and top-right UV=(1, 1)

\$\endgroup\$
2
  • \$\begingroup\$ so I was correct in flipping the v and is there a setting in blender so that I do not have to do this? I feel like flipping it could result in errors as I am assuming not every editor does it different? \$\endgroup\$
    – Matthew
    Commented May 7, 2017 at 1:45
  • \$\begingroup\$ Thanks. For future readers (that it loads bottom to top) : khronos.org/registry/OpenGL-Refpages/gl4/html/… \$\endgroup\$ Commented Jan 8, 2019 at 6:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .