상세 컨텐츠

본문 제목

SCOP: 3D 오브젝트 이동, 회전, 색상 및 텍스처 적용 - (7)

Computer Graphics/SCOP

by Banjosh 2024. 9. 27. 11:21

본문

지난번에 .obj 파일을 파싱하여 다음과 같은 오브젝트를 그리는 데 성공했었다.

 

이번에는 이 오브젝트에 다음과 같은 작업을 진행하였다.

  1. 오브젝트 3축 평행 이동 및 중심축 회전
  2. 프리미티브별 기본 색상 적용
  3. 특정 키를 눌렀을 때 텍스처로 부드럽게 전환

1. 오브젝트 3축 이동 및 회전

카메라 변수 설정

  • m_cameraPos: 카메라의 위치 벡터
  • m_cameraFront: 카메라의 앞쪽 방향 벡터
  • m_cameraUp: 카메라의 위쪽 방향 벡터

오브젝트 중심 찾기

  • 오브젝트의 좌표에서 x, y, z의 최소값과 최대값을 구해 중심을 계산한다.
  • 계산된 중심은 m_modelPos 변수에 저장한다.

오브젝트 3축 평행 이동

  • 키 입력 처리:
    • 키 입력은 glfwGetKey() 함수를 이용하여 직접 확인한다.
    • 키 입력에 따라 glmath::vec3 타입의 move 값을 변화시킨다.
    • 이동 속도는 objectSpeed로 고정된 수치를 사용한다.
	const static float objectSpeed = 0.025f;

	glmath::vec3 move(0.0f);
	
    // S, W 를 누르면 cameraFront 방향으로 objectSpeed 만큼 이동
	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
		move = move + objectSpeed * m_cameraFront;
	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
		move = move - objectSpeed * m_cameraFront;

    // A, D 를 누르면 cameraRight 방향으로 objectSpeed 만큼 이동
	glmath::vec3 cameraRight = glmath::normalize(glmath::cross(m_cameraFront, m_cameraUp));
	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
		move = move + objectSpeed * cameraRight;
	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
		move = move - objectSpeed * cameraRight;
	
    // Q, E 를 누르면 cameraUp 방향으로 objectSpeed 만큼 이동
	glmath::vec3 cameraUp = glmath::normalize(glmath::cross(cameraRight, m_cameraFront));
	if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)
		move = move + objectSpeed * cameraUp;
	if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
		move = move - objectSpeed * cameraUp;

	// m_move 값 변경
	m_move = m_move + move;

  

오브젝트 회전

  • 오브젝트는 중심(m_modelPos)을 기준으로 +y축 방향으로 회전한다.
  • 특정 키(R)를 누를 때마다 rotateSpeed만큼 m_degree 값을 증가시킨다.
    const static float rotateSpeed = 0.5f;
    float degree = 0.0f;
	
    // R 입력시 rotateSpeed 만큼 회전
	if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS)
		degree = rotateSpeed;
        
    // m_degree 값 변경
    m_degree = m_degree + degree;
    if (m_degree > 360.0f) { m_degree -= 360.0f; }

모델 행렬 계산

  • 이동과 회전을 적용하기 위해 모델 행렬을 구성한다.
  • 모델 행렬의 계산 순서는 실제 적용 순서의 역순으로 이루어진다.
	glmath::mat4 model = glmath::translate(glmath::mat4(1.0f), m_modelPos + m_move) *
			     glmath::rotate(glmath::mat4(1.0f), glmath::radians(m_degree), glmath::vec3(0.0f, 1.0f, 0.0f)) *
		 	     glmath::scale(glmath::mat4(1.0f), glmath::vec3(1.0f)) *
			     glmath::translate(glmath::mat4(1.0f), -1 * m_modelPos);

 

 


2. 프리미티브별 색상 적용

Vertex 구조체에 색상 추가

  • 프리미티브별로 색상을 적용하기 위해 Vertex 구조체에 glmath::vec3 color 멤버를 추가하였다.
struct Vertex {
	glmath::vec3 pos;
	glmath::vec2 texCoord;
	glmath::vec3 color; // 추가
};

정점 데이터 저장 방식 변경

  • 기존에는 중복되는 정점을 인덱스로 관리했지만, 프리미티브별 색상을 적용하기 위해 정점 데이터를 인덱스 순서대로 중복 저장하는 방식으로 변경하였다.
  • 이렇게 하면 메모리 사용량은 늘어나지만, 각 프리미티브(삼각형)마다 동일한 색상을 적용할 수 있다.

