Search code examples
c++opengl3dtexturesperspectivecamera

Why do textures flip differently the farther they are from x coordinate in Open GL


I'm trying to make a "flipping money" scene in Open GL where each dollar is one 2d rectangle of 4 vertices. I try to map 2 textures to it of rear and front view of a dollar and at least rotate it on the y axis. I try to change the textures based on when a dollar gets flipped meaning its rigt top vertex on the x axis gets less than its center or origin. When its x gets flipped I basically make the other texture active and bind it and vice versa, so that's how I change textures (I don't know if there's a better way to do this though). I then draw each dollar rectangle with indices in a for loop so there are 10 rectangles in a scene. Here's the code for it:

Here are my vertices and indices for a rectangle as well as an array of vectors to translate the model matrix to make the dollars appear in different parts on a screen. My texture coords in x are from -1 because the rear texture gets flipped horizontally when x goes negative, so I apply GL_MIRRORED_REPEAT and when it gets flipped it just mirrors so it looks correct:

float vertices[] = {
    // positions          // texture coords
     0.5f,  0.5f, 0.0f,   -1.0f, 1.0f,   // top right
     0.5f, -0.5f, 0.0f,   -1.0f, 0.0f,   // bottom right
    -0.5f, -0.5f, 0.0f,   0.0f, 0.0f,   // bottom left
    -0.5f,  0.5f, 0.0f,   0.0f, 1.0f    // top left
};

// separate array to calculate current vertex position 
float verticesPos[] = {
    // positions          
    0.5f,  0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    -0.5f, -0.5f, 0.0f,
    -0.5f,  0.5f, 0.0f
};

// for translating model matrix and placing dollars in different parts of a screen
glm::vec3 moneyPositions[] = {
    glm::vec3(0.0f,  0.0f,  0.0f),
    glm::vec3(2.0f,  5.0f, -15.0f),
    glm::vec3(-1.5f, -2.2f, -2.5f),
    glm::vec3(-3.8f, -2.0f, -12.3f),
    glm::vec3(2.4f, -0.4f, -3.5f),
    glm::vec3(-1.7f,  3.0f, -7.5f),
    glm::vec3(1.3f, -2.0f, -2.5f),
    glm::vec3(1.5f,  2.0f, -2.5f),
    glm::vec3(1.5f,  0.2f, -1.5f),
    glm::vec3(-1.3f,  1.0f, -1.5f)
};

Before the game loop I also created view and projection(perspective) matrix:

glm::mat4 view = glm::mat4(1.0f);
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
unsigned int viewLoc = glGetUniformLocation(shaders.ID, "view");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));

glm::mat4 projection = glm::mat4(1.0f);
projection = glm::perspective(glm::radians(45.0f), 800.0f/600.0f, 0.1f, 100.0f);
unsigned int projectionLoc = glGetUniformLocation(shaders.ID, "projection");
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));

The part where I draw rectangles and bind textures:

for (int i = 0; i < 10; ++i)
{
    glm::mat4 model = glm::mat4(1.0f);
    model = glm::translate(model, moneyPositions[i]);
    model = glm::rotate(model, (float)glfwGetTime(), glm::vec3(0.0f, 1.0f, 0.0f));
    shaders.setMat4("model", &model[0][0]);

        // calculate the current vertex positions
    glm::vec3 updatedVertices[4];
    for (int j = 0; j < sizeof(verticesPos) / sizeof(float); j += 3)
    {
        glm::vec4 pos(verticesPos[j], verticesPos[j + 1], verticesPos[j + 2], 1.0f);
        glm::vec4 modelPos = model * pos;
        updatedVertices[j / 3] = glm::vec3(modelPos[0], modelPos[1], modelPos[2]);
    }

// check if right top vertex on the x axis is less than the origin and bind the rear(back) texture, otherwise bind front texture
    if (updatedVertices[0].x < moneyPositions[i].x)
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture2);
    }
    else
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
    }

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}

My question is: Why does my center dollar rectangle (at origin 0.0, 0.0, 0.0) flips textures normally without any problem and others make it with a delay, and it seems that the farther they are from the center the more delay it takes to flip the texture, even though the calculation in the if statement should be correct and the texture should change as soon as x flips related to the origin of each rectangle. Also, I noticed that on a left hand side where x is negative, the texture flips earlier than it should and on the right it flips later. I think it has something to do with perspective, because even though I rotate each rectangle with an equal angle and speed for each of them, they all seem to rotate slightly with a different speed. Here's the visual output of the program


Solution

  • I think it has something to do with perspective

    You are correct, and to quickly confirm it, you could simply change your projection matrix to an orthographic instead of a perspective:

    projection = glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);
    

    Your camera is at (0, 0, 3), looking in the negative z direction with a FOV of 45 degrees vertically (and more horizontally). When looking at an object close the left (or right) edge of the screen, the direction you look in is not just the negative z direction: because of perspective, it's something between -z and -x (or +x). If you want the textures to switch at the correct moment, you need to take this direction into account.

    Basically, for each (flat) object, you need to compute the vector between it and the camera, and switch textures when that vector is parallel to the object's surface.

    Because each of your objects is just a flat surface, you can compute a normal vector by taking the cross product of two vectors "in the surface" (mathematically: "parallel to the surface's plane"). For example, two vectors each between different pairs of vertices. Because your example is simplistic, you can also just take the 3rd column of model as the normal vector (but then you'll need to be careful if you ever change the motion of your objects).

    Once you have a normal vector (no need to normalize it), you can compute the camera-to-object vector with moneyPositions[i] - glm::vec3(0.0f, 0.0f, -3.0f). Then, take the dot product of these two vectors: if the result is positive, you are looking at one side of the object's surface; if it's negative, you are looking at the other side. Switch textures accordingly.