2018년 4월 3일 화요일
2018년 3월 27일 화요일
5.2.2 유니폼 블록
- 유니폼 블록
- 목적
- glUniform 함수를 매번 호출하여 발생하는 성능 저하 예방
- 유니폼 업데이트를 단순하게 할 수 있도록
- 프로그램간의 유니폼을 쉽게 공유할 수 있음
- 유니폼들을 유니폼 블록으로 그룹화 하는 방법
- 버퍼 객체에 저장
- 블록 전체를 버퍼에 저장 가능
- 유니폼 버퍼 객체 (UBO : Uniform Buffer Object)
- 프로그램을 변경해도 버퍼 바인딩은 그대로 둘 수 있기에,
프로그램들은 유니폼 값을 공유할 수 있다. - 일반적으로 사용하는 쉐이더 전역구간에 선언한 유니폼은
'디폴트 유니폼 블록'에 있게 된다. - Example
- uniform TransformBlock
{
float scale;
vec3 translation;
float rotation[3];
mat4 projection_matrix;
} transform; - 유니폼 블록 만들기
- 표준 방법
- 데이터의 레이아웃에 의존
- 애플리케이션이 버퍼로 그냥 데이터를 복사하고, 멤버의 블록 내 위치가 그대로 일치한다고 가정
- Example
- layout(std140) uniform TransformBlock
{
float scale;
vec3 translation;
float rotation[3];
mat4 projection_matrix;
} transform; - 표준 또는 std140으로 선언되면, 각 블록의 멤버는 버퍼에 정의된 양만큼의 공간을 차지함.
- std140 레이아웃과 C++ 컴파일러의 패킹 룰(타입 별 차지하는 공간, 배치에 대한 룰) 사이에는 차이가 있다. 따라서 C배열의 데이터를 그대로 복사할 수 없음.
- 공유 레이아웃 방법
- 데이터가 어디에 위치할지 OpenGL이 결정하게 하는 방법
- 성능은 좋지만 불편하고 코드가 많이 필요함
- OpenGL이 성능 및 접근성에 최적화된 형태로 버퍼에 배치
- 사용하기 위해서는 유니폼 블록 멤버에 대한 버퍼 객체 상의 위치를 애플리케이션이 결정해야 함?
- 사용하기 위해서는 OpenGL이 블록 멤버에 할당한 오프셋을 알아내야 한다.
- glGetUniformIndices
void glGetUniformIndices(GLuint program, GLsizei uniformCount, const GLchar **uniformNames, GLuint *uniformIndices );- uniformCount: 인덱스를 얻어올 유니폼의 개수
- uniformNames: 유니폼의 이름
- uniformIndices: 유니폼 인덱스 리턴값
- Example
- static const GLchar * uniformNames[4] = {
"TransformBlock.scale",
"TransformBlock.translation",
"TransformBlock.rotation",
"TransformBlock.projection_matrix"
}
GLuint uniformIndices[4];
glGetUniformIndices(program, 4, uniformNames, uniformIndices); - uniformIndices 배열 안에는 유니폼 블록의 네 멤버에 대한 인덱스가 들어감.
- glGetActiveUniformsiv
void glGetActiveUniformsiv(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params );- 버퍼 내 멤버의 오프셋, 배열 stride, 행렬 stride 등을 알아낼 수 있음
- Example
- GLint uniformOffsets[4];
GLint arrayStrides[4];
GLint matrixStrides[4];
glGetActiveUniformsiv(program, 4, uniformIndices, GL_UNIFORM_OFFSET, uniformOffsets);
glGetActiveUniformsiv(program, 4, uniformIndices, GL_UNIFORM_ARRAY_STRIDE, arrayStrides);
glGetActiveUniformsiv(program, 4, uniformIndices, GL_UNIFORM_MATRIX_STRIDE, matrixStrides); - pname: 얻는 정보
- 타입
- 사이즈
- 유니폼 이름의 길이
- 유니폼이 속한 블록 내 인덱스
- 블록 내 유니폼의 오프셋
- 배열에서 다음 요소까지의 바이트수
- 배열이 아니라면 0이다.
- 유니폼이 행렬일 때 (열우선 행렬: 열, 행우선 행렬: 행)의 각 첫번째 요소들 간의 바이트수.
- 행렬이 아니라면 0이다.
- 행우선 행렬이면 1, 열우선 행렬 또는 행렬이 아닌경우 0
- 값 설정
- scale
- 단일 부동소수점 (float)
- Example
- unsigned char * buffer = (unsigned char *)malloc(4096);
*((float *)(buffer + uniformOffsets[0])) = 3.0f; - TransformBlock.scale이 블록의 uniformOffsets[0]에 위치한다.
- translation
- vec3
- Example
- *((float *)(buffer + uniformOffsets[1]))[0] = 1.0f;
*((float *)(buffer + uniformOffsets[1]))[1] = 2.0f;
*((float *)(buffer + uniformOffsets[1]))[2] = 3.0f; - rotation
- 3요소 배열
- Example
- const GLfloat rotations[] = {30.0f, 40.0f, 50.0f};
- unsigned int offset = uniformOffsets[2];
- for (int i = 0; i < 3; i++)
- {
- *((float *)(buffer + offset)) = rotations[i];
- offset += arrayStrides[2];
- }
- projection_matrix
- Example
- const GLflost matrix[] =
- {
- // 4x4 array
- };
- for (int i = 0; i < 4; i++)
- {
- GLuint offset = uniformOffsets[3] + matrixStride[3] * i;
- for (int j = 0; j < 4; j++)
- {
- *((float *)(buffer + offset)) = matrix[i*4 + j];
- offset += sizeof(GLfloat);
- }
- }
- 데이터 버퍼를 프로그램 유니폼 블록에 바인딩
- 프로그램 내 각 유니폼 블록은 컴파일러가 할당한 인덱스를 가짐
- 유니폼 블록의 최대 개수에는 제한이 있음
- glGetIntegerv()에 GL_MAX_UNIFORM_BUFFERS로 확인
- glGetUniformBlockIndex
GLuint glGetUniformBlockIndex(GLuint program, const GLchar *uniformBlockName );- uniformBlockName: 유니폼 블록의 이름
- 바인딩 순서
- [버퍼 - 유니폼 버퍼 바인딩(바인딩 포인트) - 프로그램(유니폼 블록)]
- 유니폼 블록을 바인딩 포인트에 할당
- 바인딩 포인트에 버퍼를 바인딩
- 유니폼 블록에 바인딩 포인트를 할당
- glUniformBlockBinding
void glUniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding );- program: 유니폼 블록이 위치하는 프로그램
- uniformBlockIndex: 유니폼 블록 인덱스
- uniformBlockBinding: 유니폼 블록 바인딩 포인트의 인덱스
- 유니폼 블록의 인덱스를 쉐이더에서 직접 지정
- Example
- layout(std140, binding =2) uniform TransformBlock
- {
- ...
- }
- 바인딩 포인트에 버퍼를 바인딩
- glBindBufferBase
void glBindBufferBase(GLenum target, GLuint index, GLuint buffer );- target: GL_UNIFORM_BUFFER
- index: 바인딩 포인트에 대한 인덱스(유니폼 블록의 인덱스가 아니다.)
- buffer: attach 시키려는 버퍼 객체의 이름
- Example
- // 버퍼 A -- 바인딩 포인트 3 -- Bob
- // 버퍼 B -- 바인딩 포인트 0 -- Susan
- // 버퍼 C -- 바인딩 포인트 1 -- Harry
- GLuint harry_index = glGetUniformBlockIndex(program, "Harry");
- GLuint bob_index = glGetUniformBlockIndex(program, "Bob");
- GLuint susan_index = glGetUniformBlockIndex(program, "Susan");
- // 버퍼 바인딩 -- 유니폼 블록
- glUniformBlockBinding(program, harry_index, 1);
- glUniformBlockBinding(program, bob_index, 3);
- glUniformBlockBinding(program, susan_index, 0);
- // 버퍼 -- 바인딩 포인트
- glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer_b);
- glBindBufferBase(GL_UNIFORM_BUFFER, 1, buffer_c);
- glBindBufferBase(GL_UNIFORM_BUFFER, 3, buffer_a);
- 쉐이더 내부에서 binding으로 유니폼 블록에 대한 바인딩을 설정하면 glUniformBlockBinding 함수호출이 필요없다.
- Example
- layout (binding = 1) uniform Harry
- {
- };
- layout (binding = 3) uniform Bob
- {
- };
- layout (binding = 0) uniform Susan
- {
- };
- 쉐이더 내에 바인딩을 설정하는 방식은 OpenGL 함수 호출이 적고, 이름을 몰라도 바인딩 포인트에 유니폼 블록을 연결할 수 있음.
5.2.1 디폴트 블록 유니폼
- 유니폼
- 애플리케이션이 직접 쉐이더 스테이지로 데이터를 전달하는 방법
- 디폴트 블록 유니폼
- 위치, 텍스처 좌표 등 버텍스별로 필요한 데이터가 아닌,
전체 프리미티브 배치를 렌더링하는 동안 또는 그 후에도 변하지 않는 데이터를 쉐이더에 전달하는 방법 (ex. 변환행렬) - 유니폼은 어떤 쉐이더 스테이지에도 들어갈수있다.
- Example
- uniform float fTime;
uniform int iIndex; - 항상 상수로 간주. 선언시에 디폴드 값 초기화 외에는 쉐이더 내에서 변경 불가능.
- 동일한 유니폼을 여러 쉐이더 스테이지에 선언한다면, 각각의 스테이지는 동일한 유니폼값을 가진다.
- Example
- layout (location = 17) uniform vec4 myUniform;
- 위치 17에 할당됨
- 설정하지 않을 시에 OpenGL이 자동으로 설정
- glGetUniformLocation
- uniform 변수에 어떤 위치가 할당되었는지 확인
GLint glGetUniformLocation(GLuint program, const GLchar *name );- Example
- GLint loc = glGetUniformLocation(myProgram, "vColor");
- 유니폼 이름이 프로그램에 없는 경우 -1 리턴
- 쉐이더에서 해당 유니폼을 사용하지 않을 경우,
컴파일시 사라질 수도 있다. - 대소문자 유의
- 설정
- 스칼라 및 벡터
void glUniform1f(GLint location, GLfloat v0 );void glUniform2f(GLint location, GLfloat v0, GLfloat v1 );void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2 );void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3 );void glUniform1i(GLint location, GLint v0 );void glUniform2i(GLint location, GLint v0, GLint v1 );- Example
- glUseProgram(myShader);
glUniform1f(locTime, 12.3f);
glUniform1i(locFlag, GL_TRUE);
glUniform4f(locColor, 0.0f, 1.0f, 0.0f, 1.0f); - 배열
void glUniform1fv(GLint location, GLsizei count, const GLfloat *value );void glUniform2fv(GLint location, GLsizei count, const GLfloat *value );void glUniform3fv(GLint location, GLsizei count, const GLfloat *value );void glUniform4fv(GLint location, GLsizei count, const GLfloat *value );void glUniform1iv(GLint location, GLsizei count, const GLint *value );void glUniform2iv(GLint location, GLsizei count, const GLint *value );- Example
- GLfloat vColors[2][4] = {{1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f}};
glUniform4fv(locColor, 2, vColors); - 행렬
void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value );void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value );void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value );void glUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value );- count: 포인터 인자 value가 가리키는 행렬 개수
- transpose: GL_FALSE(열 우선, 기본), GL_TRUE(행 우선)
2018년 3월 26일 월요일
5.1.3 버퍼로부터 버텍스 쉐이더에 입력 전달하기
- 버텍스 배열 객체 (VAO : Vertex Array Object)
- 생성 및 바인딩
- GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao); - 상태 설정
- glVertexAttribPointer
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer );- index : 버텍스 속성의 인덱스
- 셰이더에서 location index
- #version 430 core
layout (location = 0) in vec4 offset;
~ - size : 각 버텍스에 대해 버퍼에 저장된 컴포넌트의 개수
- type: 데이터 타입
- normalized: 데이터를 정규화 할 것인지 (0.0~1.0 사이)
- stride: 버텍스 데이터의 시작과 다음 시작점 사이에 얼마나 많은 바이트가 있는지
- pointer: GL_ARRAY_BUFFER에 바인딩된 버퍼상에서 버텍스 속성데이터가 시작하는 위치에 대한 오프셋
- Example
- glBindBuffer(GL_ARRAY_BUFFER, buffer);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL); - 속성 0
- 4 컴포넌트
- 부동소수점
- 정규화 안함
- 촘촘히 패킹된 데이터
- 오프셋 0 (NULL 포인터)
- Shader
- #version 430 core
layout (location = 0) in vec4 position;
void main(void)
{
gl_Position = position;
} - 하드 코딩된 데이터가 삭제되고, position에 버텍스 데이터가 들어가게 된다.
- 설정
void glEnableVertexAttribArray(GLuint index );- 버퍼의 데이터를 사용해서 버텍스 속성을 채우라고 지시
- 몇 번째 인덱스의 속성을 활성화 할 것인지 설정 (셰이더의 속성 활성화)
- 해제
void glDisableVertexAttribArray(GLuint index );- 버텍스 속성 채우기 후 호출하여 속성 비활성화
- 그 외
- 여러 개의 버텍스 쉐이더 입력 사용하기
- Shader Example
- layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color; - 입력 위치 알아내기
- glGetAttribLocation
GLint glGetAttribLocation(GLuint program, const GLchar *name );- program: 버텍스 쉐이더 프로그램
- name: 속성 이름 (쉐이더 내 변수명)
- 위의 쉐이더에서 "position"시 0을, "color"시 1을 리턴. 그 외에는 -1 리턴
- 쉐이더 코드에 위치를 설정하지 않은 경우, OpenGL이 자동으로 설정해주며 이 경우 위 함수를 사용하여 위치를 알아내야 한다.
- 버텍스 쉐이더 입력을 데이터로 연결시키는 방법
- 독립 속성
- 다른 버퍼에 위치해 있을 수 있거나, 적어도 동일한 버퍼에서 다른 위치에 있는 경우
- Example
- GLuint buffers[2];
static const GLfloat positoins[] = {...};
static const GLfloat colors[] = {...};
glGenBuffers(2, &buffers);
// positions(0)
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);
// colors(1)
glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(1); - 두 속성에 값을 전달하기 위해 촘촘히 패킹된 데이터 배열을 사용
- 배열-구조체 (SoA : Structure of Arrays) 데이터 라고 함.
- 인터리브 속성
- 구조체-배열 (AoS : Array of Structures) 데이터 사용
- struct vertex
{
float x;
float y;
float z;
float r;
float g;
float b;
} - 두 개의 입력이 단일 구조체 안에 번갈아 들어 있음.
- stride 인자를 사용해야 한다.
- Example
- GLuint buffer;
static const GLfloat vertices[] = {...};
// 버퍼 객체 할당 및 초기화
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// position(0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (void *)offsetof(vertex, x));
glEnableVertexAttribArray(0);
// colors(1)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (void *)offsetof(vertex, r));
glEnableVertexAttribArray(1); - 파일로부터 객체 로딩하기
- 모델 데이터를 파일에 저장하고 애플리케이션으로 로딩하는 방법
2018년 3월 20일 화요일
5.1 버퍼
- 버퍼
- 타입이 정해져 있지 않은 데이터의 연속 공간
- 이름으로 구별된다
- 버퍼를 사용하기 전에 사용할 이름을 예약 후 메모리 할당, 데이터 저장.
- 버퍼 객체를 위해 할당한 메모리를 데이터 스토어 라고 부른다.
- 버퍼를 사용하여 메모리 할당하기
- glBufferData
void glBufferData(GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage );- target : 버퍼가 어떤 타깃에 바인딩될지?
Buffer Binding Target Purpose GL_ARRAY_BUFFERVertex attributes GL_ATOMIC_COUNTER_BUFFERAtomic counter storage GL_COPY_READ_BUFFERBuffer copy source GL_COPY_WRITE_BUFFERBuffer copy destination GL_DISPATCH_INDIRECT_BUFFERIndirect compute dispatch commands GL_DRAW_INDIRECT_BUFFERIndirect command arguments GL_ELEMENT_ARRAY_BUFFERVertex array indices GL_PIXEL_PACK_BUFFERPixel read target GL_PIXEL_UNPACK_BUFFERTexture data source GL_QUERY_BUFFERQuery result buffer GL_SHADER_STORAGE_BUFFERRead-write storage for shaders GL_TEXTURE_BUFFERTexture data buffer GL_TRANSFORM_FEEDBACK_BUFFERTransform feedback buffer GL_UNIFORM_BUFFERUniform block storage - size : 버퍼 사이즈
- data : 초기화 데이터 (NULL 가능)
- usage : 버퍼를 어떤 목적으로 사용 할 것인지.
- The symbolic constant must be
GL_STREAM_DRAW,GL_STREAM_READ,GL_STREAM_COPY,GL_STATIC_DRAW,GL_STATIC_READ,GL_STATIC_COPY,GL_DYNAMIC_DRAW,GL_DYNAMIC_READ, orGL_DYNAMIC_COPY. - glGenBuffers
void glGenBuffers(GLsizei n, GLuint * buffers );- 버퍼를 생성하고 GLuint 형의 핸들러를 생성한다.
- Example
- GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, 1024*1024, NULL, GL_STATIC_DRAW); - 버퍼 생성 후 바인드. 해당 버퍼에는 1MB의 공간 할당.
- NULL대신에 데이터 포인터 삽입시, 해당 데이터로 초기화 가능.
- glBufferSubData
- 버퍼에 데이터를 저장
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data );- target : 위와 동일한 바인딩 타겟
- offset : 데이터 시작 위치 (byte)
- size : offset으로부터 저장할 데이터 크기
- data : 저장할 데이터의 포인터
- Example
- static const float data[] = {~};
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(data), data); - glMapBuffer
- 버퍼에 데이터를 저장
void *glMapBuffer(GLenum target, GLenum access );- target : 위와 동일한 바인딩 타겟
- access : access policy.
The symbolic constant must beGL_READ_ONLY,GL_WRITE_ONLY, orGL_READ_WRITE. - Example
- static const float data[] = {~};
void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(ptr, data, sizeof(data));
glUnmapBuffer(GL_ARRAY_BUFFER); - 호출 당시 모든 데이터가 준비되지 않은 상황에서 유용하다. 데이터를 생성하거나 읽어서 메모리에 담아 둘 필요 없이, 파일 내용을 매핑된 버퍼로 직접 읽어들일 수 있음.
- glUnmapBuffer 호출 시 데이터 복사본을 유지하지 않음.
- glClearBufferSubData
- 버퍼에 저장하려는 데이터가 하나의 상수값일 때 유용함.
void glClearBufferSubData(GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void * data );- target : 위와 동일한 바인딩 타겟
- internalformat
Sized Internal Format Base Type Components Norm 0 1 2 3 GL_R8ubyte 1 YES R 0 0 1 GL_R16ushort 1 YES R 0 0 1 GL_R16Fhalf 1 NO R 0 0 1 GL_R32Ffloat 1 NO R 0 0 1 GL_R8Ibyte 1 NO R 0 0 1 GL_R16Ishort 1 NO R 0 0 1 GL_R32Iint 1 NO R 0 0 1 GL_R8UIubyte 1 NO R 0 0 1 GL_R16UIushort 1 NO R 0 0 1 GL_R32UIuint 1 NO R 0 0 1 GL_RG8ubyte 2 YES R G 0 1 GL_RG16ushort 2 YES R G 0 1 GL_RG16Fhalf 2 NO R G 0 1 GL_RG32Ffloat 2 NO R G 0 1 GL_RG8Ibyte 2 NO R G 0 1 GL_RG16Ishort 2 NO R G 0 1 GL_RG32Iint 2 NO R G 0 1 GL_RG8UIubyte 2 NO R G 0 1 GL_RG16UIushort 2 NO R G 0 1 GL_RG32UIuint 2 NO R G 0 1 GL_RGB32Ffloat 3 NO R G B 1 GL_RGB32Iint 3 NO R G B 1 GL_RGB32UIuint 3 NO R G B 1 GL_RGBA8uint 4 YES R G B A GL_RGBA16short 4 YES R G B A GL_RGBA16Fhalf 4 NO R G B A GL_RGBA32Ffloat 4 NO R G B A GL_RGBA8Ibyte 4 NO R G B A GL_RGBA16Ishort 4 NO R G B A GL_RGBA32Iint 4 NO R G B A GL_RGBA8UIubyte 4 NO R G B A GL_RGBA16UIushort 4 NO R G B A GL_RGBA32UIuint 4 NO R G B A - format : GL_RED, GL_RG, GL_RGB, GL_RGBA 중 하나로 지정
- type : 컴포넌트의 데이터 타입 (GL_UNSIGNED_BYTE, GL_FLOAT, GL_UNSIGNED_INT 등)
- glCopyBufferSubData
- 버퍼 간 데이터의 복사
void glCopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size );- readTarget, writeTarget : 버퍼 바인딩 타겟
- readOffset, writeOffset : 데이터 시작지점(offset)
- size : 크기
- readtarget, writeTarget은 서로 데이터를 복사 할 두 버퍼가 바인딩된 타깃이다.
- 가용한 버퍼 바인딩 포인트에 바인딩되어 있어야 한다.
- 하지만 버퍼는 한 번에 오직 한 버퍼만 바인딩 가능
- GL_ARRAY_BUFFER 타깃인 두 버퍼간에는 복사가 불가능
- 이를 위해 OpenGL에서는 GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER 타깃을 제공함.
피드 구독하기:
덧글 (Atom)