[PA 2] Object Picker - Bunny 만들기
Category: Computer Graphics
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 모델을 띄우고 클릭했을 때 토끼의 색깔을 바꾸는 코드를 짜는 과제
- 먼저 mesh 모델을 받아와서 띄우고 -> front buffer, back buffer로 나누어서 받기
- 클릭할 때마다 픽셀 정보를 받아오기
- bunny 색과 같은 경우 -> 다른 픽셀 값을 적용
Result 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);
}