6 minute read

그래픽 파이프라인

  • 최종적으로 화면에 이미지가 보이기 전까지 여러차례의 변환 과정을 거친다.

[no-alignment]

  • Model Coord. : 각각의 모델들을 기준을하는 좌표계
  • World Coord. : 모든 모델들을 아우르는, 기준이 되는 좌표계
  • Camera Coord. : 어떻게, 어디서 어디를 바라볼지의 시점 좌표계
  • Model Mat. : 모델의 위치 행렬 , 어디에 있는지, translate, scale, rotate를 통해 변환 가능
  • View Mat. : 카메라의 위치, 방향, 바라보는 지점을 설정. lookAt 사용
  • Projection Mat : 화면에 어떻게 투영시킬지, ortho, perspective 사용

MVP

  • 위의 3가지 행렬들의 곱을 통해 Homogeneous Coor을 만들 수 있다.
glm::mat4 MVP = Projection * View * Model; // 순서에 반대로 곱셈을 거친다. 바뀌면 안된다. 

행렬 변환

Translate

  • 이동 변환 행렬 계산은 다음과 같다.
            X_t = X + T_x
            Y_t = Y + T_y
            Z_t = Z + T_z 
            이므로 4X4 행렬로는 아래와 같다.

            | X_t |   | 1  0  0  T_x |   | X |
            | Y_t | = | 0  1  0  T_y | X | Y |  
            | Z_t |   | 0  0  1  T_z |   | Z |
            |  1  |   | 0  0  0   1  |   | 1 |

  • OpenGL3에서는 다음과 같이 작성한다.
    glm::mat4 Model = glm::mat4(1.0f); // 모델 좌표계 생성
    glm::vec3 trans_vec(2.5f, 0.5f, 0.5f); // 변환 행렬 생성
    glm::mat4 trans_Model = glm::translate(Model, trans_vec); // 행렬 곱

Scale

  • 크기 변환 행렬 계산은 다음과 같다.
            X_s = X * S_x
            Y_s = Y * S_y
            Z_s = Z * S_z 
            이므로 4X4 행렬로는 아래와 같다.

            | X_s |   | S_x  0  0  0 |   | X |
            | Y_s | = | 0  S_y  0  0 | X | Y |  
            | Z_s |   | 0   0  S_z 0 |   | Z |
            |  1  |   | 0   0   0  1 |   | 1 |

  • OpenGL3에서는 다음과 같이 작성한다.
    glm::mat4 Model = glm::mat4(1.0f); // 모델 좌표계 생성
    glm::vec3 scale_vec(2.0f, 2.0f, 2.0f); // 변환 행렬 생성, 전체적으로 두배씩 늘린다.
    glm::mat4 scale_Model = glm::scale(Model, scale_vec); // 행렬 곱

Rotate

  • 회전 행렬 변환 계산은 다음과 같다.
            Z 축에 대한 회전을 했을  좌표는 아래와 같이 변한다.

            X_r = X * cos R - Y * sin R
            Y_r = X * sin R + Y * cos R
            Z_r = Z
            이므로 4X4 행렬로는 아래와 같다.

            | X_r |   | cos R  -sin R    0    0 |   | X |
            | Y_r | = | sin R   cos R    0    0 | X | Y |  
            | Z_r |   |   0       0      1    0 |   | Z |
            |  1  |   |   0       0      0    1 |   | 1 |

  • OpenGL3에서는 다음과 같이 작성한다.
    glm::mat4 Model = glm::mat4(1.0f); // 모델 좌표계 생성
    glm::vec3 rotate_vec(1.0f, 0.0f, 0.0f); // 변환 행렬 생성, 보통 1(회전 적용) 또는 0(무회전) 다.
    glm::mat4 rotate_Model = glm::rotate(Model, 30.0f, rotate_vec); // 30도 각도 만큼을 rotate_vec에 회전시킨다.

복합적 변환

  • 복합적 변환을 할 때 원하는 변환을 하려면 순서가 중요하다.
  • 예를 들어서 자동차가 자연스럽게 우회전을 하는 모습을 그려내고 싶다.
  • 이때, 먼저 translate 를 하고 rotate를 하면 회전을 할 때, 원점 방향에서 회전을 시키므로 원점과 거리가 점점 멀어질수록 엄청난 폭의 회전을 하게 된다.
  • 원하는 움직임을 얻으려면, 먼저 rotate로 모델을 회전시킨후에 translate를 하는게 적당하다.
  • 아래의 사진에 오른 쪽이 이상하게 주행하는것처럼 보이는 이유는 x축으로만 translate 한것으로 나타냈기 때문이다. 만약에 삼각함수를 이용해서 실제 각도 만큼 움직이게끔한다면 좀더 자연스러운 모습을 볼수 있다.

[no-alignment]

View Matrix

  • OpenGL에서 View matrix를 나타내기 위해 lookAt을 사용한다.
    /* 카메라는 world space에서 4,3,3 에 위치하며, 원점(0,0,0)을 바라보고 있다. 방향 벡터는 0,1,0 이다. */
	glm::mat4 View       = glm::lookAt(
								glm::vec3(4,3,3), // Camera is at (4,3,3), in World Space
								glm::vec3(0,0,0), // and looks at the origin
								glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)
						   );

