반응형

입력을 받기 위해서는 Vertex Shader,

입력받은 정점 정보를 새로운 정점 정보로 변환하기 위해서는 Geometry Shader, 

결과 색을 출력하기 위해서는 Fragment Shader, 

가 필요하다.

 

Shader Program으로 Link하기 위해서는 3개의 Shader를 전부다 사용할 수 있지만, 

Geometry Shader가 없어도 Link하여 사용 가능하다.

Geometry Shader는 없어도 입력과 출력만으로 Shader를 사용할 수 있다.

 

Vertex Shader는 Buffer에 저장된 값을 지정하여 입력받을 수 있다.

Buffer는 Vertex Array Object, Vertex Buffer Object, Element Buffer Object, Render Buffer Object, Frame Buffer Object를 사용할 수 있다.

unsigned int vao;
glGenVertexArrays(n, &vao);
glBindVertexArrays(vao);
glDeleteVertexArrays(n, &vao);

unsigned int vbo;
glGenBuffers(n, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glDeleteBuffer(n, &vbo);

unsigned int ebo;
glGenBuffers(n, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glDeleteBuffer(n, &ebo);

unsigned int rbo;
glGenRenderbuffers(n, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glDeleteRenderbuffers(n, &rbo);

usngiend int fbo;
glGenFramebuffers(n, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glDeleteFramebuffers(n, &fbo);

GL Generate 함수 - 각 각의 Buffer를 n 크기만큼 생성할 수 있다.

GL Bind 함수 - 생성한 Buffer를 Binding하여, 사용 가능한 상태로 만든다.

GL Delete 함수 - 사용이 완료된 Buffer에 대하여, Delete를 실행하여 메모리를 해제해준다. 보통 Render Loop가 마무리 된 후에 호출한다.

 

VAO, VBO, EBO는 특정 값의 데이터들을 저장하는 Buffer이고, 

RBO, FBO는 Rendering되는 화면을 출력할 때의 데이터를 저장하는 Buffer이다.

 

Vertex Shader는 VAO와 VBO를 통해서, 데이터를 입력받는다.

float vertexPos[] = { ... };

unsigned int vao, vbo;

glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);

glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);

glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPos), vertexPos, GL_STATIC_DRAW);

glBufferData를 통해, 데이터의 값을 입력한 배열과 해당 배열의 크기, 동적 및 정적 Rendering 형태를 통해 VAO와 VBO에 데이터를 저장한다.

 

Buffer에 입력하여 저장된 데이터를 Vertex Shader에서 받아오기 위해서는, Vertex Shader에서 받아오려고 하는 데이터의 형태를 정의해주어야 한다.

#version 330 core

layout (location = 0) in vec3 aPos;

void main()
{
	gl_Position = vec4(aPos, 1.0);
}

layout (location = n) : VAO, VBO를 사용하여 데이터를 입력할 때, n번째 위치로 데이터를 받는다.

in & out : in은 데이터를 입력받고, out은 데이터를 다음 Shader로 출력한다.

 

또한 Shader 파일에서의 변수는 특이한 형태를 가진다.

위의 코드를 예로들면 vec3 타입은 float 타입이 3개가 모여있는 것이고, gl_Position은 vec4 타입으로 float 타입이 4개가 모여있다.

vec4 타입에 값을 대입할 때, vec4() 생성자 내의 float 개수가 총 4개가 되면 된다.

예를 들어,

vec4 = vec4(vec2, vec2)

vec4 = vec4(vec3.yz, float)

vec4 = vec4(vec3.xyz, vec3.y)

등과 같이 변수의 내부 값을 자유롭게 사용할 수 있는 특징을 가진다.

 

따라서 gl_Position = vec4(aPos, 1.0)은 

gl_Position = vec4(aPos.xyz, 1.0)

gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0)

과 같은 의미를 가진다.

 

Main함수에서 glBufferData가 완료되면, Buffer Data를 통해 입력된 데이터를 Vertex Shader로 출력할 수 있다.

glEnableVertexAttribArray(n);
glVertexAttribPointer(n, m, GL_FLOAT, GL_FALSE, m * sizeof(float), (void*)a);

 

glEnableVertexAttribArray 함수 : Vertex Shader에 입력할 layout 인덱스를 결정한다.

