1. OpenGL에서 VBO와 IBO 데이터 관리
1.1 VBO 데이터 관리 기법
- glBufferData(): VBO에 데이터를 설정하는 기본 함수로, 메모리를 할당하고 데이터를 복사한다.
nullptr
을 인자로 넣으면 메모리 할당만 이루어지고 데이터 복사는 이루어지지 않는다. 이는 필요에 따라 이후 별도의 데이터 전송을 위해 미리 메모리를 할당할 때 유용하다. - glBufferSubData(): 일부 데이터만 갱신하고 싶을 때 사용하는 함수로, 기존 메모리의 특정 위치부터 필요한 데이터 크기만큼 복사하여 성능을 최적화할 수 있다. 예를 들어, 매 프레임마다 전체 데이터가 아닌 변경된 부분만 업데이트할 때 사용할 수 있다.
- glMapBuffer(), glUnmapBuffer(): 데이터 포인터를 통해 버퍼 메모리에 직접 접근할 수 있게 해주는 함수로,
glMapBuffer()
로 메모리 포인터를 가져와memcpy()
를 사용해 데이터를 복사한 후,glUnmapBuffer()
로 접근을 종료한다. 버퍼를 직접 수정할 때 유용하다. - glCopyBufferSubData(): 한 버퍼의 데이터를 다른 버퍼로 복사하는 함수로,
glBindBuffer
와 함께 GL_COPY_READ_BUFFER 및 GL_COPY_WRITE_BUFFER의 두 버퍼를 설정하여 사용한다. 메모리 복사 시 성능을 최적화할 수 있다.
1.2 텍스처 데이터 갱신
- glTexSubImage2D(): 이미 GPU 메모리가 할당된 2D 텍스처에 새로운 CPU 데이터를 업데이트하고 싶을 때 사용하는 함수이다. 특정 위치와 크기의 영역에 새로운 이미지를 복사하여 부분 갱신할 수 있어, 예를 들어 UI 요소나 애니메이션에서 특정 부분만 갱신할 때 효율적으로 사용할 수 있다.
2. Advanced GLSL - 내장 변수와 인터페이스 블록 활용
2.1 내장 변수 (Built-in Variables)
OpenGL의 GLSL에는 미리 정의된 여러 내장 변수들이 있어, 쉐이더 간의 데이터 전달이나 특정 기능을 구현하는 데 사용할 수 있다.
- gl_Position: Vertex Shader의 결과물로, 정점의 위치 좌표를 저장한다.
- gl_PointSize: 점을 그릴 때 점의 크기를 지정하는 변수로,
GL_POINTS
와 함께 활성화하여 사용한다. - gl_VertexID: 현재 쉐이더에서 처리 중인 정점의 인덱스. 인덱스를 기반으로 다양한 효과를 추가할 수 있다.
- gl_FragCoord: 현재 프래그먼트(픽셀)의 화면상 위치를 나타내는 변수로, 화면 좌표에 따라 색상을 변화시키는 등의 효과에 활용된다.
- gl_FrontFacing: 프래그먼트가 앞면인지 뒷면인지를 알 수 있는 변수로, 앞면과 뒷면을 다르게 렌더링할 때 유용하다.
- gl_FragDepth: 픽셀의 깊이값을 설정할 수 있는 변수로, 수동으로 깊이값을 조정할 수 있다.
2.2 Interface Block을 통한 변수 그룹화
쉐이더의 Vertex Shader와 Fragment Shader 간의 데이터 전송 시 변수 연결이 일관되게 관리되어야 하는데, Interface Block을 사용하면 두 쉐이더 간에 일관된 변수 그룹을 선언하고 사용할 수 있다. 마치 구조체처럼 변수를 묶어 관리할 수 있으며, 코드의 가독성과 관리 효율성을 높인다.
Interface Block 예제 코드
#version 330 core // vs
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoords;
out VS_OUT { // Vertex Shader에서 인터페이스 블록 정의
vec2 TexCoords;
} vs_out;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main() {
gl_Position = projection * view * model * vec4(aPos, 1.0);
vs_out.TexCoords = aTexCoords; // 인터페이스 블록을 통해 텍스처 좌표를 전달
}
#version 330 core // fs
in VS_OUT { // Fragment Shader에서 동일한 인터페이스 블록으로 변수 받기
vec2 TexCoords;
} fs_in;
out vec4 FragColor;
uniform sampler2D texture;
void main() {
FragColor = texture(texture, fs_in.TexCoords); // 인터페이스 블록을 통해 받은 텍스처 좌표 사용
}
3. Uniform Buffer Object (UBO)
3.1 UBO란?
UBO(Uniform Buffer Object)는 OpenGL에서 여러 쉐이더 프로그램들이 공통 변수를 한 번에 공유할 수 있도록 해주는 버퍼다. 예를 들어, model
, view
, projection
과 같은 변환 행렬은 여러 쉐이더에서 공통으로 사용되기 때문에 이를 UBO로 설정해 각 쉐이더마다 중복 설정할 필요 없이 공유할 수 있다.
3.2 UBO 생성 및 사용 방법
- UBO 생성 및 크기 설정:
glGenBuffers
로 UBO를 생성하고 사용할 데이터 크기를glBufferData
로 지정한다. - UBO에 데이터 설정: 필요한 데이터를
glBufferSubData
를 통해 입력한다. - Shader에서 UBO 연결: 쉐이더 코드에서 Uniform Block을 정의하고 이를 UBO와 연결해 사용한다.
UBO 예제 코드
아래 예제는 Matrices
라는 UBO를 생성하고, Projection과 View 행렬을 저장해 여러 쉐이더가 이를 공통으로 사용할 수 있도록 설정한 코드다.
// 1. UBO 생성 및 크기 설정
GLuint uboMatrices;
glGenBuffers(1, &uboMatrices);
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), nullptr, GL_STATIC_DRAW);
// 2. UBO에 Projection, View 행렬 데이터 설정
glm::mat4 projection = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 100.0f);
glm::mat4 view = glm::lookAt(cameraPos, cameraTarget, cameraUp);
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(projection));
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(view));
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// 3. UBO를 바인딩하여 모든 쉐이더가 동일하게 사용할 수 있게 설정
glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboMatrices);
Shader (Uniform Block 정의 및 사용)
#version 330 core
layout(location = 0) in vec3 aPos;
layout(std140) uniform Matrices {
mat4 projection;
mat4 view;
};
uniform mat4 model;
void main() {
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
위 코드를 통해 모든 쉐이더가 동일한 Matrices
UBO를 참조하며, 공통된 projection
과 view
행렬을 사용할 수 있다.
4. Geometry Shader 소개 및 사용 사례
4.1 Geometry Shader란?
Geometry Shader는 Vertex Shader와 Fragment Shader 사이에 위치하며, 특정 도형을 입력받아 새로운 도형으로 변환하거나 수정할 수 있는 쉐이더다. 예를 들어, 점을 선으로 연결하거나, 삼각형을 여러 조각으로 분해해 폭발 효과를 줄 수 있다.
4.2 Geometry Shader 설정 과정
- 입력 도형과 출력 도형 지정: Geometry Shader에서 입력받는 도형과 출력할 도형을 지정한다.
EmitVertex()
와EndPrimitive()
사용:EmitVertex()
는 현재 위치에 정점을 생성하고,EndPrimitive()
는 생성된 정점들을 하나의 도형으로 묶어준다.
Geometry Shader 예제 코드
아래는 점(Point primitive)을 입력받아 선(line strip primitive)으로 변환하는 코드로, 점이 선으로 연결되는 효과를 구현한다.
#version 330 core
layout(points) in;
layout(line_strip, max_vertices = 2) out;
void main() {
vec4 left = gl_in[0].gl_Position + vec4(-0.1, 0.0, 0.0, 0.0);
vec4 right = gl_in[0].gl_Position + vec4(0.1, 0.0, 0.0, 0.0);
gl_Position = left;
EmitVertex();
gl_Position = right;
EmitVertex();
EndPrimitive();
}
이 예제에서 EmitVertex()
는 각 정점을 출력하고, EndPrimitive()
는 현재의 도형을 마무리 짓는다. 이를 통해 점을 선으로 변환하여 다양한 그래픽 효과를 줄 수 있다.
4.3 Geometry Shader 활용 예시
- Object Exploding: Geometry Shader를 통해 삼각형의 각 정점을 중심에서 바깥으로 확장시키면, 폭발하는 듯한 효과를 줄 수 있다.
- Normal Vector 시각화: 각 삼각형의 법선 벡터를 따라 작은 선을 생성하여 물체 표면의 방향성을 시각화할 수 있다.
'Computer Graphics > OpenGL' 카테고리의 다른 글
OpenGL 정리 - 24. Shadow Mapping (0) | 2024.11.10 |
---|---|
OpenGL 정리 - 23. 인스터싱 (Instancing), 안티-앨리어싱(Anti-aliasing) (0) | 2024.10.30 |
OpenGL 정리 - 21. 큐브 맵 (Cubemap) (0) | 2024.10.28 |
OpenGL 정리 - 20. 프레임 버퍼 (Frame Buffer) (0) | 2024.10.25 |
OpenGL 정리 - 19. 스텐실(Stencil), 블랜딩(Blending), 컬링(Face Culling) (0) | 2024.10.25 |