Software Development/Graphics

OpenGL Super Bible 그래픽스 공부하기 - 3. 파이프라인 따라가기 - 1) 쉐이더간 데이터 전달

huiyu 2018. 1. 20. 18:42

OpenGL Super Bible 그래픽스 공부하기 - 1. OpenGL 기본개념이해

OpenGL Super Bible 그래픽스 공부하기 - 2. 첫번째 OpenGL 프로그램

OpenGL Super Bible 그래픽스 공부하기 - 2-3. 처음 그리는 삼각형

Part 3 파이프라인 따라가기 - 1) 쉐이더간 데이터 전달

  - OpenGL 파이프라인의 각 스테이지가 하는 일
  - 작성한 쉐이더를 고정 함수 파이프라인 스테이지에 연결하는 방법
  - 그래픽스 파이프라인의 모든 스테이지에서 동시에 사용하는 프로그램을 만드는 방법


 3.1 Vertex Shader에 Data 전달하기
  - Vertex Shader : 첫번째 프로그래밍 가능 스테이지, 유일한 필수 스테이지
                   실행 전 버텍스 페칭/버텍스 풀링(정점 풀링)이라는 고정 함수 스테이지 실행, 이 작업은 자동으로 버텍스 쉐이더에 입력 제공

  3.1.1 Vertex 속성
   - GLSL에서 쉐이더로 Data를 가져오거나 내보내는 작업은 in/out 지시어로 선언
   - 스테이지 사이에 in과 out을 사용하면 쉐이더 간의 연결선을 마련하여 그 선을 통해 데이터를 전달 가능

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#version 430 core
 
//offset이 입력 버텍스 속성이다.
layout (location = 0) in vec4 offset;
 
void main(void)
{
    const vec4 vertices[3= vec4[3](vec4( 0.25-0.250.51.0),
                                     vec4(-0.25-0.250.51.0),
                                     vec4( 0.25,  0.250.51.0));
 
    //offset을 하드코딩된 버텍스 위치에 더한다.
    gl_Position = vertices[gl_VertexID] + offset;
}
cs

 "in" 저장 지시어 : 버텍스 쉐이더의 입력으로 설정, 고정함수 버텍스 페치 스테이지에 의해 그 내용이 자동으로 채워진다는 의미이다.
   이 변수를 버텍스 속성이라고 부른다.

* 버텍스 쉐이더에 입력으로 'offset' 변수를 추가했다. 첫번째 쉐이더에 대한 입력이므로, 버텍스 페치 스테이지에서 자동으로 채워진다.
이 스테이지는 glVertexAttrib*()라는 버텍스 속성 함수 중 하나로 변수를 채울 수 있다.

1
void glVertexAttrib4fv(GLuint index, const GLfloat * v);
cs

 - index : 속성을 참조하기 위한 값,
 - v : 속성에 넣을 새로운 데이터,
 쉐이더 코드의 layout (location = 0)<- 레이아웃 지시어를 사용하여 버텍스 속성의 위치를 0으로 설정, 이 값이 index이다.

*glVertexAttrib*()함수를 호출할 때마다 버텍스 쉐이더에 전달하는 버텍스 속성의 값을 갱신할 수 있다. 이를 사용하여 애니매이션도 가능

draw시 아래와 같이 호출해준다.

1
2
3
4
 GLfloat attrib[] = {(float)sin(currentTime) * 0.5f,
                    (float)cos(currentTime) * 0.6f,
                    0.0f, 0.0f };
glVertexAttrib4fv(0, attrib);
cs

실행시키면 원을 그리는 삼각형을 그릴 수 있다.(gcc에서 빌드시 아래와 같이 에러가 나온다면 math.h사용을 위해 -lm을 추가해주자.)

1
2
3
4
huiyu@huiyu-linux:~/Opengl$ gcc opengl.c -o test -I /usr/include/SDL2/ -I /usr/include/GL/ -lSDL2 -lGL
/usr/bin/ld: /tmp/ccyEe9d6.o: undefined reference to symbol 'cos@@GLIBC_2.2.5'
//lib/x86_64-linux-gnu/libm.so.6: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
cs

컴파일은 아래와 같이,

1
gcc opengl.c -o test -I /usr/include/SDL2/ -I /usr/include/GL/ -lSDL2 -lGL -lm
cs



3.2 스테이지간 데이터 전달
 - in과 out 키워드를 사용하면 스테이지간 데이터 전달이 가능하다.
 - 한 쉐이더에서 출력 변수에 저장하는 값은 다음 스테이지에서 in 키워드로 선언된 유사한 이름의 변수로 보내진다.
 ex) 버텍스 쉐이더에서 vs_color라는 변수를 out 키워드로 서언했다면, 프래그먼트 쉐이더 스테이지에서는 in 키워드로 선언된 vs_color로 연관

