david
david

Reputation: 103

OpenGL 3D model texture mapping

I am trying to render an obj model with texture. Here is what I do:

  1. Get the 3d model model and the corresponding view matrix view_mat and projection matrix proj_mat from the image.
  2. Project the 3d model to the image using proj_mat * view_mat * model, in this way I can get uv coordinates in the image for every vertex in 3d model.
  3. Use the uv coordinates to render the 3d model.

Here is what I get (on the left is the render result), I think I should get the main steps done right, as the overall texture looks in the right position. But it looks like that triangles are not in the rotation mode.

Here is the part of the code I consider that is related to the texture mapping.

int main() {
    Tracker tracker;
    tracker.set_image("clooney.jpg");
    Viewer viewer(tracker.get_width(), tracker.get_height());
    while (tracker.track()) {
        Model* model = tracker.get_model();
        glm::mat4x4 proj_mat = tracker.get_proj_mat();
        proj_mat = glm::transpose(proj_mat);
        glm::mat4x4 view_mat = tracker.get_view_mat();
        view_mat = glm::transpose(view_mat);
        // render 3d shape
        viewer.set_model(model);
        viewer.draw(view_mat, proj_mat);
        waitKey(0);
    }
    return 0;
}
// initialization of the render part
Viewer::Viewer(int width, int height) {
    glfwInit();
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
    m_window = glfwCreateWindow(width, height, "demo", NULL, NULL);
    if (!m_window)
    {
        fprintf(stderr, "Failed to open GLFW window\n");
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwMakeContextCurrent(m_window);
    glfwGetWindowSize(m_window, &m_width, &m_height);
    glfwSetFramebufferSizeCallback(m_window, reshape_callback);
    gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
    glfwSwapInterval(1);
    config();
}

void Viewer::config() {
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glDisable(GL_CULL_FACE);
    glShadeModel(GL_FLAT);
}

// entry of the drawing function
void Viewer::draw(glm::mat4x4 view_mat, glm::mat4x4 proj_mat) {
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    proj_mat = glm::transpose(proj_mat);
    glLoadMatrixf(&proj_mat[0][0]);
    glMatrixMode(GL_MODELVIEW);
    view_mat = glm::transpose(view_mat);
    glLoadMatrixf(&view_mat[0][0]);
    // m_pmodel is an instance of Model Class
    // set texture
    m_pmodel->set_texture(m_image);
    // set model uvs
    m_pmodel->set_uvs(view_mat, proj_mat);
    m_pmodel->draw(); 
    glfwSwapBuffers(m_window);
    glfwPollEvents();
}
// set the texture for the model from the image
void Model::set_texture(cv::Mat img) {
    glGenTextures(1, &m_texture);
    glBindTexture(GL_TEXTURE_2D, m_texture);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, img.cols, img.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, img.data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glGenerateMipmap(GL_TEXTURE_2D);
    glEnable(GL_TEXTURE_2D);
}
// specify correspondence between image and the model
void Model::set_uvs(glm::mat4x4 view_mat, glm::mat4x4 proj_mat) {
    for (int i = 0; i < m_uvs.size(); i++) {
        glm::vec4 clip_coord = proj_mat * view_mat * glm::vec4(m_vertices[i], 1);
        float w = clip_coord.w;
        glm::vec3 normal_coord = glm::vec3(clip_coord.x, clip_coord.y, clip_coord.z) / w;
        m_uvs[i] = glm::vec2(normal_coord.x * 0.5f + 0.5f, normal_coord.y * 0.5f + 0.5f);
    }
}
// render the 3d model
void Model::draw() const {
    glBindTexture(GL_TEXTURE_2D, m_texture);
    for (unsigned long i = 0; i < m_faces.size(); ++i) {
        glm::ivec3 face = this->m_faces[i];
        glBegin(GL_TRIANGLES);
        for (int j = 0; j < 3; j++) {
            glm::vec3 v = this->m_vertices[face[j]];
            glm::vec2 uv = this->m_uvs[face[j]];
            glVertex3f(v.x, v.y, v.z);
            glTexCoord2f(1 - uv.x,1 - uv.y);
        }
        glEnd();
    }
}

Upvotes: 0

Views: 766

Answers (1)

Rabbid76
Rabbid76

Reputation: 211239

You have to set the current texture coordinate (glTexCoord) before you specify a vertex (glVertex), because the current color, normal, texture coordinates, and fog coordinate are associated with the vertex when glVertex is called.

This means you have to swap glVertex3f and glTexCoord2f:

glTexCoord2f(1 - uv.x,1 - uv.y);
glVertex3f(v.x, v.y, v.z);

Otherwise you would set the texture coordinate which is associated to the next vertex position.


See OpenGL 2.0 API Specification, 2.6 Begin/End Paradigm, page 13:

Each vertex is specified with two, three, or four coordinates. In addition, a current normal, multiple current texture coordinate sets, multiple current generic vertex attributes, current color, current secondary color, and current fog coordinate may be used in processing each vertex.

Upvotes: 1

Related Questions