- 렌더링 성능과 퀄리티를 높이는 텍스쳐 기법
- 기존 밉맵을 사용하지 않을 때의 문제점
- 반짝거림 현상
- 렌더링 크기가 텍스쳐 크기보다 작은 경우.
- 성능
- 밉맵이란?
- 여러 크기의 밉맵 텍스쳐를 준비하고, 현재에 가장 적합한 텍스쳐를 선택하는 기법
- 초기 설정
- glTexSubImage2D
void glTexSubImage2D(
GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid * data )
;- level
- 0, 1, 2 ~ 순
- 밉맵을 사용하지 않을 경우 0으로 설정
- 밉맵 기본 레벨 및 최대 레벨 지정
- glTexParameteri
void glTexParameteri(
GLenum target, GLenum pname, GLint param )
;- 예제
- // 기본 레벨을 0으로
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
- // 최대 레벨을 4로
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);
- 밉맵 필터링
- 텍스쳐 필터링의 종류
-
GL_NEAREST
- Returns the value of the texture element that is nearest (in Manhattan distance) to the center of the pixel being textured.
GL_LINEAR
- Returns the weighted average of the four texture elements that are closest to the center of the pixel being textured.
GL_NEAREST_MIPMAP_NEAREST
- Chooses the mipmap that most closely matches the size of the pixel being textured and uses the
GL_NEAREST
criterion (the texture element nearest to the center of the pixel) to produce a texture value.GL_LINEAR_MIPMAP_NEAREST
- Chooses the mipmap that most closely matches the size of the pixel being textured and uses the
GL_LINEAR
criterion (a weighted average of the four texture elements that are closest to the center of the pixel) to produce a texture value.GL_NEAREST_MIPMAP_LINEAR
- Chooses the two mipmaps that most closely match the size of the pixel being textured and uses the
GL_NEAREST
criterion (the texture element nearest to the center of the pixel) to produce a texture value from each mipmap. The final texture value is a weighted average of those two values.GL_LINEAR_MIPMAP_LINEAR
- Chooses the two mipmaps that most closely match the size of the pixel being textured and uses the
GL_LINEAR
criterion (a weighted average of the four texture elements that are closest to the center of the pixel) to produce a texture value from each mipmap. The final texture value is a weighted average of those two values. - https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexParameter.xml
- GL_LINEAR나 GL_NEAREST로 설정하는 경우 밉맵을 사용하지 않는다.
- GL_NEAREST_MIPMAP_NEAREST는 성능이 좋고 Aliasing이 적다.
- GL_LINEAR_MIPMAP_NEAREST가 일반적으로 게임에서 많이 사용되는데,
더 좋은 필터링 결과와 다른 크기의 맵 레발 간에 밉맵선택이 빠르기 때문. - 위의 GL_*_MIPMAP_* GL_TEXTURE_MIN_FILTER에만 적용이 가능하다.
GL_TEXTURE_MAG_FILTER는 GL_LINEAR 또는 GL_NEAREST여야 한다. - Nearest 방식은 밉 레벨이 바뀌는 부분에 일그러진 구간이 보일 수 있으며, 이는 GL_LINEAR_MIPMAP_LINEAR 또는 GL_NEAREST_MIPMAP_LINEAR로 해결할 수 있으나, 추가 연산으로 오버헤드가 발생한다.
- GL_LINEAR_MIPMAP_LINEAR는 삼중선형 밉맵이라고도 하며 좋은 결과를 나타낸다.
2018년 7월 31일 화요일
5.5.5-[3, 4] 밉맵(Mipmap), 밉맵 필터링
2018년 5월 7일 월요일
5.5.5-[1,2] 여러 텍스쳐 사용하기, 텍스쳐 필터링
- 여러 텍스쳐 사용하기
- 하나의 쉐이더에서 여러 텍스쳐를 사용하려면 여러 샘플러 유니폼을 만들고, 각각 다른 텍스쳐 유닛을 참조하도록 설정해야 함
이 때 여러 텍스쳐를 Context에 binding 필요 - 최대 유닛 개수
- Example
- GLint units;
- glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &units);
- 텍스처를 특정 텍스처 유닛에 바인딩
- Example
- 텍스쳐 유닛 5 선택
- glActiveTexture(GL_TEXTURE0 + 5);
- 예약 토큰 사용
- GLuint textures[3];
- glGenTextures(3, &textures);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, textures[0]);
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, textures[2]);
- 쉐이더 내에서 샘플러 유니폼이 각각의 텍스쳐 유닛을 참조하도록 설정
- glUniform 사용
void glUniform1f(
GLint location, GLfloat v0 )
;- location: shader 내 uniform 변수의 binding location
- v0: 변수에 넣을 값
- 쉐이더 코드에서 샘플러는 유니폼으로 선언되기 때문에
glGetUniformLocation으로 그 위치를 찾아 값을 수정한다. - Example
- GLint loc = glGenUniformLocation(mProgram, "name_of_uniform_variable");
- glUniform1i(loc, GL_TEXTURE2);
- binding 레이아웃 지시어 사용
- Example
- Shader
- layout (binding = 0) uniform sampler2D tex1;
- layout (binding = 1) uniform sampler2D tex2;
- Code
- glUniform1i(0, GL_TEXTURE0);
- glUniform1i(1, GL_TEXTURE1);
- 텍스쳐 필터링
- 텍셀과 화면의 픽셀이 일대일 매칭이 되는 경우는 거의 없음.
- 텍스쳐 이미지는 늘어나거나 줄어들어 지오메트리 서피스에 적용된다.
- texelFetch
gvec4 texelFetch(
gsampler2DRect sampler, ivec2 P )
;- 텍스쳐로부터 특정 정수 텍스쳐 좌표를 사용하여 하나의 텍셀을 읽어온다.
- 프래그먼트 대 텍셀 비율이 정수가 아닌 경우에는 이 함수를 사용하지 않는다.
- texture
gvec4 texture(
gsampler2DRect sampler, vec2 P )
;- 부동소수점 텍스쳐 좌표를 인자로 받음.
- [0.0 ~ 1.0] 사이의 범위가 텍스쳐에 매핑된다.
범위 밖에 있는 값도 가능하며, 이를 사용하여 확대/축소등이 가능 - 늘어나거나 줄어든 텍스쳐 맵으로부터 컬러 프래그먼트를 계산하는 작업을
텍스쳐 필터링이라고 한다. - 텍스쳐를 늘리는 작업을 확대라고 하며, 줄이는 작업을 축소라고 한다.
- 확대 및 축소 필터 사용
- 샘플러 인자로 GL_TERXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER를 사용하며 GL_NEAREST와 GL_LINEAR 사용가능.
- GL_NEAREST (최단 인접 필터링)
- 가장 단순하며 빠른 필터링
- 텍스쳐가 늘어나는 경우 블록 모양의 픽셀을 만든다.
- Example
- glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glSamplerParameteri(sampler, GL_TEXTURE_MAX_FILTER, GL_NEAREST);
- GL_LINEAR (선형 필터링)
- 텍스쳐 좌표에 가장 가까운 텍셀을 구하고, 텍스쳐 좌표 주변 텍셀의 가중치 평균(선형 보간)을 사용하여 계산.
- 텍스쳐가 늘어날 때 흐려지는 효과가 있어서 최단 인접 필터링에 비해 더 자연스럽다.
- Example
- glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
https://learnopengl.com/Getting-started/Textures |
2018년 4월 24일 화요일
5.5.[4-5] 파일로부터 텍스처 로딩하기, 텍스처 데이터 읽는 방식 제어하기 (1/2)
- 파일로부터 텍스처 로딩하기
- KTX (Khronos TeXture format)
- glTexStorage2D 및glTexSubImage2D 등의 텍스처 함수에 전달할 대부분의 인자를 포함하기 때문에 파일의 텍스처를 직접 로딩할 수 있다.
- https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
- 텍스처 좌표
- Example
- Vertex shader
- #version 430 core
- uniform mat4 mv_matrix;
- uniform mat4 proj_matrix;
- layout (location = 0) in vec4 position;
- layout (location = 4) in vec2 tc;
- out VS_OUT
- {
- vec2 tc;
- } vs_out;
- void main(void)
- {
- // 각 버텍스 위치 계산
- vec4 pos_vs = mv_matrix * position;
- // 텍스처 좌표를 그대로 전달
- vs_out.tc = tc;
- gl_Position = proj_matrix * pos_vs;
- }
- Fragment shader
- #version 430 core
- layout (binding = 0) uniform sapler2D tex_object;
- in VS_OUT
- {
- vec2 tc;
- } fs_in;
- out vec4 color;
- void main(void)
- {
- // 받은 좌표값을 스케일하여 텍스쳐를 출력한다.
- color = texture(tex_object, fs_in.tc * vec2(2.0, 3.0));
- }
- 텍스처 데이터 읽는 방식 제어하기
- 일반적으로 텍스처 좌표는 정규화된다. (0.0 ~ 1.0)
- 래핑 모드
- (0.0 ~ 1.0) 범위를 벗어나는 텍스처 좌표에 대해 어떻게 처리할지
- 필터링 모드
- 샘플들의 중간값을 어떻게 처리할지
- 샘플러의 래핑 및 필터링 모드를 제어하는 인자들은 샘플러 객체에 저장된다.
- 샘플러 객체 생성
void glGenSamplers(
GLsizei n, GLuint *samplers )
;- n: 생성 할 샘플러 개수
- samplers: 샘플러 객체를 저장할 배열 주소
- 샘플러 인자 설정
void glSamplerParameterf(
GLuint sampler, GLenum pname, GLfloat param )
;void glSamplerParameteri(
GLuint sampler, GLenum pname, GLint param )
;- sampler: 샘플러 객체 핸들러
- pname: 샘플러 파라미터의 심볼릭 네임
GL_TEXTURE_WRAP_S
,GL_TEXTURE_WRAP_T
,GL_TEXTURE_WRAP_R
,GL_TEXTURE_MIN_FILTER
,GL_TEXTURE_MAG_FILTER
,GL_TEXTURE_BORDER_COLOR
,GL_TEXTURE_MIN_LOD
,GL_TEXTURE_MAX_LOD
,GL_TEXTURE_LOD_BIAS
GL_TEXTURE_COMPARE_MODE
, orGL_TEXTURE_COMPARE_FUNC
- param: 샘플러 파라미터
- 샘플러 객체를 텍스처 유닛에 바인딩
void glBindSampler(
GLuint unit, GLuint sampler )
;- unit: 텍스처 유닛의 인덱스
- 텍스처로부터 텍스처 샘플러의 인자를 분리함으로써의 장점
- 텍스처가 많은 경우에도 동일한 샘플링 인자를 사용할 수 있다.
- 바인딩된 텍스처를 변경해도 샘플러 인자는 변경하지 않아도 된다.
- 동일한 텍스처에 여러 다른 샘플러 인자를 사용가능.
- 텍스처 안에 저장된 샘플러 객체 사용
- 타깃에 바인딩 한 후 함수 호출
void glTexParameterf(
GLenum target, GLenum pname, GLfloat param )
;void glTexParameteri(
GLenum target, GLenum pname, GLint param )
;- target: 텍스쳐 종류 (GL_TEXTURE_2D 등)
- pname: 텍스쳐 파라미터의 심볼릭 네임
GL_DEPTH_STENCIL_TEXTURE_MODE
,GL_TEXTURE_BASE_LEVEL
,GL_TEXTURE_COMPARE_FUNC
,GL_TEXTURE_COMPARE_MODE
,GL_TEXTURE_LOD_BIAS
,GL_TEXTURE_MIN_FILTER
,GL_TEXTURE_MAG_FILTER
,GL_TEXTURE_MIN_LOD
,GL_TEXTURE_MAX_LOD
,GL_TEXTURE_MAX_LEVEL
,GL_TEXTURE_SWIZZLE_R
,GL_TEXTURE_SWIZZLE_G
,GL_TEXTURE_SWIZZLE_B
,GL_TEXTURE_SWIZZLE_A
,GL_TEXTURE_WRAP_S
,GL_TEXTURE_WRAP_T
, orGL_TEXTURE_WRAP_R
- param: pname에 따른 파라미터 (GL_NEAREST 등)
2018년 4월 22일 일요일
5.5.[1-3] 텍스쳐 생성 및 초기화, 텍스쳐 타깃과 타입, 쉐이더에서 텍스쳐 읽기
- 쉐이더에서 읽기/쓰기 가능한 구조화된 스토리지 형식
- 대부분 이미지 데이터로 사용
- 2D 레이아웃을 가장 일반적으로 사용하지만, (1차원, 3차원, 배열, 정육면체 등)으로 생성 가능하다.
- 텍스쳐 생성 및 초기화
- [생성 - 바인딩 - 텍스쳐 스토리지 할당]
- Example
- GLuint texture;
- glGenTextures(1, &texture);
- // GL_TEXTURE_2D 바인딩 포인트를 사용하여 컨텍스트에 바인딩
- glBindTexture(GL_TEXTURE_2D, texture);
- glTexStorage2D(
GL_TEXTURE_2D, // 2D텍스쳐
1, // 1 밉맵레벨
GL_RGBA32F, // 32비트 부동소수점 RGBA 데이터(텍스쳐 내부 포맷)
256, 256); // 256*256 텍셀 - 텍스쳐 데이터 갱신
- Example
- float *data = new float[256 * 256 * 4];
- // data에 이미지 데이터 삽입 프로세스
- glTexSubImage2D(
GL_TEXTURE_2D, // 2D 텍스쳐
0, // 레벨 0
0, 0, // 오프셋 0, 0
256, 256, // 256 * 256 텍셀, 전체 이미지 덮어씀
GL_RGBA, // 4 Channel
GL_FLOAT, // 부동 소수점
data); - delete [] data;
- 텍스쳐 타깃과 타입
GL_TEXTURE_1D
,GL_TEXTURE_2D
,GL_TEXTURE_3D
,GL_TEXTURE_1D_ARRAY
,GL_TEXTURE_2D_ARRAY
,GL_TEXTURE_RECTANGLE
,GL_TEXTURE_CUBE_MAP
,GL_TEXTURE_CUBE_MAP_ARRAY
,GL_TEXTURE_BUFFER
,GL_TEXTURE_2D_MULTISAMPLE
orGL_TEXTURE_2D_MULTISAMPLE_ARRAY
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindTexture.xhtmlGL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY
단일 객체로 합쳐진 텍스쳐 이미지 배열
GL_TEXTURE_BUFFER
1D 텍스쳐와 유사하지만 스토리지가 실제 버퍼 객체로 표현되는 특별한 텍스쳐 타입
1D 텍스쳐의 최대크기보다 훨씬 더 크다.
- GL_TEXTURE_2D_MULTISAMPLE
- 멀티 샘플 안티엘리어싱에서 사용
- 쉐이더에서 텍스쳐 읽기
- 텍스쳐는 쉐이더에서 샘플러 변수로 사용됨
- 2차원 텍스쳐를 나타내는 샘플러 타입은 sampler2D
- Example
- #version 430 core
- uniform sampler2D s;
- out vec4 color;
- void main(void)
- {
- color = texelFetch(s, ivec2(gl_FragCoord.xy), 0);
- }
- gl_FragCoord로 부터 구한 텍스쳐 좌표를 사용하여 유니폼 샘플러로부터 읽음.
gvec4 texelFetch(
gsampler2D sampler, ivec2 P, int lod )
;- lod: 밉맵 레벨 (1 레벨일 경우 0)
- 샘플러 타입
GLSL sampler OpenGL texture enum Texture type gsampler1D GL_TEXTURE_1D 1D texture gsampler2D GL_TEXTURE_2D 2D texture gsampler3D GL_TEXTURE_3D 3D texture gsamplerCube GL_TEXTURE_CUBE_MAP Cubemap Texture gsampler2DRect GL_TEXTURE_RECTANGLE Rectangle Texture gsampler1DArray GL_TEXTURE_1D_ARRAY 1D Array Texture gsampler2DArray GL_TEXTURE_2D_ARRAY 2D Array Texture gsamplerCubeArray GL_TEXTURE_CUBE_MAP_ARRAY Cubemap Array Texture
(requires GL 4.0 or ARB_texture_cube_map_array)gsamplerBuffer GL_TEXTURE_BUFFER Buffer Texture gsampler2DMS GL_TEXTURE_2D_MULTISAMPLE Multisample Texture gsampler2DMSArray GL_TEXTURE_2D_MULTISAMPLE_ARRAY Multisample Array Texture - 정수형 샘플러는 앞에 i를 붙이고, unsigned는 u
(ex. isampler2D, usampler2D)
2018년 4월 18일 수요일
5.4 어토믹 카운터
- 여러 쉐이더에서 공유되는 특별한 변수 타입
- 버퍼 객체에 의해 채워진다.
- 버퍼에 저장된 값을 증가시키거나 감소시키며, Atomic 한 특징이 있다.
- 쉐이더에서 동시에 호출하는 경우 Atomic은 보장되지만, 연산이 수행되는 순서는 보장되지 않으므로 특정 값을 받을 것이라고 기대하면 안된다.
- 버퍼 바인딩 인덱스 및 버퍼 내 오프셋은 binding 및 offset 레이아웃 지시어로 설정할 수 있다.
- 선언
- layout (binding = 3, offset = 8) uniform atomic_unit my_variable;
- binding: 어토믹 카운터 바인딩 포인트
- offset: 버퍼 내 오프셋
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | #include "OpenGL.h" #ifdef EX5_20 GLuint program; GLuint buffer; int getWindowWidth() { return 800; } int getWindowHeight() { return 600; } void onAwake() { static const char * vs_source[] = { "#version 430 core \n" " \n" "layout (binding = 0) uniform area_block \n" "{ \n" " uint counter_value; \n" "}; \n" " \n" "out vec4 color; \n" " \n" "uniform float max_area; \n" " \n" "void main(void) \n" "{ \n" " float brightness = clamp(float(counter_value) / max_area, \n" " 0.0, 1.0); \n" " color = vec4(brightness, brightness, brightness, 1.0); \n" "} \n" }; static const char * fs_source[] = { "#version 430 core \n" " \n" "layout (binding = 0, offset = 0) uniform atomic_uint area; \n" " \n" "void main(void) \n" "{ \n" " atomicCounterIncrement(area); \n" "} \n" }; program = glCreateProgram(); GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fs, 1, fs_source, NULL); glCompileShader(fs); GLuint vs = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vs, 1, vs_source, NULL); glCompileShader(vs); CheckShaderCompileError(fs); CheckShaderCompileError(vs); glAttachShader(program, vs); glAttachShader(program, fs); glLinkProgram(program); glGenBuffers(1, &buffer); glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, buffer); glBufferData(GL_ATOMIC_COUNTER_BUFFER, 16 * sizeof(GLuint), NULL, GL_DYNAMIC_COPY); glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 3, buffer); const GLuint zero = 0; glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, buffer); // 방법 1 - glBufferSubData를 사용하여 어토믹 카운터를 리셋한다. glBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 2 * sizeof(GLuint), sizeof(GLuint), &zero); // 방법 2 - 버퍼를 매핑하여 직접 값을 쓴다 //GLuint *data = (GLuint *)glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, 16 * sizeof(GLuint), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT); //data[2] = 0; //glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER); //// 방법 3 - glCelarBufferSubData를 사용한다. //glClearBufferSubData(GL_ATOMIC_COUNTER_BUFFER, // GL_R32UI, // 2 * sizeof(GLuint), // sizeof(GLuint), // GL_RED_INTEGER, GL_UNSIGNED_INT, &zero); } void onUpdate(double current_time) { } void onShutdown() { glDeleteProgram(program); glDeleteBuffers(1, &buffer); } #endif | cs |
2018년 4월 11일 수요일
5.3 쉐이더 스토리지 블록
- 쉐이더 스토리지 블록
- 유니폼 블록과 쉐이더 스토리지 블록의 차이점
- 쉐이더가 쉐이더 스토리지 블록에 쓸 수 있다.
- 쉐이더 스토리지 공간의 멤버에 대해 어토믹 연산을 수행할 수 있다.
- 쉐이더 스토리지 블록은 크기 제한값이 훨씬 더 크다.
- std430의 새로운 패킹 규칙을 통해 데이터가 유니폼 블록보다 더 효율적으로 패킹되며 더 직접적인 접근이 가능하다.
- 사용
- 유니폼 블록을 선언하듯이 쉐이더에서 블록을 선언하면 된다.
단, uniform 키워드 대신 buffer 키워드 사용. - std140 뿐만 아니라 std430 패킹 레이아웃도 지원한다.
- 패킹 레이아웃 이름은 쉐이딩 언어 버전을 따라 지어졌다. std140은 GLSL1.40(OpenGL 3.1), std430은 GLSL 4.30(OpenGL 4.3)
- Example
- #version 430 core
- struct my_structure
- {
- int pea;
- int carrot;
- vec4 potato;
- };
- layout (binding = 0, std430) buffer my_storage_block
- {
- vec4 foo;
- vec3 bar;
- int baz[24];
- my_structure veggies;
- };
- glBufferData를 사용하여 버퍼에 데이터를 전달 할 수 있다.
- glMapBuffer를 GL_READ_ONLY로 사용하여 쉐이더에서 생산한 데이터를 읽을 수 있다.
- 버텍스 속성 대신 쉐이더 스토리지 블록 사용
- #version 430 core
- struct vertex
- {
- vec4 position;
- vec3 color;
- };
- layout (binding = 0, std430) buffer my_vertices
- {
- vertex vertices[];
- };
- uniform mat4 transform_matrix;
- out VS_OUT
- {
- vec3 color;
- };
- void main(void)
- {
- gl_Position = transform_matrix * vertices[gl_VertexID].position;
- vs_out.color = vertices[gl_VertexID].color;
- }
- 유니폼 블록이나 버텍스 속성에 비해 최적화가 어려움.
- 어토믹 메모리 연산
- 메모리 읽기와 쓰기 중간에 인터럽트가 일어나지 않아 결과가 보장되는 것
- 같은 메모리 위치의 값을 동시에 접근하여 수정하면서 충돌이 발생함.
- 어토믹 메모리 함수
int atomicAdd(
inout int mem, int data )
;uint atomicAdd(
inout uint mem, uint data )
;- mem을 읽어 data에 더한다. 결과를 mem에 쓰고, 원래 mem에 저장되어 있던 값을 리턴한다.
- atomicAnd, atomicOr, atomicXor 등
- 리턴형이 int와 unit가 있다.
- 어토믹 연산이 동일 위치에 동시에 발생하는 경우 각 호출은 직렬화 됨.
- 메모리 관련 문제
- RAW(Read-After-Write): 메모리에 쓴 다음 읽기를 시도하는 경우, 시스템에서 순서가 조정되어 쓰기가 완료되기 전에 읽기를 수행하는 경우.
- WAW(Write-After-Write): 쓰는 도중에 쓰는 경우
- WAR(Write-After-Read): 병렬 처리 시스템에서 다른 쓰레드간 동일 위치에 쓰기를 수행하는 경우.
- OpenGL의 병렬성 때문에 메모리 문제를 피하기 위한 여러가지 방법이 필요하다.
- 메모리 베리어(Memory Barrier)
void glMemoryBarrier(
GLbitfield barriers )
;- GL_ALL_BARRIER_BITS
- 모든 메모리 연산 순서를 동기화
- GL_SHADER_STORAGE_BARRIER_BIT
- 다음 쉐이더가 데이터를 액세스 하기 전에 이전 접근(쓰기)를 완료하도록 함.
- 베리어 이후에 실행하는 쉐이더는 쓰기 동작이 완료된 후의 데이터를 보게 됨.
- GL_UNIFORM_BARRIER_BIT
- 데이터를 쓴 메모리가 베리어 이후에 유니폼 버퍼로 사용되는 경우, 쓰기가 완료 될 때 까지 기다렸다가 유니폼 버퍼를 사용하도록 함.
- GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT
- 버텍스 속성을 통해 버텍스 데이터로 버퍼를 사용하기 전에 버퍼 쓰기 작업을 완료하도록 함.
- GLSL
uint memoryBarrier(
void )
;- 쉐이더 내에서 호출 시 기존에 수행했던 메모리 읽기/쓰기 동작을 완료시킴.
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 함수 호출이 적고, 이름을 몰라도 바인딩 포인트에 유니폼 블록을 연결할 수 있음.
피드 구독하기:
글 (Atom)