[PA 2] Object Picker - Bunny 만들기

Update:     Updated:

Category:

Tags:



Task Lists

  • Load and draw mesh [9 Points]

    Do not read your mesh with absolute path. If you read your mesh with absolute path, it would not run in my system. (Execution fail, Your score will be 0)

  • Implement picking with front and back buffer method [9 Points]

    • Change color when you only pick the surface of bunny
    • Draw the rendering result in the front buffer
    • Draw the image of object id in the back buffer
    • Read the pixel value of back buffer when you click the image and identify what object is under the cursor.


정리하면, bunny 모델을 띄우고 클릭했을 때 토끼의 색깔을 바꾸는 코드를 짜는 과제

  1. 먼저 mesh 모델을 받아와서 띄우고 -> front buffer, back buffer로 나누어서 받기
  2. 클릭할 때마다 픽셀 정보를 받아오기
  3. bunny 색과 같은 경우 -> 다른 픽셀 값을 적용



Result Image

원래는 흰색에서 토끼 영역을 클릭하면 분홍색으로 바뀐다.

image



Code Implementation

tinyobj의 경우, 참고할 수 있는 리소스가 많이 없어서 어려웠다. 모델 불러오는 것부터 많이 막혔었는데 여기서 도움이 된 건 tinyobj 원본 코드 파일 주석을 참고한 거랑 헤더 파일에서 함수 원형을 뜯어보는게 제일 이해가 빠른 것 같다.

#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <tinyobjloader/tiny_obj_loader.h>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>

double red = 1.0;
double green = 1.0;
double blue = 1.0;
double xpos, ypos;
float pixels[3];

// Window dimensions
const GLuint WIDTH = 1280, HEIGHT = 720;

// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mode){
    glfwGetCursorPos(window, &xpos, &ypos);
    
    glReadBuffer(GL_BACK);
    glReadPixels(xpos, HEIGHT-ypos, 1, 1, GL_RGB, GL_FLOAT, pixels);
    
    if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS){
        if (pixels[0] == 0.0f && pixels[1] == 0.0f && pixels[2] == 0.0f){
            red = 220.f/255.f, green = 81.f/255.f, blue = 120.f/255.f;
        }
        else {
            red = 1.0, green = 1.0, blue = 1.0;
        }
    }
}
// The MAIN function, from here we start the application and run the game loop
int main()
{
    std::cout << "Starting GLFW context, OpenGL 3.1" << std::endl;
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "glskeleton", NULL, NULL);
    glfwMakeContextCurrent(window);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetMouseButtonCallback(window, mouse_button_callback);

    if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress))
    {
        std::cout << "Failed to initialize OpenGL context" << std::endl;
        return -1;
    }
    // load model
    tinyobj::attrib_t attr;
    std::vector< tinyobj::shape_t > shaps;
    std::vector< tinyobj::material_t > mats;
    std::string inputfile = "bunny.obj";
    std::string warn;
    std::string err;

    bool ret = tinyobj::LoadObj(&attr, &shaps, &mats, &warn, &err, inputfile.c_str());
      
    if (!err.empty()) { // `err` may contain warning message.
      std::cerr << err << std::endl;
    }

    if (!ret) {
      exit(1);
    }
    

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);
    
    glm::mat4 matModel1 = glm::identity<glm::mat4>(); //4x4 identity matrix
    glm::mat4 matModel2 = glm::identity<glm::mat4>(); //4x4 identity matrix
    glm::mat4 matView = glm::lookAt(glm::vec3(0, 4, 4), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));
    glm::mat4 matProj = glm::perspective(glm::radians(60.0f), (float)WIDTH/HEIGHT, 0.1f, 100.0f);

    // Render loop
    while (!glfwWindowShouldClose(window))
    {
        glfwWaitEvents(); //waits for input
        
        // Check if any events have been activated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();
        
        // Render
        // Clear the colorbuffer
        glDrawBuffer(GL_FRONT);
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        
        // set projection matrix for this frame
        glMatrixMode(GL_PROJECTION); // set projection matrix
        // use either of following lines to set the value of projection matrix
        glLoadMatrixf(glm::value_ptr(matProj)); // you should include glm/gtc/type_ptr.hpp for glm::value_ptr
        glLoadMatrixf(&matProj[0][0]); // you can use this also.
        
        // set model view matrix for the model1
        glm::mat4 modelView1 = matView * matModel1;
        glMatrixMode(GL_MODELVIEW);
        glLoadMatrixf(glm::value_ptr(modelView1));
        
        glBegin(GL_TRIANGLES);
        glColor3d(red, green, blue);
        // draw
        for (size_t s = 0; s < shaps.size(); s++) {

            size_t idx_offset = 0;
            
            for (size_t i = 0; i < shaps[s].mesh.num_face_vertices.size(); i++) {
                
                int offset = shaps[s].mesh.num_face_vertices[i];
                
                for (size_t j = 0; j < offset; j++) {
                    // access to vertex
                    tinyobj::index_t idx = shaps[s].mesh.indices[idx_offset + j];
                    tinyobj::real_t x = attr.vertices[3*idx.vertex_index+0];
                    tinyobj::real_t y = attr.vertices[3*idx.vertex_index+1];
                    tinyobj::real_t z = attr.vertices[3*idx.vertex_index+2];
                    glVertex3f(x, y, z);
                }
                idx_offset += offset;
            }
        }
        glEnd();
        glFlush();
        
        glDrawBuffer(GL_BACK);
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        
        // set projection matrix for this frame
        glMatrixMode(GL_PROJECTION); // set projection matrix
        // use either of following lines to set the value of projection matrix
        glLoadMatrixf(glm::value_ptr(matProj)); // you should include glm/gtc/type_ptr.hpp for glm::value_ptr
        glLoadMatrixf(&matProj[0][0]); // you can use this also.
        
        // set model view matrix for the model1
        glm::mat4 modelView2 = matView * matModel1;
        glMatrixMode(GL_MODELVIEW);
        glLoadMatrixf(glm::value_ptr(modelView2));
        
        glBegin(GL_TRIANGLES);
        glColor3d(0.0, 0.0, 0.0);
        // draw
        for (size_t s = 0; s < shaps.size(); s++) {

            size_t idx_offset = 0;
            
            for (size_t i = 0; i < shaps[s].mesh.num_face_vertices.size(); i++) {
                
                int offset = shaps[s].mesh.num_face_vertices[i];
                
                for (size_t j = 0; j < offset; j++) {
                    tinyobj::index_t idx = shaps[s].mesh.indices[idx_offset + j];
                    tinyobj::real_t x = attr.vertices[3*idx.vertex_index+0];
                    tinyobj::real_t y = attr.vertices[3*idx.vertex_index+1];
                    tinyobj::real_t z = attr.vertices[3*idx.vertex_index+2];
                    glVertex3f(x, y, z);
                }
                idx_offset += offset;
            }
        }
        glEnd();
        glFlush();
        // Swap the screen buffers
        glFinish();
    }

    // Terminates GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}






맨 위로 이동하기