일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 26 | 27 | 28 | 29 | 30 |
- 프로젝트 여러개
- git
- 수리명제
- VAO
- 튜링기계
- .sln
- vertex
- multi process
- OpenGL
- fragment
- fetching
- 정의
- turingmachine
- 자동판결
- qtspim stack
- glfw
- Qtspim
- glDrawArrays
- 멀티프로세스
- 시작
- vertex sahder
- vertexarrayobject
- shader
- superbible
- 하나의 솔루션
- 솔루션에프로젝트추가
- Tutorial
- visualstudip
- interface block
- 파일생성 명령어
- Today
- Total
공사중
[SB]Vertex fetch, Vertex shader, shader 간 데이터 전달(in/out) 본문
[SB]Vertex fetch, Vertex shader, shader 간 데이터 전달(in/out)
행운개발자 LuckyDeveloper 2019. 3. 2. 21:37- 버텍스 패치
- 버텍스 쉐이더
- 테셀레이션 컨트롤 쉐이더
- 테셀레이션
- 테셀레이션 이벨류에이션 쉐이더
- 지오메트리 쉐이더
- 래스터라이제이션
- 프래그먼트 쉐이더
- 프레임버퍼 동작
vertex fetch부터 vertex shader까지
openGL에서 첫 번째 프로그래밍가능 스테이지이자 유일한 필수 스테이지인 vertex shader stage이전에는 vertex fetch 스테이지가 실행됩니다. GLSL에서 쉐이더로 데이터를 가져오거나 보내는 작업은 in/out 키워드를 사용해서 전역변수를 선언하는 방식으로 이루어집니다. 이전에 작성했던 fragment shader에서 out 키워드를 사용해서 색상을 출력했습니다. 이처럼 이전 쉐이더에서 out 키워드를 포함하고, 다음 쉐이더에서 in 키워드를 포함하는 방식으로 데이터를 전송할 수 있습니다. (fragment는 마지막 단계라서 이 결과값을 받는 in 키워드는 없습니다.)
그럼 전체 스테이지에서 첫 번째 단계인 vertex fetch에서 vertex shader로 어떻게 데이터가 전달되는지 알아보겠습니다. 먼저 vertex shader의 코드에 변화가 있습니다.
//vertex shader #version 430 core 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 = vertives[gl_VertexID] + offset; }
vertices 벡터를 정의하고 각 벡터에 gl_VertexID를 이용해서 접근한 뒤 변수 offset을 더해서 내장변수 gl_Position에 더했습니다. offset을 선언하는 라인은 아래와 같은 뜻입니다.
- vector 4개를 구성요소로 갖는 변수 offset을 전역변수로 정의한다.
- 이 변수는 이전단계로부터 전달되어서 사용된다(in keyword)
- 이런 방법으로 정의된 변수들은 vertex attribute라고 불린다.
- 여러 개의 vertex attribute 중에서 이 attribute의 변호를 0번으로 붙힌다.
- 물론 지금은 단하나의 attribute만 정의했다.
그렇다면 첫 번째 프로그래밍 가능 스테이지인 vertex shader에는 어떤 방식으로 값을 전달할까요?
glVertexAttrib*() glVertexAttrib4fv(GLuint index, const GLfloat* v); glVetexAttrib4fv(0, attrib);
위 메서드는 render 함수에서 호출됩니다. index인자는 속성을 참조하기 위한 값(몇 번째 attribute를 사용해서 그림을 그릴 것인지 정의)이고, v는 속성에 넣을 새로운 데이터를 가리키는 포인터입니다.
virtual void render(double currentTime) { const GLfloat color[] = { (float)sin(currentTime) * 0.5f + 0.5, (float)cos(currentTime) * 0.5f + 0.5, 0.0f, 1.0f }; glClearBufferfv(GL_COLOR, 0, color); glUseProgram(rendering_program); GLfloat attrib[] = { (float)sin(currentTime) * 0.5f, (float)cos(currentTime) * 0.6f, 0.0f, 0.0f }; //offset의 값을 vertex shader로 전달한다. glVertexAttrib4fv(0, attrib); glDrawArrays(GL_TRIANGEL, 0, 3); }
shader 간 데이터 전달은 in/out 키워드를 사용하지만, fixed 된 stage에서 shader로 데이터를 보낼 때는 사전 정의된 메서드를 사용하면 되는 것을 보았습니다. 이제 shader간 데이터 전달을 작성해보겠습니다.
//vertex shader #version 430 core layout(location = 0) in vec4 offset; layout(location = 1) in vec4 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 = vertives[gl_VertexID] + offset; //vs_color에 고정값을 출력한다. vs_color = color; }
위 vertex shader에서는 두 개의 vertex attribute를 정의했습니다. 그리고 각각에 0,1이라는 vertex attribute index를 부여했습니다. 그리고 이 값들을 가지고 내장변수 gl_Position과 vs_color 변수의 값을 결정했습니다. 이제 이 출력값 vs_color를 fragment shader에서 받아서 사용할 수 있습니다.
//fragment shader #version 430 core //vertex shader로부터 입력을 받음 in vec4 vs_color; //마지막 단계인 프레임 버퍼에 출력값을 전달 out vec4 color; void main(void) { //입력값을 변환하지 않고 그대로 출력값으로 전달 color = vs_color; }
이제 시작부터 끝까지 데이터가 이동하는 것을 구현해봤으므로, 삼각형에 새로운 색을 입힐 수 있게 되었습니다.
Interface block
인터페이스 변수를 한 번에 하나씩 설정하는 것은 가장 간단한 방법의 shader 데이터 전송방법입니다. 하지만 대부분의 실제 app에서는 많은 다른 종류으 데이터를 스테이지별로 전송합니다. 이러한 데이터에는 배열, 구조체 그리고 다른 복잡한 형태의 변수들이 표함됩니다. 이를 위해서 여러 변수를 하나의 interface block으로 그룹화할 수 있습니다. 이 방법은 구조체를 선언하는 방식과 많이 유사합니다.
//vertex shader #version 430 core layout(location = 0) in vec4 offset; layout(location = 1) in vec4 color; out VS_OUT{ vec4 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 = vertives[gl_VertexID] + offset; //vs_color에 고정값을 출력한다. vs_color.color = color; }
대문자로 적은 VS_OUT은 블록이름이고 vs_out는 인스터스 이름입니다. 스테이지 간 데이터 전달에서 in/out키워드에서의 이름이 매치되면 데이터 전송이 가능한 것이고, 실제로 shader 내부에서는 인스턴스의 이름이 사용됩니다.
//fragment shader #version 430 core //vertex shader로부터 입력을 받음 in VS_OUT{ vec4 color; } fs_in; //마지막 단계인 프레임 버퍼에 출력값을 전달 out vec4 color; void main(void) { color = fs_in.color; }
interface block은 블록이름을 사용해서 매칭시키지만, 블록 instance는 각 쉐이더 스테이지 별로 다른 이름을 가질 수 있습니다. VS_OUT의 인스턴스 이름이 vs_out이었다가 fs_in으로 바뀐 것처럼 말입니다. 이것이 가능한 이유는 두 가지입니다.
- 블록 인스터스가 각 쉐이더 스테이지별로 다른 이름을 가짐으로서 좀 더 편하게(혼동하지않고)사용하기 위함
- 특정 쉐이더 스테이지 간에 걸쳐서 단일 아이템들이 배열로도 사용될 수 있는 interface를 제공하기 때문
또한 interface block은 shader간 데이터 전달에 사용되므로 vertex shader의 입력이나, fragment shader의 출력에 사용될 수 없습니다.
'개발 | OpenGL > OpenGL Super Bible' 카테고리의 다른 글
[SB] Tesellation Control Shader (0) | 2019.03.04 |
---|---|
[SB] window에 삼각형 그리기 (0) | 2019.03.01 |
[SB] 예제 프로젝트 빌드와 실행해보기 (1) | 2019.02.27 |
[SB] 전체적인 흐름 짚어보기 (0) | 2019.02.26 |
[SB] 들어가기 (0) | 2019.02.25 |