본문 바로가기
공허의 유산/표현의 자유

[opengl].[#2.GLSL] 07. Vertex Shader에서 간단한 애니메이션 구현

by 바른생활머시마 2023. 1. 11.
728x90
반응형

앞에서 Vertex Shader에서 수식을 이용하여 Vertex 좌표를 변경 할 수 있는 것을 보았고, 이것이 Vertex의 좌표를 변경 할 뿐, Vertex로 구성 된 메쉬의 Topology와는 관계가 없다는 사실, 그리고 Pixel 수준에서 변형이 되는 것이 아니라는 것을 보았습니다.

https://learn-and-give.tistory.com/31 

 

[opengl].[#2.GLSL] 06. gl_Position 끄적거리기(2)

지난 시간에는 Shader 밖에서 설정 된 Vertex 좌표가 gl_Position에 전달되는지 확인 해 봤습니다. 코드 상에서는 glRotate를 했지만 이것은 Model View Matrix에 영향을 미쳤고, glVertex로 입력 된 좌표가 그대

learn-and-give.tistory.com

 

이번에는 Animation을 Shader에서 구현 해 보겠습니다.

우선, Shader 밖에서 주전자가 회전하는 Animation을 구현 해 보겠습니다.

간단히 매 프레임 증가하는 각도만큼 회전시켜 주전자를 그리도록 하겠습니다.

void display()
{
	//Clear
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//Draw
	glColor3f(0.2f, 0.2f, 0.6f);
	glPushMatrix();
	glRotatef(rotate_angle, 0.f, 1.f, 0.f);
	glutWireTeapot(0.5f);
	glPopMatrix();
	glFlush();

	glutSwapBuffers();
	rotate_angle = rotate_angle + 0.1f;
	if (rotate_angle > 360.f) rotate_angle -= 360.f;
}

결과는 아래와 같이 회전합니다. "전원"을 안꽂은 노트북에서 돌렸더니 성능에 따라서 속도가 오락가락하네요.

glRotatef를 이용한 주전자의 회전

 glRotate를 쓰지 않고, Shader 내부에서 Vertex가 꿀렁꿀렁 물결치게 하려면 어떻게 하면 될까요?

glRotate에 사용 된 변수를 Vertex Shader어 전달을 해서,  x좌표를 기준으로 변환 되는 수식의 x에 더해주면 그 값이 주기적으로 달라지겠네요. 현재는 그 값이 Degree 단위의 각도라는 점도 고려 해 둬야겠구요.

 

 Vertex Shader에 값 전달하기

 Shader 밖에서 Shader 안으로 값을 전달하는 것은, 이전과는 완전히 다른 차원의 세계가 열리는 것이죠. 그 값을 사용자 입력으로부터 받는다면, 사용자 입력으로 Shader를 조정 할 수 있게 된다는 뜻이기도 합니다.

 

 우선 Shader 밖의 코드에서 변수는 프레임 별로 계속 증가시키되, 회전 시키는 코드(glRotatef)는 제거하고, 이 값을 Shader에 넘겨서 Shader로부터 변화가 생기도록 하겠습니다.

 

코드를 설명하기 전에 말로 먼저 좀 정리를 해보겠습니다. OpenGL로 텍스쳐링을 구현 해 보았다면, 이해하는데 많은 도움이 될 것입니다. Texture를 쓸 때는, glGenTexture라고 하여 그래픽카드에 Texture 저장 공간을 만들고, 그 핸들을 받아옵니다. 그리고, 그 Texture를 사용 할 때는, glBindTexture에 핸들을 넘겨, 지정 된 텍스쳐를 처리합니다. 텍스쳐 데이터를 전송하거나 파라미터를 설정하는 작업을 할 때도 사전에 해당 텍스쳐를 Binding 시켜야 합니다. 이 Binding 과정을 통해서 이후의 OpenGL 함수들이 해당 Texture의 조작에 사용되게 됩니다. 

 

 자, 그럼 Texture가 무엇인가? 오브젝트 표면을 채워줄 이미지 데이터인데, 좀 더 일반적으로 말하면 그냥 데이터 덩어리입니다. 이미지 데이터라면, RGB(A) 색상을 나타내기 위한 형식이나 범위에 맞춰서 작성 된 값들이 있는 데이터입니다. 그래픽 카드로의 데이터 전달은 동일한 과정을 거치게 됩니다. 이런 데이터는 꼭 그래픽용 데이터일 필요가 없고, GPU에서의 연산을 위해 전달하기도 합니다. 그런 목적으로 GPU를 쓸 때 GPGPU라는 말을 쓰기도 하죠.(옛 생각이 나서...) 아무튼, 텍스쳐링을 위해 데이터를 전달하는 과정은 CPU쪽에서 다루던 데이터를 GPU로 전달하는 과정이며, Shader로 데이터를 전달하는 과정 또한 같습니다. 오히려 텍스쳐링을 위한 데이터 전달 과정의 조금 더 일반화 된 형태이죠.

 

필요한 요소를 정리 해 보면 아래와 같습니다.

  • Vertex Shader 내에서 각도 값을 담을 변수(Shader 내의 변수로써, angle이라고 하겠습니다.)
  • Vertex Shader 내의 각도 값을 담을 변수에 접근 할 핸들(Shader 밖의 핸들용 변수로써, loc라고 하겠습니다.)

 

Vertex Shader 코드

 Vertex Shader 코드를 먼저 살펴보겠습니다.

uniform float angle;
void main()
{	
	gl_FrontColor = gl_Color;
	vec4 newPos = vec4(gl_Vertex);
	newPos.y = newPos.y + sin(8.0*newPos.x+angle) / 4.0;
	gl_Position = gl_ModelViewProjectionMatrix * newPos;
}

외부에서 전달 받을 값이 저장 될 angle이라는 변수가 선언되었는데, 변수 앞에 uniform이라는 키워드가 있습니다. 이 키워드를 적어줘야 외부에서 값을 전달 받는 변수가 됩니다. 수식은 단순합니다. 0~360 사이 값이 sin 함수 내부에 더해지면 x위치에 따라 꿀렁꿀렁거리게 변한 형상이 순서대로 변위가 조정됩니다. 물결 치듯~

 

그럼, Vertex Shader에 값을 넘기기 위해서 밖에서 해줄 일은, 저 변수에 접근 할 핸들을 할당 받고, 그 핸들을 이용해서 값을 넘겨주는 것입니다. 핸들은 Shader program이 만들어 진 후, 미리 받아둡니다. 기존 코드의 가장 마지막에 핸들을 받아오는 코드를 한 줄 추가 했습니다.

//GLuint v_shader, f_shader, program_shader
void setShaders()
{
	char* vs = NULL, * fs = NULL, * fs2 = NULL;

	v_shader = glCreateShader(GL_VERTEX_SHADER);
	f_shader = glCreateShader(GL_FRAGMENT_SHADER);

	vs = textFileRead((char*)"minimal.vert");
	fs = textFileRead((char*)"minimal.frag");

	const char* vv = vs;
	const char* ff = fs;

	glShaderSource(v_shader, 1, &vv, NULL);
	glShaderSource(f_shader, 1, &ff, NULL);

	free(vs); free(fs);

	glCompileShader(v_shader);
	glCompileShader(f_shader);

	printShaderInfoLog(v_shader);
	printShaderInfoLog(f_shader);

	program_shader = glCreateProgram();
	glAttachShader(program_shader, v_shader);
	glAttachShader(program_shader, f_shader);

	glLinkProgram(program_shader);
	printProgramInfoLog(program_shader);

	glUseProgram(program_shader);

	//animation용 회전값 핸들 설정
	loc = glGetUniformLocationARB(program_shader, "angle");
}

값의 변경은 랜더링 하는 부분에서 새로운 각도로 갱신하고, 그 갱신 된 값을 Shader로 전달하도록 했습니다. 기존에 angle의 증가값을 그대로 쓰면 주기가 너무 짧아서 증가량을 아주 작은 값으로 줄였습니다.추가 된 코드는 glUniform1fARB 함수입니다.핸들의 변수에 rotate_angle의 값, float 하나를 전달한다는 뜻입니다.

void display()
{
	//Clear
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//Draw
	glColor3f(0.2f, 0.2f, 0.6f);
	glUniform1fARB(loc, rotate_angle);
	//glPushMatrix();
	//glRotatef(rotate_angle, 0.f, 1.f, 0.f);
	glutWireTeapot(0.5f);
	//glPopMatrix();
	glFlush();

	glutSwapBuffers();
	rotate_angle = rotate_angle + 0.001f;
	if (rotate_angle > 360.f) rotate_angle -= 360.f;
}

결과는 아래 영상과 같습니다.

 

꽤 인상 깊은 효과죠??

다음 시간부터 조명에 대해서 해 볼텐데, OpenGL의 조명이 우리를 위해 참 많은 것을 해줬었다는 것을 알 수 있게 되죠.

728x90
반응형

댓글