[2023 동계 모각소] 알구자구 1주차

jungizz_·2024년 1월 13일
0

모각소

목록 보기
7/12
post-thumbnail

📝 1주차

  • Opengl에서 창 띄우기
  • Opengl에서 Obj파일 불러오기

1. window 창 띄우기

✔️ main.cpp

  • 해상도와 타이틀을 설정하여 창 객체 생성

  • 창을 끄라는 명령이 나오기 전까지 while 무한루프를 돌며 프로그램을 업데이트하고 렌더링

    1. 프레임버퍼 청소
    2. 렌더링
    3. 스왑
    4. 쌓인 시스템 이벤트 처리
  • 루프가 끝나면 GLFW 정리 및 종료

#define GLEW_STATIC

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <vector>

using namespace glm;

void render(GLFWwindow* window);
void init();


int main(void) 
{
    if (!glfwInit()) exit(EXIT_FAILURE);                                    // glfw 핵심 객체 초기화
    glfwWindowHint(GLFW_SAMPLES, 8);                                        // 생성할 Window의 기본 설정
    GLFWwindow* window = glfwCreateWindow(640, 480, "Hello", NULL, NULL);   // 창 객체 생성
    
    glfwMakeContextCurrent(window);            // 생성된 창에 대응되는 opengl 컨텍스트 객체 생성        
    glewInit();                              
    glfwSwapInterval(1);                       // 스왑 간격 : 0 설정하면 fps 제한X, 1 설정하면 fps 제한 60
    while (!glfwWindowShouldClose(window)) {   // 창이 닫히기 전까지 무한루프
        render(window);
        glfwSwapBuffers(window);
        glfwPollEvents();                      // 대기 중인 이벤트 처리
    }                                          
    glfwDestroyWindow(window);                 // 루프가 끝났으므로 종료
    glfwTerminate();
}


void render(GLFWwindow* window) {
    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0, 0, width, height);
    glClearColor(0.1, 0.1, 0.1, 0);
    glEnable(GL_DEPTH_TEST);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glDrawElements(GL_TRIANGLES, triangles.size()*3, GL_UNSIGNED_INT, 0);
}

2. Assimp Library를 사용해 Obj 파일 불러오기

✔️ Assimp library의 구조

  • opengl에서 사용되는 모델 importing 라이브러리 중 하나 (Open Asset Import Library)
  • Assimp가 로드한 모델의 데이터들은 Assimp가 생성한 데이터 구조로 불러오므로 많은 종류의 모델 파일 import 가능
    • 로드한 모델의 모든 데이터를 담고 있는 Scene객체
    • 각 노드는 Scene객체에 저장된 데이터를 가짐
    • 인덱스를 포함한 노드 모음이 형성되고, 각 노드는 자식을 가질 수 있음

✔️objLoader.h

  • loadObj(): assimp의 readfile함수로 obj파일 로드
  • processNode(): Rootnode를 통해 모든 부모, 자식 노드에 접근하여 recursive하게 processMesh 함수 호출
  • processMesh(): Mesh의 vertex정보(position)와 Mesh의 face정보(triangles)를 받아옴
#pragma once

#ifndef objLoader_h
#define objLoader_h

#include <glm/glm.hpp>
#include <vector>

#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>


std::vector<glm::vec3> vertices;
std::vector<glm::u32vec3> triangles;


void processMesh(aiMesh* mesh, const aiScene* scene) 
{
    // mesh's vertex
    vertices.resize(mesh->mNumVertices);
    normals.resize(mesh->mNumVertices);
    texcoords.resize(mesh->mNumVertices);

    for (unsigned int i = 0; i < mesh->mNumVertices; ++i)
    {
        glm::vec3 vector;

        // position
        vector.x = mesh->mVertices[i].x;
        vector.y = mesh->mVertices[i].y;
        vector.z = mesh->mVertices[i].z;
        vertices[i] = vector;

        // normal . . .

        // texture coordinate . . .

    }

    // mesh's face (triangle)
    for (unsigned int i = 0; i < mesh->mNumFaces; ++i)
    {
        aiFace face = mesh->mFaces[i];
        triangles.push_back(glm::u32vec3(face.mIndices[0], face.mIndices[1], face.mIndices[2]));
    }
}

void processNode(aiNode* node, const aiScene* scene)
{
    for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
        processMesh(mesh, scene);
    }
    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
        processNode(node->mChildren[i], scene);
    }
}

bool loadObj(const std::string& filePath)
{
    Assimp::Importer importer;
    const aiScene* scene = importer.ReadFile(filePath, aiProcess_Triangulate | aiProcess_FlipUVs);

    if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
    {
        std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl;
        return 0;
    }
    processNode(scene->mRootNode, scene);
}

#endif