*vs_color를 선언하여 스테이지간 데이터를 전달해보자.
 - vertex shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#version 430 core
 
//offset과 color는 입력 버텍스 속성이다.
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
 
//vs_color는 다음 스테이지로 보내질 출력이다.
out vec4 vs_color;
 
void main(void)
{
    const vec4 vertices[3= vec4[3](vec4( 0.25-0.250.51.0),
                                      vec4(-0.25-0.250.51.0),
                                     vec4( 0.25,  0.250.51.0));
 
    //하드코딩된 버텍스 위치에 offset을 더한다.
    gl_Position = vertices[gl_VertexID] = +offset;
 
    //vs_color에 고정값을 출력한다.
    vs_color = color;
}
cs

 location 1을 가진 2번째 입력, color 선언 후 이 값을 vs_color라는 output으로 전달하고 있다.

- fragment shader

1
2
3
4
5
6
7
8
9
10
11
12
#version 430 core
 
//버텍스 쉐이더로부터의 입력
in vec4 vs_color;
 
//프레임버퍼로 출력
out vec4 color;
 
void main(void)
{
    color = vs_color;
}
cs

glAttrib로 입력하면 색이 지정 가능하다.

1
2
GLfloat colorAttrib[] = {1.0f, 0.0f, 0.0f, 1.0f};
glVertexAttrib4fv(1, colorAttrib);
cs



 3.2.1 인터페이스 블록

  - 하나씩 설정하여 전달하는 방법은 스테이지 간데이터 전송을 위한 가장 간단한 방법이다.
  - 하지만 실제 앱에서는 많은 종류의 데이터를 스테이지별로 전달한다.(배열, 구조체, 복잡한 형태의 변수들..)
-> 여러 변수를 하나의 인터페이스 블록으로 그룹화 가능!!, 구조체와 유사

*vertex Shader, 인터페이스 블록은 블록이름(VS_OUT)으로 매칭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#version 430 core
 
//offset은 입력 버텍스 속성이다.
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
 
//VS_OUT을 출력 인터페이스 블록으로 선언
out VS_OUT
{
    vec4 color; //color를 다음 스테이지로 보낸다.
}vs_out;
 
void main(void)
{
    const vec4 vertices[3= vec4[3](vec4( 0.25-0.250.51.0), 
                                     vec4(-0.25-0.250.51.0),
                                     vec4( 0.250.25, .0.51.0));
 
    //offset을 하드코딩된 버텍스 위치에 더한다.
    gl_Position = vertices[gl_VertexID] + offset;
 
    //vs_color에 고정값을 출력
    vs_out.color = color;
    
}
cs


*fragment shader, VS_OUT을 입력으로 받는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#version 430 core
 
//VS_OUT을 입력 인터페이스 블록으로 선언
in VS_OUT
{
    vec4 color; //color를 다음 스테이지로 보낸다.
}fs_in;
 
//프레임버퍼로 출력
out vec4 color;
 
void main(void)
{
    //버텍스 쉐이더로부터 받은 색상을 단순히 출력으로 저장
    color = fs_in.color;
}
cs

* 인터페이스 블록은 블록이름을 사용하여 매칭->VS_OUT, 
 그러나 블록 인스턴스는 각 쉐이더 스테이지별로 다른 이름을 가질 수 있다. (vs_out, fs_in)
- 블록을 참조하는 이름은 각 스테이지별로 다르게 할 수 있어, 혼동을 방지할수있다.(vertex에선 out, fragment에선 in)

*인터페이스 블록은 스테이지간 데이터 이동 시에만 사용이 가능하다. 입력이나 출력에 포함 불가능



728x90