Transformation_2

대인공·2022년 8월 9일
0

OpenGL

목록 보기
6/8
post-thumbnail
  • C++ 행렬 / 벡터 연산
    - GLSL의 경우 내부적으로 행렬 및 벡터와 관련된 다양한 기능 및 내부 함수를 제공한다
    - C++에는 기본적인 수학적 연산 외에 선형대수 관련기능을 제공하지 않는다.
    -> 라이브러리를 활용하면 된다.

c++ 선형대수 라이브러리

  • Eigen3 : 가장 많이 사용되는 c++선형대수 라이브러리
    - 일반적인 n차원 선형대수 연산

  • GLM : OpenGL Math 라이브러리
    - 3D 그래픽스에 필요한 4차원 선형대수 연산

GLM Dependency

  • 코드 추가 , Dependency.cmake 작성
# glm
ExternalProject_Add(
  dep_glm
  GIT_REPOSITORY "https://github.com/g-truc/glm" // 라이브러리가 있는곳
  GIT_TAG "0.9.9.8"
  GIT_SHALLOW 1
  UPDATE_COMMAND ""
  PATCH_COMMAND ""
  CONFIGURE_COMMAND ""
  BUILD_COMMAND ""
  TEST_COMMAND ""
  INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory
    ${PROJECT_BINARY_DIR}/dep_glm-prefix/src/dep_glm/glm
    ${DEP_INSTALL_DIR}/include/glm
  )
set(DEP_LIST ${DEP_LIST} dep_glm)
  • glm 라이브러리 헤더 추가 , src / common.h 작성
...
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
  • GLM Test , src / context.cpp / Init() 작성
