OpenGL 쉐이더 프로그래밍 - 3D 좌표계

타입·2025년 7월 23일

컴퓨터 그래픽스

목록 보기
13/24

OpenGL의 Back Face Culling

Normal Vector 뒤집기

모든 점과 선분은 항상 front facing
triangle은 front/back face 구분

카메라가 물체 안으로 들어가면 normal vector를 뒤집어야함
CCW -> CW로 전환
꼭짓점의 순서는 그대로지만 법선 벡터가 반대로 향함

  • glFrontFace(mode)
    법선 벡터의 방향을 지정
    mode: GL_CCW/GL_CW

  • glCullFace(mode)
    어느 면을 컬링할지 지정
    mode: GL_BACK/GL_FRONT

  • glEnable(GL_CULL_FACE) / glDisable(GL_CULL_FACE)
    페이스 컬링 활성화/비활성화

  • Tringle Strips
    모든 삼각형의 법선 벡터가 균일하게 나오도록 번갈아서 vertex 순서를 반대로 그림
    일관되게 CCW 방향 유지

3D 좌표계

OpenGL의 canonical view volume은 왼손 좌표계
카메라로부터 멀어지는 위치로 Z 증가

  • 좌표계 충돌 현상
    오른손 좌표계의 좌표를 OpenGL에 그리면 법선 벡터가 CW로 설정되는 문제 발생
    모든 z좌표를 negate하여 CCW 유지
    혹은 모델링 단계부터 왼손 좌표게를 사용 (DirectX)

  • vertex shader 프로그램
    OpenGL에선 오른손 좌표계의 물체를 받아 z좌표를 negate하는 방식으로 주로 처리

#version 330 core

in vec4 aPos; // vertex position: attribute
in vec4 aColor; // vertex color: attribute
out vec4 vColor; // varying color: varying

void main(void) {
	gl_Position = aPos;
	gl_Position.z *= -1.0F; // negation
	vColor = aColor;
}

피라미드 돌리기

y축을 중심으로 물체를 회전
y축을 기준으로 좌표계를 보면 zx 평면에 대하여 2차원 회전을 적용하는 것과 같음

  • vertex shader 프로그램
#version 330 core

in vec4 aPos; // vertex position: attribute
in vec4 aColor; // vertex color: attribute
out vec4 vColor; // varying color: varying
uniform float uTheta; // rotation angle: uniform

void main(void) {
	// zx 평면에 대하여 회전 연산
	gl_Position.z = aPos.z * cos(uTheta) - aPos.x * sin(uTheta);
	gl_Position.x = aPos.z * sin(uTheta) + aPos.x * cos(uTheta);
	gl_Position.yw = aPos.yw;
	gl_Position.z *= -1.0F; // negation
	vColor = aColor;
}
  • updateFunc()
    rotation angle = elapsed time * (1/2 π rad / 1sec)
float theta = 0.0F;
system_clock::time_point lastTime = system_clock::now();

void updateFunc(void) {
	system_clock::time_point curTime = system_clock::now();
	milliseconds elapsedTimeMSEC = duration_cast<milliseconds>(curTime - lastTime); // in millisecond
	theta = (elapsedTimeMSEC.count() / 1000.0F) * (float)M_PI_2; // in <math.h>, M_PI_2 = pi/2
}

y축을 기준으로 회전하는 피라미드

Vertex-Based Objects

피라미드의 색상을 vertex 단위로 지정

  • glDrawElements()
    인덱스 배열로 데이터를 간접적으로 전달 가능

  • main 코드
    필요한 vertex 정보만 입력

glm::vec4 vertPos[] = { // 5 vertices
	{ 0.0F, 0.5F, 0.0F, 1.0F }, // v0
	{ 0.5F, -0.3F, 0.0F, 1.0F }, // v1
	{ 0.0F, -0.3F, -0.5F, 1.0F }, // v2
	{ -0.5F, -0.3F, 0.0F, 1.0F }, // v3
	{ 0.0F, -0.3F, 0.5F, 1.0F }, // v4
};

glm::vec4 vertColor[] = { // 5 colors
	{ 1.0F, 1.0F, 1.0F, 1.0F, }, // v0: white
	{ 1.0F, 0.3F, 0.3F, 1.0F, }, // v1: red
	{ 0.3F, 1.0F, 0.3F, 1.0F, }, // v2: green
	{ 0.3F, 0.3F, 1.0F, 1.0F, }, // v3: blue
	{ 1.0F, 1.0F, 0.3F, 1.0F, }, // v4: yellow
};
  • drawFunc()
    인덱스 배열을 넘겨서 면을 그림
GLuint indices[] = { // 6 * 3 = 18 indices
	0, 1, 2, // face 0: v0-v1-v2
	0, 2, 3, // face 1: v0-v2-v3
	0, 3, 4, // face 2: v0-v3-v4
	0, 4, 1, // face 3: v0-v4-v1
	1, 4, 3, // face 4: v1-v4-v3
	1, 3, 2, // face 5: v1-v3-v2
};

void drawFunc(void) {
	...
	glVertexAttribPointer(locPos, 4, GL_FLOAT, GL_FALSE, 0, glm::value_ptr(vertPos[0]));
	GLuint locColor = glGetAttribLocation(prog, "aColor");
	glEnableVertexAttribArray(locColor);
	glVertexAttribPointer(locColor, 4, GL_FLOAT, GL_FALSE, 0, glm::value_ptr(vertColor[0]));
	GLuint locTheta = glGetUniformLocation(prog, "uTheta");
	glUniform1f(locTheta, theta);
	// draw the pyramid
	glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_INT, indices); // 18 indices
    ...
}

꼭짓점에 지정한 색상에 따라 이중 선형 보간된 피라미드가 그려짐

DrawArrays vs DrawElements

  • glDrawArrays
    18 vertices 4 floats = 72 floats
    18 colors
    4 floats = 72 floats
    face 마다 다른 색상 설정 가능

  • glDrawElements
    5 vertices 4 floats = 20 floats
    5 colors
    4 floats = 20 floats
    18 indices = 18 integers
    효과적인 메모리 사용

profile
주니어 언리얼 프로그래머

0개의 댓글