랜덤 색상 배정

  • std::random 라이브러리를 이용하여 각 프리미티브에 랜덤한 색상을 지정했다.
void Model::setColor(std::vector<Vertex>& vertices) {
	static std::random_device rd;
	static std::mt19937 gen(rd());
	static std::uniform_real_distribution<float> dis(0.0f, 1.0f);
	
	for (int i = 0; i < vertices.size() / 3; i++) {
		glmath::vec3 color(dis(gen), dis(gen), dis(gen));
		vertices[3 * i].color = color;
		vertices[3 * i + 1].color = color;
		vertices[3 * i + 2].color = color;
	}
}

셰이더에서 색상 처리

  • 버텍스 셰이더프래그먼트 셰이더에서 color 속성을 처리하도록 수정하였다.
  • VAO 설정 시 location = 2에 color 속성을 지정하였다.

 

	m_vertexArray->setAttribute(0, 3, sizeof(Vertex), offsetof(Vertex, pos));
	m_vertexArray->setAttribute(1, 2, sizeof(Vertex), offsetof(Vertex, texCoord));
    	// location = 2에 color 값 추가
	m_vertexArray->setAttribute(2, 3, sizeof(Vertex), offsetof(Vertex, color));
  • 렌더링 결과는 각 프리미티브마다 랜덤한 색상이 적용된 오브젝트로 다음과 같다.

 


3. 특정 키를 눌렀을 때 텍스처로 부드럽게 전환

텍스처 좌표 저장

  • OBJ 파일 파싱 시 정점의 위치 정보와 함께 텍스처 좌표도 저장하도록 수정했다.
  • 텍스처 좌표는 정점의 위치를 기반으로 계산하였다.
	objInfo->vertexInfo.vPosInfo.push_back(Pos(v[0], v[1], v[2]));
	objInfo->vertexInfo.vTexInfo.push_back(TexCoord(v[0], v[1]));
  • 또는 UV 좌표계를 활용하여 텍스처 좌표를 계산하였다.
	float pi = 3.141592f;
	float r = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
	if (r == 0) r = 1.0f;
	float theta = std::atan2(v[2], v[0]);
	float phi = std::asin(v[1] / r);
	float U = (phi + pi / 2) / pi;
	float V = (theta + pi) / (2.0f * pi);
	objInfo->vertexInfo.vTexInfo.push_back(TexCoord(V, U));
	objInfo->vertexInfo.vPosInfo.push_back(Pos(v[0], v[1], v[2]));

 

텍스처와 기본 색상 간의 부드러운 전환

  • 특정 키를 누르면 텍스처와 기본 색상 사이를 부드럽게 전환하도록 구현하였다.
  • 프래그먼트 셰이더에서 texRatio라는 uniform 변수를 사용하여 텍스처와 기본 색상의 비율을 조절하였다.
void main() {
	// texture color + base color
	fragColor = texture(tex, texCoord) * texRatio + vec4(color, 1.0f) * (1 - texRatio);
}
  • texRatio 값은 0.0에서 1.0 범위에 존재하며, rendering 전에 + 혹은 - 방향으로 계속 변화시킨다.
  • texRatio 값을 transSpeed 만큼 변경하는데 T key를 누를때마다 transSpeed의 방향을 반대로 바꿔준다.
  • rendering 전에 Uniform 변수에 texRatio를 저장한다.
    static float transSpeed = -0.01;
    static bool keyTPressed = false;
	
	if (glfwGetKey(window, GLFW_KEY_T) == GLFW_PRESS) {
        if (!keyTPressed) {
            transSpeed = -transSpeed;
            keyTPressed = true;
        }
    } else {
        keyTPressed = false;
    }

렌더링 결과

  • 텍스처가 적용된 오브젝트로 부드럽게 전환되는 모습을 확인할 수 있다.
  • UV 좌표를 적용한 텍스처 매핑을 통해 보다 자연스러운 텍스처링이 가능해졌다.

 

texture에 position 좌표 적용
texture 좌표에 UV 좌표 적용


마무리

이로써 SCOP 과제의 기본 구현 사항을 모두 완료하였다! 오브젝트의 이동과 회전, 프리미티브별 색상 적용, 그리고 텍스처 전환까지 성공적으로 구현하였다.

앞으로는 보너스 기능이나 추가적인 개선 사항을 고려해 볼 계획이다.

관련글 더보기