✔️ main.cpp

  • loadObj함수를 호출하여 모델을 로드하고, 받아온 mesh 데이터를 버퍼에 저장
    1. 버텍스 버퍼 오브젝트(VBO)에 vertex 정보 데이터를 저장
    2. 버텍스 어레이 오브젝트(VAO)에 버퍼들을 저장 (0번 공간에 vertices 정보를 가진 버퍼 저장)
    3. 엘레먼트 어레이 버퍼에 삼각형을 그릴 순서 정보 저장
  • window 창에서 렌더링 구역과 색을 설정하고 glDrawElements 함수로 렌더링
. . .

GLuint vertexBuffer = 0; // 버퍼 ID (GLuint: Opengl의 unsigned long형)
GLuint vertexArray = 0; // 버텍스어레이 ID
GLuint elementBuffer = 0;
Program program;

void init() {
    if (!loadObj("LPS_Head.obj")) {
        std::cerr << "Failed to load the model!" << std::endl;
        glfwTerminate();
        exit(EXIT_FAILURE);
    }
    program.loadShaders("shader.vert", "shader.frag");
    
    // <버텍스 정보 저장>
    // 1. Vertex Buffer Object (VBO)
    glGenBuffers(1, &vertexBuffer); // 버퍼 1개 생성
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); // 사용할 버퍼 선언(바인딩; 하이라이팅)
    glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(vec3), vertices.data(), GL_STATIC_DRAW); // 버퍼에 버텍스 정보 데이터 저장

    // 2. Vertex Array Object (VAO)
    glGenVertexArrays(1, &vertexArray); // 버텍스어레이 1개 생성
    glBindVertexArray(vertexArray); // 사용할 버텍스어레이 선언

    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glEnableVertexAttribArray(0); // 버텍스어레이 안에 버텍스 속성을 담을 0번 공간 활성화
    glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0); // 활성화한 속성을 가진 버퍼를 0번 공간에 저장 (버텍스 당 숫자 3개 x, y, z)
	
    // 3. Element Array Buffer
    glGenBuffers(1, &elementBuffer); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer); 
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangles.size() * sizeof(u32vec3), triangles.data(), GL_STATIC_DRAW);

}


void render(GLFWwindow* window) {
    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0, 0, width, height);
    glClearColor(0.1, 0.1, 0.1, 0);
    glEnable(GL_DEPTH_TEST);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    
    glUseProgram(program.programID);

    GLuint colorLocation = glGetUniformLocation(program.programID, "color");
    glUniform4fv(colorLocation, 1, value_ptr(vec4(1, 1, 0, 1)));

    glBindVertexArray(vertexArray);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
    glDrawElements(GL_TRIANGLES, triangles.size()*3, GL_UNSIGNED_INT, 0);
}

✔️ shader.frag

#version 150 core

uniform vec4 color;
out vec4 out_Color;

void main(void)
{
	out_Color = color;
}


🔎 구현 과정에서...

  • Assimp로 모델 데이터는 잘 받아온 것 같은데 모델이 렌더링되지 않는 문제

    1. 렌더링 과정의 문제인지 확인하기 위해 삼각형을 그려봤고, 삼각형도 뜨지 않음

      • 컴퓨터그래픽스 과제 코드를 참고하며 작성하다보니, 전달 원리를 잘 몰랐던 셰이더코드를 신경쓰지 않았음
      • 셰이더 코드의 입력 데이터에 데이터를 전달해주지 않아 생긴 오류
         glUseProgram(program.programID);
         GLuint colorLocation = glGetUniformLocation(program.programID, "color");
         glUniform4fv(colorLocation, 1, value_ptr(vec4(1, 1, 0, 1)));
      • 위와 같이 작성하여 입력 데이터 전달 (vertex shader의 color 입력 데이터 전달)
      • 셰이더 코드의 데이터 전달 과정을 이해할 수 있게 됨

    2. 삼각형은 뜨지만, obj 모델은 여전히 안뜸

      • loadObj.hprocessMesh() 함수에서 버텍스 정보를 가질 벡터들을 버텍스 수 만큼 resize 했는데 push_back으로 버텍스 정보들을 넣어줌.......(바보) 정작 필요한 곳에 데이터들이 들어가지 못함

      • 인덱싱으로 정보들을 넣어주고 렌더링 성공

        void processMesh(aiMesh* mesh, const aiScene* scene) 
        {
            // 버텍스 정보 벡터 resize
            vertices.resize(mesh->mNumVertices);
            normals.resize(mesh->mNumVertices);
            texcoords.resize(mesh->mNumVertices);
        
            for (unsigned int i = 0; i < mesh->mNumVertices; ++i)
            {
                glm::vec3 vector;
                vector.x = mesh->mVertices[i].x;
                vector.y = mesh->mVertices[i].y;
                vector.z = mesh->mVertices[i].z;
        
                // vertices.push_back(vector);
                vertices[i] = vector;
        
                . . .
profile
( •̀ .̫ •́ )✧

0개의 댓글