Back to top

LearnOpenGL(04) - Shaders

작성날짜 2022/06/26

이전글: LearnOpenGL(03) - Hello Triangle

다음글: LearnOpenGL(05) - 텍스처


GLSL


Hello Triangle 챕터에서 언급했듯이, 셰이더는 GPU에 있는 작은 프로그램입니다. 각 프로그램들은 그래픽 파이프라인의 특정 구간에서 작동합니다. 기본적으로 셰이더는 단순히 입력을 출력으로 바꿔주는 프로그램일뿐입니다. 또한 셰이더는 매우 독립적인데, 셰이더들은 각자의 입력과 출력 이외의 방법으로는 서로 커뮤니케이션을 할 수 없습니다.

셰이더는 C와 비슷한 언어인 GLSL로 쓰여졌습니다. GLSL은 그래픽을 다루거나 벡터나 행렬을 계산하기 위해 맞춤 제작되었습니다.

셰이더는 항상 버전 선언으로 시작하고, 입력과 출력 변수들이 뒤따르며, 유니폼과 메인 함수가 위치합니다. 각 셰이더의 진입점은 메인 함수이며 그곳에서 입력과 출력이 처리됩니다. 혹시 유니폼이 무엇인지 모르겠더라도 괜찮습니다. 곧 설명해 드리겠습니다.


셰이더는 일반적으로 다음과 같은 구조를 가집니다:

#version version_number
in type in_variable_name;
in type in_variable_name;

out type out_variable_name;

uniform type uniform_name;

void main()
{
    // process input(s) and do some weird praphics stuff

    // output processed stuff to output variable
    out_vaiable_name = weird_stuff_we_processed;
}

버텍스 셰이더에서 각 입력 변수는 버텍스 속성(vertex attribute)라고 합니다. 하드웨어에 따라 선언 가능한 버텍스 속성의 최대 개수가 달라집니다. OpneGL은 적어도 4개의 컴포넌트를 가진 16개의 버텍스 속성을 보장하며, 몇몇 하드웨어서는 더 많이 가질 수도 있습니다. 가능한 속성의 개수를 알고 싶다면 GL_MAX_VERTEX_ATTRIBS: 명령어로 검색해 볼 수 있습니다.

int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;

이 명령어는 최소 16을 반환하며 이는 대부분의 목적에서 필요보다 많은 숫자입니다.

article_img_0_rte_image_140.png제 컴퓨터에서는 16개를 지원하네요!

Types

GLSL은 다른 많은 프로그래밍 언어처럼 우리가 하려는 작업을 위한 특정한 데이터 타입을 가지고 있습니다. GLSL은 int, float, double, uint 그리고 bool처럼 매우 기본적인 데이터 타입을 가지고 있으며 또한 우리가 자주 사용하게 될 vector와 matricex라는 특별한 두 컨테이너 타입도 갖고 있습니다. matrices에 대해서는 다음 챕터에 설명하겠습니다.

 

Vectors

GLSL에서의 벡터는 방긍 언급한 기본 타입에 대한 2,3 또는 4개의 컴포넌트 컨테이너입니다. 벡터는 다음과 같은 형태를 갖습니다(n 은 컴포넌트의 갯수를 나타냅니다.):

  • vecn: n개 float에 대한 기본 벡터
  • bvecn: n개 boolean의 벡터
  • inecn: n개 integer의 벡터
  • uvecn: n개 unsigned integer의 벡터
  • dvecn: n개 double 컴포넌트의 벡터

우리는 대부분 vecn을 사용할텐데, 우리의 목적에 float 타입이면 충분합니다.

 

벡터의 컴포넌트들은 vec.x를 통해 접근할 수 있으며 여기에서 x는 벡터의 첫 번째 컴포넌트입니다. .x, .y, .z, .w를 통해 네 컴포넌트에 접근할 수 있습니다. 또한 GLSL은 rgba를 사용해 색깔이나 stpq를 이용해 텍스처 좌표에 접근할 수 있도록 허용합니다.

벡터를 사용해 Swizzling이라고 하는 재미있고 유연한 컴포넌트 선택이 가능합니다. Swizzling으로 다음과 같은 구문을 사용할 수 있습니다:

vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;

원래 벡터에 해당 컴포넌트가 있는 경우 4개의 문자를 조합하여 (같은 타입인)새로운 벡터를 만들 수 있습니다. 예를 들어 vec2에서 .z 컴포넌트에 접근하는 것은 허용되지 않습니다. 또한 벡터를 다른 벡터 생성자 호출에 대한 인수로 전달하여 필요한 인수 수를 줄일수도 있습니다.

vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);

따러서 벡터는 모든 종류의 입력과 출력을 할 때 사용할 수 있는 유연한 데이터 타입이라고 할수 있습니다.

 

Ins and outs

셰이더는 그 자체로 작고 멋진 프로그램이지만 전체의 일부분일 뿐이므로 각 셰이더가 서로 정보를 교환할 입력과 출력을 갖길 원합니다. GLSL은 이를 위해 특별히 in out 키워드를 정의했습니다. 각 셰이더는 해당 키워드를 사용해 입력과 출력을 지정할 수 있으며 출력 변수가 다음 단계의 입력 변수와 일치하면 변수는 전달됩니다. 또한 버텍스 셰이더와 프래그먼트 셰이더는 약간 다릅니다.

 

버텍스 셰이더는 반드시 어떤 형태의 입력을 받아야하며 그러지 않을 경우



관련글

LearnOpenGL(02) - Hello Window LearnOpenGL(03) - Hello TriangleLearnOpenGL(05) - 텍스처LearnOpenGL(07) - 좌표계
LearnOpenGL(04) - Shaders
An unhandled error has occurred. Reload 🗙