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.25, 0.5, 1.0), vec4(-0.25, -0.25, 0.5, 1.0), vec4( 0.25, 0.25, 0.5, 1.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.25, 0.5, 1.0), vec4(-0.25, -0.25, 0.5, 1.0), vec4( 0.25, 0.25, 0.5, 1.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.25, 0.5, 1.0), vec4(-0.25, -0.25, 0.5, 1.0), vec4( 0.25, 0.25, .0.5, 1.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)
*인터페이스 블록은 스테이지간 데이터 이동 시에만 사용이 가능하다. 입력이나 출력에 포함 불가능
'Software Development > Graphics' 카테고리의 다른 글
OpenGL Super Bible 그래픽스 공부하기 - 3. 파이프라인 따라가기 - 3) 지오메트리 쉐이더 (0) | 2018.01.21 |
---|---|
OpenGL Super Bible 그래픽스 공부하기 - 3. 파이프라인 따라가기 - 2) 테셀레이션 (0) | 2018.01.21 |
OpenGL Super Bible 그래픽스 공부하기 - 2.3 처음 그리는 삼각형 (0) | 2018.01.14 |
우분투 환경에서 SDL2 + GL 설정하고 빌드해보기 (0) | 2018.01.14 |
OpenGL Super Bible 그래픽스 공부하기 - 2. 첫번째 OpenGL 프로그램 (0) | 2018.01.14 |