glVertexAttribPointer 함수 : layout 인덱스에 입력할 데이터의 개수 m개를 반복되서 입력될 데이터의 사이값(Stride) 크기만큼 (void*)의 위치에서부터 입력을 시작한다.

각 각의 내용을 Vertex Shader에 입력한다고 한다면, 아래의 코드가 필요하다.

#version 330 core

layout (location = 0) in vec4 aPos;
layout (location = 1) in vec4 aColor;
layout (location = 2) in vec2 aTexCoords;

out vec4 Color;
out vec2 TexCoords;

void main()
{
    gl_Position = aPos;
    Color = aColor;
    TexCoords = aTexCoords;
}
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 10 * sizeof(GL_FLOAT), (void*)0);

glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 10 * sizeof(GL_FLOAT), (void*)(4 * sizeof(GL_FLOAT)));

glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 10 * sizeof(GL_FLOAT), (void*)(8 * sizeof(GL_FLOAT)));

layout 0은 vec4 타입의 Position, 시작 지점은 배열의 0번째 인덱스부터, 10 크기 만큼의 Stride마다 반복된다.

layout 1은 vec4 타입의 Color, 시작 지점은 Position이 끝나는 4번째 인덱스부터, 10 크기 만큼의 Stride마다 반복된다.

layout 2은 vec2 타입의 Tex Coords, 시작 지점은 Color가 끝나는 8번째 인덱스부터, 10 크기 만큼의 Stride마다 반복된다.

 

 

Vertex Shader가 실행되고 나면, Shader Program으로 연결된 Geometry Shader로 out 선언한 데이터가 넘어간다.

Geometry Shader도 Vertex Shader와 마찬가지로 layout을 통해 Vertex Data를 사용한다.

#version 330 core

layout (Input Type) in;
layout (Render Type, max_vertices = n) out;

void main()
{
	EmitVertex(); // * n
    
    EndPrimitive();
}

Input Type에 해당하는 명령어는 아래와 같다.

  • points (1) : GL_POINTS
  • lines (2) : GL_LINE or GL_LINE_STRIP
  • lines_adjacency (4) : GL_LINES_ADJACENCY or GL_LINE_STRIP_ADJACENCY
  • trangles (3) : GL_TRIANGLES or GL_TRANGLE_STRIP or GL_TRANGLE_FAN
  • trangles_adjacency (6) : GL_TRANGLES_ADJACENCY or GL_TRANGLE_STRIP_ADJACENCY

각 각의 타입은 (n)의 Vertex 개수를 가져오며, Render Type에 해당하는 Rendering 작업을 하게 된다.

Render Type에 해당하는 명령어는 아래와 같다.

  • point
  • line_strip
  • triangle_strip

Render Type에 해당하는 n개의 Vertex만 출력하게 되며, Render Type에 벗어나는 Vertex는 출력하지 않는다.

 

EmitVertex() 함수가 호출되면, 현재 입력된 Vertex를 출력한 후, 다음 사이클로 넘기게 된다.

때문에 EmitVertex() 함수는 모든 Vertex가 사용되는 횟수만큼 호출하여야 한다.

Vertex를 모두 사용한 후에는, EndPrimitive() 함수를 통해 출력된 Vertex를 결합하게 된다.

 

 

Geometry Shader가 실행되면, 마찬가지로 Shader Program에 연결된 Fragment Shader로 출력된 Vertex가 넘어가게 된다.

이전에 실행된 Vertex Shader에서 out된 Vertex들이 Geometry Shader가 실행되어 적용한 Vertex로 넘어가게 되고, Geometry Shader에서는 실행되지 않았어도 Frament Shader으로 사용된다.

#version 330 core
 
out vec4 FragColor;
 
in vec4 Color;
in vec2 TexCoords;

void main()
{
    FragColor = Color;
}

Vertex Shader의 out 변수명과 Fragment Shader의 in 변수명은 일치해야 한다.

Fragment Shader에서 out된 vec4 타입 변수는 Shader Program의 최종 Rendering 출력 결과로 그려진다.

반응형

'게임 개발 끄적 > OpenGL' 카테고리의 다른 글

[OpenGL] Shader Program  (0) 2019.11.04
[OpenGL] Shader Rendering  (0) 2019.10.15
[OpenGL] 기본 코드 이해  (0) 2019.10.14
[OpenGL] 환경 설정 구축  (0) 2019.10.14

+ Recent posts