// 위치 (1, 0, 0)의 점. 동차좌표계 사용
glm::vec4 vec(1.0f,  0.0f, 0.0f, 1.0f);
// 단위행렬 기준 (1, 1, 0)만큼 평행이동하는 행렬
auto trans = glm::translate(glm::mat4(1.0f), glm::vec3(1.0f, 1.0f, 0.0f));
// 단위행렬 기준 z축으로 90도만큼 회전하는 행렬
auto rot = glm::rotate(glm::mat4(1.0f),
  glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
// 단위행렬 기준 모든 축에 대해 3배율 확대하는 행렬
auto scale = glm::scale(glm::mat4(1.0f), glm::vec3(3.0f));
// 확대 -> 회전 -> 평행이동 순으로 점에 선형 변환 적용
vec = trans * rot * scale * vec;
SPDLOG_INFO("transformed vec: [{}, {}, {}]", vec.x, vec.y, vec.z);

- Log : transformed vec: [0.9999999, 4, 0]

Vertex Transformation

  • 정점에 대한 변환의 일반적이 방식
    - VBO상의 정점은 고정
    - vertex shader에서 변환 행렬을 uniform으로 입력
    - vertex shader 내에서 행렬곱 계산

  • 코드 수정 , shader / texture.vs 작성
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
 
uniform mat4 transform; // 추가
 
out vec4 vertexColor;
out vec2 texCoord;
 
void main() {
    gl_Position = transform * vec4(aPos, 1.0); // 추가
    vertexColor = vec4(aColor, 1.0);
    texCoord = aTexCoord;
}
  • uniform값 전달 , src / context.cpp / Init() 작성
// 0.5배 축소후 z축으로 90도 회전하는 행렬
auto transform = glm::rotate(glm::scale(glm::mat4(1.0f), glm::vec3(0.5f)), glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
auto transformLocation = glGetUniformLocation(m_program->Get(), "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));

Coordinate System

  • 좌표계
    - 어떤 정점의 위치를 기술하기 위한 기준
    - 선형 변환은 한 좌표계로 기술된 벡터를 다른 좌표계에 대해 기술하는 변환으로 해석할 수 있다.

    • 설명
    1. 기준이 되는 전역 좌표계 W
      v = [5,4]



    1. 좌표계W와 [3,3]만큼 떨어져 있는 지역 좌표계 L
      L을 기준으로 한 v = [2,1]



    1. [2,1]을 [3,3]만큼 평행이동 시킨다
      -> L을 기준으로 [2,1]에 위치한 점은 W를 기준으로 [3,3]만큼 평행이동되어 있다.
      -> 지역좌표계 L에 찍혀있는 점은 아래로 나타낼 수 있다.

      (전역좌표계 W기준 v의 위치) =
      (전역좌표계 W기준 지역좌표계L의 원점의 위치) + (지역좌표계 기준 v의 위치)



    1. 좌표계 W와 [5,2]만큼 떨어져 있고 z축 방향으로 45도 회전한 지역좌표계 L
      -> L을 기준으로 기술된 v의 위치 = [1.414... , 1.414...]

  • 자주 사용되는 좌표계 용어
    - World space : 항상 기준이 되는 좌표계 (전역 좌표계)
    - Local (Object) space : 그 오브젝트를 기준으로한 좌표계
    - View (Eye) space : 바라보고있는 는의 위치를 기준으로한 좌표계
    - Screen space : 화면을 기준으로한 좌표계

좌표공간의 변환

  • OpenGL의 그림이 그려지는 공간은 Normalized된 공간이다.
    - [-1,1]범위로 Normalized된 공간
    - Canonical space

  • Object들은 Local space를 기준으로 기술
    - Local space -> World space -> View space -> Canonical space의 순으로 변환

  • Transform Metrix
    - Model Metrix : Local을 World로
    - View Metrix : World를 Camera로
    - Projection Metrix : Camera를 Canonical로
    - Clip space에서 [-1,1]범위 밖으로 벗어난 면들은 Clipping해서 안나오게 함


* Local space -> World space -> View space 를 묶어서 Model View Transform이라고 한다.
* Model Metrix, View Metrix, Progection Metrix 이 세개를 묶어 MVP Metrix라고 한다.

Orthogonal Projection

  • 직교 투영
    - 원근감 없이 평행한 선이 계속 평행하도록 투영하는 방식
    - 설계도면 등을 그려낼때 유용하다.

    - 6개의 파라미터 : left, right, bottom, top, near, far
    - z축에 -1 : Clip space 이후에는 오른손 좌표계에서 왼손 좌표계로 변경

    • 오른손 좌표계 / 왼손 좌표계
      - x축, y축을 화면의 오른 / 위 방향으로 했을때
      - 오른손 좌표계 : z축이 화면에서부터 나오는 방향
      - 왼손 좌표계 : z축이 화면으로 들어가는 방향



  • 원근 투영
    - 변환 이전에 평행한 선이 변환 후에 한점(소실점)에서 만남
    - 멀리 있을수록 물체가 작아져 원근감이 발생

    - 4개의 파라미터 : 종횡비(Aspect Ratio), 화각(Field Of View), near, far



  • 모든 변환의 조합
    - Local space를 기준으로 한 좌표 V의 Clip space에서의 좌표는?

    위에 써놓았듯이 MVP (model-view-projection) matrix라고 한다.



3D Transformation

  • 코드 수정 , src / context.cpp / Init() 작성
// x축으로 -55도 회전
auto model = glm::rotate(glm::mat4(1.0f), glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
// 카메라는 원점으로부터 z축 방향으로 -3만큼 떨어짐
auto view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -3.0f));
// 종횡비 4:3, 세로화각 45도의 원근 투영
auto projection = glm::perspective(glm::radians(45.0f), (float)640 / (float)480, 0.01f, 10.0f);
auto transform = projection * view * model;
auto transformLocation = glGetUniformLocation(m_program->Get(), "transform");
  • 수정
auto projection = glm::perspective(glm::radians(45.0f), (float)640 / (float)480, 0.01f, 10.0f); 

윈도우의 크기를 지정해주어도 되지만 cMakeList.txt에 있는 이미 지정된 Window Size인

set(WINDOW_WIDTH XXX)
set(WINDOW_HEIGHT XXX)

를 사용하여

auto projection = glm::perspective(glm::radians(45.0f), (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.01f, >10.0f); 

로 수정해야지 제대로된 종횡비 값이 지정된다.

큐브 만들기

  • 코드 수정 , src / context.cpp / Init() 작성
	float vertices[] = 
    {
 	-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
 	0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
 	0.5f,  0.5f, -0.5f, 1.0f, 1.0f,
 	-0.5f,  0.5f, -0.5f, 0.0f, 1.0f,

  	-0.5f, -0.5f,  0.5f, 0.0f, 0.0f,
 	0.5f, -0.5f,  0.5f, 1.0f, 0.0f,
 	0.5f,  0.5f,  0.5f, 1.0f, 1.0f,
	-0.5f,  0.5f,  0.5f, 0.0f, 1.0f,

 	-0.5f,  0.5f,  0.5f, 1.0f, 0.0f,
 	-0.5f,  0.5f, -0.5f, 1.0f, 1.0f,
 	-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  	-0.5f, -0.5f,  0.5f, 0.0f, 0.0f,

   	0.5f,  0.5f,  0.5f, 1.0f, 0.0f,
   	0.5f,  0.5f, -0.5f, 1.0f, 1.0f,
   	0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
   	0.5f, -0.5f,  0.5f, 0.0f, 0.0f,

  	-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
   	0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
   	0.5f, -0.5f,  0.5f, 1.0f, 0.0f,
  	-0.5f, -0.5f,  0.5f, 0.0f, 0.0f,

 	-0.5f,  0.5f, -0.5f, 0.0f, 1.0f,
	0.5f,  0.5f, -0.5f, 1.0f, 1.0f,
 	0.5f,  0.5f,  0.5f, 1.0f, 0.0f,
 	-0.5f,  0.5f,  0.5f, 0.0f, 0.0f,
	};

	uint32_t indices[] = 
    {
	   0,  2,  1,  2,  0,  3,
	   4,  5,  6,  6,  7,  4,
	   8,  9, 10, 10, 11,  8,
 	 12, 14, 13, 14, 12, 15,
	  16, 17, 18, 18, 19, 16,
	  20, 22, 21, 22, 20, 23,
	};
    
...

	m_vertexLayout = VertexLayout::Create();
	m_vertexBuffer = Buffer::CreateWithData( GL_ARRAY_BUFFER, GL_STATIC_DRAW, vertices, sizeof(float) * 120);
 
	m_vertexLayout->SetAttrib(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, 0);
	m_vertexLayout->SetAttrib(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 5, sizeof(float)  3);
 
	m_indexBuffer = Buffer::CreateWithData(GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW, ndices, sizeof(uint32_t) * 36);
  • 코드 수정 , src / context.cpp / Render() 작성
void Context::Render() 
{
  	glClear(GL_COLOR_BUFFER_BIT);
 
  	glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}
  • 빌드 및 실행 결과
    - 박스의 뒷면이 앞면을 덮어서 그려진다.

깊이가 없다.

Depth Buffer

- Z Buffer라고도 한다.
- 각 픽셀의 컬러값을 저장하는 버퍼 외에, 해당 픽셀의 깊이값(z축 값)을 저장한다.

  • 깊이 테스트(Depth test)

    • 어떤 픽셀의 값을 업데이트 하기전, 현재 그리려는 픽셀의 Z값과 깊이 버퍼에 저장된 해당 위치의 Z값을 비교해본다. (같은 좌표에 있는 여러 픽셀의 깊이 값을 비교해본다.)

    -> 비교결과 현재 그리려는 픽셀이 이전에 그려진 픽셀보다 뒤에 있을경우 픽셀을 그리지 않는다. (가장 가까운 픽셀만 그린다.)

  • OpenGL의 Depth Buffer 초기값은 1
    - 1이 가장 뒤에 있고, 0이 가장 앞에 있다. (왼손좌표계)

    • 사용 함수
      - glEnable(GL_DEPTH_TEST) / glDisable(GL_DEPTH_TEST)로 깊이 테스트를 켜고 끌수 있다.
      - glDepthFunc() : 깊이 테스트 통과조건을 변경할 수 있다. (기본값은 'GL_LESS')


  • 코드 수정 , src / Context.cpp / Render() 작성
void Context::Render() 
{
 	 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 	 glEnable(GL_DEPTH_TEST);
 	 glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}


  • 빌드 및 실행 결과
    - 박스의 뒷면이 앞면을 덮어서 그려진다.

가장 가까운 픽셀만 그려졌다.

Refactiring Uniform

  • uniform값을 설정하는 메소드 추가 , src / program.h 작성
public :

	...
    
	void SetUniform(const std::string& name, int value) const;
	void SetUniform(const std::string& name, const glm::mat4& value) const;
    
private : ...
  • uniform값을 설정하는 메소드 추가 , src / program.cpp 작성

...

void Program::SetUniform(const std::string& name, int value) const 
{
 	 auto loc = glGetUniformLocation(m_program, name.c_str());
 	 glUniform1i(loc, value);
}

void Program::SetUniform(const std::string& name, const glm::mat4& value) const 
{
 	 auto loc = glGetUniformLocation(m_program, name.c_str());
 	 glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(value));
}
  • 코드 수정 , src / context.cpp / Init() 작성
    기존의 glUniform대신 Program :: SetUniform으로 변경
	m_program->Use();
	m_program->SetUniform("tex", 0);
	m_program->SetUniform("tex2", 1);
 
...
 
	auto transform = projection * view * model;
	m_program->SetUniform("transform", transform);

Transformation On Every Frame

  • transform의 지정을 매 프레임마다 지정하기



  • 코드 수정 , src / context.cpp / Render() 작성

void Context::Render() 
{
  	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 	 auto projection = glm::perspective(glm::radians(45.0f), (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.01f, 10.0f);
  	auto view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -3.0f));
  	auto model = glm::rotate(glm::mat4(1.0f), glm::radians((float)glfwGetTime() * 120.0f), glm::vec3(1.0f, 0.5f, 0.0f));
 	 auto transform = projection * view * model;
  	m_program->SetUniform("transform", transform);
	
  	glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}

glfwGetTime() : Context :: 오브젝트가 생성된 이후로 얼마만큼 시간이 흘렀는지 초 단위로 기록
-> 이 코드에선 Context :: Init()이 실행된 이후로 얼마만큼 시간이 흘렀는지 초 단위로 기록




여러개의 큐브를 만들어 적용시키기

  • 코드 수정 , src / context.cpp / Render() 작성
void Context::Render() {
    std::vector<glm::vec3> cubePositions = 
    {
        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),
    };

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);

    auto projection = glm::perspective(glm::radians(45.0f), (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.01f, 20.0f);
    auto view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -3.0f));

    for (size_t i = 0; i < cubePositions.size(); i++)
    {
        auto& pos = cubePositions[i];
        auto model = glm::translate(glm::mat4(1.0f), pos);
        model = glm::rotate(model, glm::radians((float)glfwGetTime() * 120.0f + 20.0f * (float)i), glm::vec3(1.0f, 0.5f, 0.0f));
        auto transform = projection * view * model;
        m_program->SetUniform("transform", transform);
        glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
    }
}
profile
이제 막 시작하는 유니티 클라이언트

0개의 댓글

관련 채용 정보