OpenGL GLSL Shader 변수 전달하기 [Learn OpenGL 쉬운 번역]

728x90
반응형
※ 본 포스팅은 learnopengl.com을 번역 및 가감한 포스팅이며, learnopengl에서는 번역 작업 참여를 적극 장려하고 있습니다!
아래 링크에서 원문을 확인할 수 있습니다.

 

Shaders

shader는 GPU에서 실행되는 작은 프로그램이라고 보면 됩니다. 그래픽 파이프라인에서 shader는 온전히 입력과 출력으로만 정보를 주고받을 수 있는 형태입니다. 이번 포스팅에서는 opengl shader 언어니 GLSL에 대해 알아봅시다. 

 

GLSL

GLSL은 C와 유사한데, 그래픽에서 사용하도록 설계되어있어서 vector나 matrix 연산에 특화되어있습니다.

 

먼저 버전을 선언하고, 입출력 변수, uniforms, main 함수로 구성됩니다. shader는 main으로 진입하고 입력된 변수들을 처리한 결과로 출력 변수에 담아 출력합니다. uniform이라는 것은 추후 알아봅니다.

 

#version version_number
in type in_variable_name;
in type in_variable_name;

out type out_variable_name;
  
uniform type uniform_name;
  
void main()
{
  // 입력을 처리하고 그래픽 관련 작업 수행
  ...
  // 처리된 결과를 출력 변수에 저장
  out_variable_name = weird_stuff_we_processed;
}

vertex shader에서 입력 변수는 vertex attribute라고 하는데 최대 개수는 하드웨어적인 한계가 있습니다. 못해도 요소가 4개인 vertex attribute 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개를 반환하는게 일반적이고 이정도면 충분하죠.

 

Types

GLSL의 데이터 type을 알아봅시다. 보통 C언어와 비슷하게 int, float, double, uint, bool 등을 가지고 있습니다. 또 자주 사용하게 될 vector와 matrix도 있습니다. 이건 나중에 알아봅시다. 

 

Vectors

GLSL의 type중 하나를 각 2,3,4 개 씩 담을 수 있습니다. type에 따라서 아래와 같은 종류가 있습니다. (n은 2,3,4 중 하나입니다)

 

  • vecn: 기본적으로 n개의 float을 가진 벡터.
  • bvecn: n개의 boolean을 가진 벡터.
  • ivecn: n개의 integer를 가진 벡터.
  • uvecn: n개의 unsigned integer를 가진 벡터.
  • dvecn: n개의 double 구성 요소를 가진 벡터.

보통은 float을 주로 사용하니까 vecn을 많이 씁니다.

 

vector 요소에는 vec.x 와 같은 방식으로 접근하고, 종류는 .x, .y, .z, .w 가 있습니다. 또 color를 표현하기 위해 rgba를, texture 좌표를 표현하기 위해 stpq를 사용해서 접근할 수 있습니다.

 

vector type은 아래와 같은 방법으로 요소를 여러 개 선택할 수 있는데 이를 swizzling이라고 합니다.

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

 

 

원래 vector에다가 요소를 추가해서 새로운 vector로 초기화 할 수도 있습니다. 

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

vector는 요소를 여러개 고르고 초기화하고 유연하게 사용할 수 있습니다. 위 예제 코드만 보더라도 어떤 사용방식을 허용하는지 감이 대충 잡힐 겁니다. 

 

Ins and outs

shader에 입출력으로 데이터를 주고 받기 위해서 in와 out 키워드를 지정해서 사용합니다. 출력되는 out 변수의 형식이 다음 연결되는 shader의 in 변수 입력 형식과 같아야지 정상적으로 정보가 전달됩니다.

 

vertex shader는 vertex data로부터 입력을 받는데, 원하는 형태로 입력 받도록 설정할 수 있습니다. 입력 변수를 location이라는 메타데이터로 지정해서 CPU단에서 vertex attribute를 구성해서 보냅니다. 그게 layout (location = 0)의 형태입니다. 

즉 vertex shadre는 입력을 위해서 layout을 지정해서 vertex data와 연결합니다.

 

shader에서 layout을 지정하는 방법 말고도 glGetAttribLocation이라는 함수로 attribute location을 CPU에서 요청해서 설정할 수도 있습니다. 근데 vertex shader에서 설정하는게 더 보기 편하고 opengl의 작업량도 줄어드니 layout이 더 많이 쓰입니다.

 

fragment shader의 경우는 출력이 vec4의 color변수입니다. fragmen shader 자체가 최종적으로 화면에 띄우는 색상을 생성하는 shader이기 때문이죠. 만약 이를 따르지 않고 출력 색상을 지정 안하면 opengl이 화면을 온통 검은색 혹은 흰색으로 렌더링해버릴 겁니다. 

 

보았듯 shader에서 shader로 데이터를 전송하려면 shader의 출력을 선언하고 이걸 받는 다음 shader에서도 같은 형태의 입력을 선언합니다. 두 shader간 type과 이름이 동일해야지 opengl이 변수를 연결해서 데이터를 전송해줍니다.(이건 program object를 link할 때 수행하는 작업입니다)

실제로 vertex shader가 fragment shader로 정보를 전달하는 코드를 봅시다. 

 

Vertex shader

#version 330 core
layout (location = 0) in vec3 aPos; // the position variable has attribute position 0
  
out vec4 vertexColor; // specify a color output to the fragment shader

void main()
{
    gl_Position = vec4(aPos, 1.0); // see how we directly give a vec3 to vec4's constructor
    vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // set the output variable to a dark-red color
}

Fragment shader

#version 330 core
out vec4 FragColor;
  
in vec4 vertexColor; // the input variable from the vertex shader (same name and same type)  

void main()
{
    FragColor = vertexColor;
}

보면 vertex shader에서 vec4 타입의 vertexColor를 출력하고 fragment shader에서 입력으로 받습니다. 이렇게 이름과 타입이 같으면 shader간 연결되어 정보를 전달합니다. vertex shader에서 지정한 vertexColor색이 fragment shader로 그대로 넘어와서 출력 화면에 반영합니다. 

 

 

이렇게 반영되어 어두운 빨간색 삼각형이 출력되면 vertex shader에서 fragment shader로 정상적으로 전송된 겁니다. 

728x90
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유