Projection Matrix

  • OpenGL에서 Projection Matrix를 나타내기 위해 perspectiveortho를 사용한다.
	// Projection matrix : 30도의 시야각도(이게 작아지면 물체를 확대해서 보는 느낌이 들 수 있다.) , 4:3 ratio, display range : 0.1 unit <-> 100
    glm::mat4 Projection = glm::perspective(glm::radians(30.0f), 4.0f / 3.0f, 0.1f, 100.0f);
	
    
    // frustum 의 왼쪽 ,오른쪽, 맨 밑 , 맨 위, 가까운 평면 , 먼 평면
	glm::mat4 Projection = glm::ortho(-10.0f,10.0f,-10.0f,10.0f,0.0f,100.0f); // In world coordinates

전체 코드

  • 전체 코드는 다음과 같다.
// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#define GLEW_STATIC
// Include GLEW
#include <GL/glew.h>

// Include GLFW
#include <GLFW/glfw3.h>
GLFWwindow* window;
#include <direct.h>

// Include GLM
#include <glm/glm.hpp>
using namespace glm;

#include "common/shader.hpp"

#include <glm/gtc/matrix_transform.hpp>

int main( void )
{
	// Initialise GLFW
     char curDir[1000];
      _getcwd(curDir,1000);

    printf( "%s", curDir);


	if( !glfwInit() )
	{
		fprintf( stderr, "Failed to initialize GLFW\n" );
		getchar();
		return -1;
	}

	glfwWindowHint(GLFW_SAMPLES, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //We don't want the old OpenGL

	// Open a window and create its OpenGL context
	window = glfwCreateWindow( 1024, 768, "Tutorial 03 - Matrices", NULL, NULL);
	if( window == NULL ){
		fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n" );
		getchar();
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);

	// Initialize GLEW
	glewExperimental = true; // Needed for core profile
	if (glewInit() != GLEW_OK) {
		fprintf(stderr, "Failed to initialize GLEW\n");
		getchar();
		glfwTerminate();
		return -1;
	}

	// Ensure we can capture the escape key being pressed below
	glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);

	// Dark blue background
	glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

	GLuint VertexArrayID;
	glGenVertexArrays(1, &VertexArrayID);
	glBindVertexArray(VertexArrayID);

	// Create and compile our GLSL program from the shaders
	GLuint programID = LoadShaders( "SimpleTransform.vertexshader", "SingleColor.fragmentshader" );

	// Get a handle for our "MVP" uniform
	GLuint MatrixID = glGetUniformLocation(programID, "MVP");

	// Projection matrix : 45?Field of View, 4:3 ratio, display range : 0.1 unit <-> 100
	glm::mat4 Projection = glm::perspective(glm::radians(10.0f), 4.0f / 3.0f, 0.1f, 100.0f);
	// Or, for an ortho camera :
//	glm::mat4 Projection = glm::ortho(-10.0f,10.0f,-10.0f,10.0f,0.0f,100.0f); // In world coordinates

	// Camera matrix
	glm::mat4 View       = glm::lookAt(
								glm::vec3(4,3,3), // Camera is at (4,3,3), in World Space
								glm::vec3(0,0,0), // and looks at the origin
								glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)
						   );
	// Model matrix : an identity matrix (model will be at the origin)


    // 1. Scale, 2. Rotate 3. Trans
    glm::mat4 ori_Model = glm::mat4(1.0f);
    glm::vec3 scale_vec(2.0f, 2.0f, 2.0f);
	glm::mat4 Model1 = glm::scale(ori_Model, scale_vec);
    glm::vec3 rotate_vec(1.0f, 0.0f, 0.0f);
    glm::mat4 Model2 = glm::rotate(Model1, 1.0f, rotate_vec);
	glm::vec3 trans_vec(2.5f, 0.5f, 0.5f);
	glm::mat4 Model3 = glm::translate(Model2, trans_vec);

	// Our ModelViewProjection : multiplication of our 3 matrices
	glm::mat4 MVP        = Projection * View * Model1; // Remember, matrix multiplication is the other way around

	static const GLfloat g_vertex_buffer_data[] = {
		-1.0f, -1.0f, 0.0f,
		 1.0f, -1.0f, 0.0f,
		 0.0f,  1.0f, 0.0f,
	};

	GLuint vertexbuffer;
	glGenBuffers(1, &vertexbuffer);
	glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
	glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

	do{

		// Clear the screen
		glClear( GL_COLOR_BUFFER_BIT );

		// Use our shader
		glUseProgram(programID);

		// Send our transformation to the currently bound shader,
		// in the "MVP" uniform
		glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);

		// 1rst attribute buffer : vertices
		glEnableVertexAttribArray(0);
		glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
		glVertexAttribPointer(
			0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
			3,                  // size
			GL_FLOAT,           // type
			GL_FALSE,           // normalized?
			0,                  // stride
			(void*)0            // array buffer offset
		);

		// Draw the triangle !
		glDrawArrays(GL_TRIANGLES, 0, 3); // 3 indices starting at 0 -> 1 triangle

		glDisableVertexAttribArray(0);

		// Swap buffers
		glfwSwapBuffers(window);
		glfwPollEvents();

	} // Check if the ESC key was pressed or the window was closed
	while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
		   glfwWindowShouldClose(window) == 0 );

	// Cleanup VBO and shader
	glDeleteBuffers(1, &vertexbuffer);
	glDeleteProgram(programID);
	glDeleteVertexArrays(1, &VertexArrayID);

	// Close OpenGL window and terminate GLFW
	glfwTerminate();

	return 0;
}



Categories:

Updated: