태그

레이블이 OpenGL Super Bible인 게시물을 표시합니다. 모든 게시물 표시
레이블이 OpenGL Super Bible인 게시물을 표시합니다. 모든 게시물 표시

2018년 7월 31일 화요일

5.5.5-[3, 4] 밉맵(Mipmap), 밉맵 필터링


  1. 렌더링 성능과 퀄리티를 높이는 텍스쳐 기법
  2. 기존 밉맵을 사용하지 않을 때의 문제점
    1. 반짝거림 현상
      1. 렌더링 크기가 텍스쳐 크기보다 작은 경우. 
      2. 성능
  3. 밉맵이란?
    1. 여러 크기의 밉맵 텍스쳐를 준비하고, 현재에 가장 적합한 텍스쳐를 선택하는 기법
  4. 초기 설정
    1. glTexSubImage2D
      1. void glTexSubImage2D(GLenum target,
        GLint level,
        GLint xoffset,
        GLint yoffset,
        GLsizei width,
        GLsizei height,
        GLenum format,
        GLenum type,
        const GLvoid * data);
        1. level
          1. 0, 1, 2 ~ 순
          2. 밉맵을 사용하지 않을 경우 0으로 설정
  5. 밉맵 기본 레벨 및 최대 레벨 지정
    1. glTexParameteri
      1. void glTexParameteri(GLenum target,
        GLenum pname,
        GLint param);
    2. 예제
      1. // 기본 레벨을 0으로
      2. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);

      3. // 최대 레벨을 4로
      4. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);
  6. 밉맵 필터링
    1. 텍스쳐 필터링의 종류
      1. 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.
      2. https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexParameter.xml
    2. GL_LINEAR나 GL_NEAREST로 설정하는 경우 밉맵을 사용하지 않는다.
    3. GL_NEAREST_MIPMAP_NEAREST는 성능이 좋고 Aliasing이 적다.
    4. GL_LINEAR_MIPMAP_NEAREST가 일반적으로 게임에서 많이 사용되는데,
      더 좋은 필터링 결과와 다른 크기의 맵 레발 간에 밉맵선택이 빠르기 때문.
    5. 위의 GL_*_MIPMAP_* GL_TEXTURE_MIN_FILTER에만 적용이 가능하다.
      GL_TEXTURE_MAG_FILTER는 GL_LINEAR 또는 GL_NEAREST여야 한다.
    6. Nearest 방식은 밉 레벨이 바뀌는 부분에 일그러진 구간이 보일 수 있으며, 이는 GL_LINEAR_MIPMAP_LINEAR 또는 GL_NEAREST_MIPMAP_LINEAR로 해결할 수 있으나, 추가 연산으로 오버헤드가 발생한다.
    7. GL_LINEAR_MIPMAP_LINEAR는 삼중선형 밉맵이라고도 하며 좋은 결과를 나타낸다.

2018년 5월 7일 월요일

5.5.5-[1,2] 여러 텍스쳐 사용하기, 텍스쳐 필터링


  1. 여러 텍스쳐 사용하기
    1. 하나의 쉐이더에서 여러 텍스쳐를 사용하려면 여러 샘플러 유니폼을 만들고, 각각 다른 텍스쳐 유닛을 참조하도록 설정해야 함
      이 때 여러 텍스쳐를 Context에 binding 필요
    2. 최대 유닛 개수
      1. Example
        1. GLint units;
        2. glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &units);
    3. 텍스처를 특정 텍스처 유닛에 바인딩
      1. Example
        1. 텍스쳐 유닛 5 선택
          1. glActiveTexture(GL_TEXTURE0 + 5);
        2. 예약 토큰 사용
          1. GLuint textures[3];

          2. glGenTextures(3, &textures);

          3. glActiveTexture(GL_TEXTURE0);
          4. glBindTexture(GL_TEXTURE_2D, textures[0]);

          5. glActiveTexture(GL_TEXTURE2);
          6. glBindTexture(GL_TEXTURE_2D, textures[2]);
    4. 쉐이더 내에서 샘플러 유니폼이 각각의 텍스쳐 유닛을 참조하도록 설정
      1. glUniform 사용
        1. void glUniform1f(GLint location,
          GLfloat v0);
          1. location: shader 내 uniform 변수의 binding location
          2. v0: 변수에 넣을 값
        2. 쉐이더 코드에서 샘플러는 유니폼으로 선언되기 때문에
          glGetUniformLocation으로 그 위치를 찾아 값을 수정한다.
        3. Example
          1. GLint loc = glGenUniformLocation(mProgram, "name_of_uniform_variable");
          2. glUniform1i(loc, GL_TEXTURE2);
      2. binding 레이아웃 지시어 사용
        1. Example
          1. Shader
            1. layout (binding = 0) uniform sampler2D tex1;
            2. layout (binding = 1) uniform sampler2D tex2;
          2. Code
            1. glUniform1i(0, GL_TEXTURE0);
            2. glUniform1i(1, GL_TEXTURE1);
  2. 텍스쳐 필터링
    1. 텍셀과 화면의 픽셀이 일대일 매칭이 되는 경우는 거의 없음.
    2. 텍스쳐 이미지는 늘어나거나 줄어들어 지오메트리 서피스에 적용된다.
    3. texelFetch
      1. gvec4 texelFetch(gsampler2DRect sampler,
        ivec2 P);
        1. 텍스쳐로부터 특정 정수 텍스쳐 좌표를 사용하여 하나의 텍셀을 읽어온다.
        2. 프래그먼트 대 텍셀 비율이 정수가 아닌 경우에는 이 함수를 사용하지 않는다.
    4. texture
      1. gvec4 texture(gsampler2DRect sampler,
        vec2 P);
        1. 부동소수점 텍스쳐 좌표를 인자로 받음.
        2. [0.0 ~ 1.0] 사이의 범위가 텍스쳐에 매핑된다.
          범위 밖에 있는 값도 가능하며, 이를 사용하여 확대/축소등이 가능
    5. 늘어나거나 줄어든 텍스쳐 맵으로부터 컬러 프래그먼트를 계산하는 작업을
      텍스쳐 필터링이라고 한다.
    6. 텍스쳐를 늘리는 작업을 확대라고 하며, 줄이는 작업을 축소라고 한다.
    7. 확대 및 축소 필터 사용
      1. 샘플러 인자로 GL_TERXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER를 사용하며 GL_NEAREST와 GL_LINEAR 사용가능.
      2. GL_NEAREST (최단 인접 필터링)
        1. 가장 단순하며 빠른 필터링
        2. 텍스쳐가 늘어나는 경우 블록 모양의 픽셀을 만든다.
        3. Example
          1. glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
          2. glSamplerParameteri(sampler, GL_TEXTURE_MAX_FILTER, GL_NEAREST);
      3. GL_LINEAR (선형 필터링)
        1. 텍스쳐 좌표에 가장 가까운 텍셀을 구하고, 텍스쳐 좌표 주변 텍셀의 가중치 평균(선형 보간)을 사용하여 계산.
        2. 텍스쳐가 늘어날 때 흐려지는 효과가 있어서 최단 인접 필터링에 비해 더 자연스럽다.
          1. Example
            1. glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            2. glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        https://learnopengl.com/Getting-started/Textures

2018년 4월 24일 화요일

5.5.[4-5] 파일로부터 텍스처 로딩하기, 텍스처 데이터 읽는 방식 제어하기 (1/2)


  1. 파일로부터 텍스처 로딩하기
    1. KTX (Khronos TeXture format)
      1. glTexStorage2D 및glTexSubImage2D 등의 텍스처 함수에 전달할 대부분의 인자를 포함하기 때문에 파일의 텍스처를 직접 로딩할 수 있다.
      2. https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
  2. 텍스처 좌표
    1. Example
      1. Vertex shader
        1. #version 430 core

        2. uniform mat4 mv_matrix;
        3. uniform mat4 proj_matrix;

        4. layout (location = 0) in vec4 position;
        5. layout (location = 4) in vec2 tc;

        6. out VS_OUT
        7. {
        8.   vec2 tc;
        9. } vs_out;

        10. void main(void)
        11. {
        12.   // 각 버텍스 위치 계산
        13.   vec4 pos_vs = mv_matrix * position;

        14.   // 텍스처 좌표를 그대로 전달
        15.   vs_out.tc = tc;

        16.   gl_Position = proj_matrix * pos_vs;
        17. }
      2. Fragment shader
        1. #version 430 core

        2. layout (binding = 0) uniform sapler2D tex_object;

        3. in VS_OUT
        4. {
        5.   vec2 tc;
        6. } fs_in;

        7. out vec4 color;

        8. void main(void)
        9. {
        10.   // 받은 좌표값을 스케일하여 텍스쳐를 출력한다.
        11.   color = texture(tex_object, fs_in.tc * vec2(2.0, 3.0));
        12. }
  3. 텍스처 데이터 읽는 방식 제어하기
    1. 일반적으로 텍스처 좌표는 정규화된다. (0.0 ~ 1.0)
    2. 래핑 모드
      1. (0.0 ~ 1.0) 범위를 벗어나는 텍스처 좌표에 대해 어떻게 처리할지
    3. 필터링 모드
      1. 샘플들의 중간값을 어떻게 처리할지
    4. 샘플러의 래핑 및 필터링 모드를 제어하는 인자들은 샘플러 객체에 저장된다.
    5. 샘플러 객체 생성
      1. void glGenSamplers(GLsizei n,
         GLuint *samplers);
        1. n: 생성 할 샘플러 개수
        2. samplers: 샘플러 객체를 저장할 배열 주소
    6. 샘플러 인자 설정
      1. void glSamplerParameterf(GLuint sampler,
         GLenum pname,
         GLfloat param);
         
        void glSamplerParameteri(GLuint sampler,
         GLenum pname,
         GLint param);
        1. sampler: 샘플러 객체 핸들러
        2. pname: 샘플러 파라미터의 심볼릭 네임
          GL_TEXTURE_WRAP_SGL_TEXTURE_WRAP_TGL_TEXTURE_WRAP_RGL_TEXTURE_MIN_FILTERGL_TEXTURE_MAG_FILTER,GL_TEXTURE_BORDER_COLORGL_TEXTURE_MIN_LODGL_TEXTURE_MAX_LODGL_TEXTURE_LOD_BIAS GL_TEXTURE_COMPARE_MODE, or GL_TEXTURE_COMPARE_FUNC 
        3. param: 샘플러 파라미터
    7. 샘플러 객체를 텍스처 유닛에 바인딩
      1. void glBindSampler(GLuint unit,
         GLuint sampler);
        1. unit: 텍스처 유닛의 인덱스
    8. 텍스처로부터 텍스처 샘플러의 인자를 분리함으로써의 장점
      1. 텍스처가 많은 경우에도 동일한 샘플링 인자를 사용할 수 있다.
      2. 바인딩된 텍스처를 변경해도 샘플러 인자는 변경하지 않아도 된다.
      3. 동일한 텍스처에 여러 다른 샘플러 인자를 사용가능.
    9. 텍스처 안에 저장된 샘플러 객체 사용
      1. 타깃에 바인딩 한 후 함수 호출
      2. void glTexParameterf(GLenum target,
         GLenum pname,
         GLfloat param);
         
        void glTexParameteri(GLenum target,
         GLenum pname,
         GLint param);
        1. target: 텍스쳐 종류 (GL_TEXTURE_2D 등)
        2. pname: 텍스쳐 파라미터의 심볼릭 네임
          GL_DEPTH_STENCIL_TEXTURE_MODEGL_TEXTURE_BASE_LEVELGL_TEXTURE_COMPARE_FUNCGL_TEXTURE_COMPARE_MODE,GL_TEXTURE_LOD_BIASGL_TEXTURE_MIN_FILTERGL_TEXTURE_MAG_FILTERGL_TEXTURE_MIN_LODGL_TEXTURE_MAX_LODGL_TEXTURE_MAX_LEVELGL_TEXTURE_SWIZZLE_RGL_TEXTURE_SWIZZLE_GGL_TEXTURE_SWIZZLE_B,GL_TEXTURE_SWIZZLE_AGL_TEXTURE_WRAP_SGL_TEXTURE_WRAP_T, or GL_TEXTURE_WRAP_R
        3. param: pname에 따른 파라미터 (GL_NEAREST 등)

2018년 4월 22일 일요일

5.5.[1-3] 텍스쳐 생성 및 초기화, 텍스쳐 타깃과 타입, 쉐이더에서 텍스쳐 읽기


  1. 쉐이더에서 읽기/쓰기 가능한 구조화된 스토리지 형식
  2. 대부분 이미지 데이터로 사용
  3. 2D 레이아웃을 가장 일반적으로 사용하지만, (1차원, 3차원, 배열, 정육면체 등)으로 생성 가능하다.
  4. 텍스쳐 생성 및 초기화
    1. [생성 - 바인딩 - 텍스쳐 스토리지 할당]
    2. Example
      1. GLuint texture;

      2. glGenTextures(1, &texture);

      3. // GL_TEXTURE_2D 바인딩 포인트를 사용하여 컨텍스트에 바인딩
      4. glBindTexture(GL_TEXTURE_2D, texture);

      5. glTexStorage2D(
        GL_TEXTURE_2D,  // 2D텍스쳐
        1,  // 1 밉맵레벨
        GL_RGBA32F,  // 32비트 부동소수점 RGBA 데이터(텍스쳐 내부 포맷)
        256, 256);  // 256*256 텍셀
  5. 텍스쳐 데이터 갱신
    1. Example
      1. float *data = new float[256 * 256 * 4];

      2. // data에 이미지 데이터 삽입 프로세스

      3. glTexSubImage2D(
        GL_TEXTURE_2D,  // 2D 텍스쳐
        0,  // 레벨 0
        0, 0,  // 오프셋 0, 0
        256, 256,  // 256 * 256 텍셀, 전체 이미지 덮어씀
        GL_RGBA, // 4 Channel
        GL_FLOAT, // 부동 소수점
        data);

      4. delete [] data;
  6. 텍스쳐 타깃과 타입
    1. GL_TEXTURE_1DGL_TEXTURE_2DGL_TEXTURE_3DGL_TEXTURE_1D_ARRAYGL_TEXTURE_2D_ARRAYGL_TEXTURE_RECTANGLEGL_TEXTURE_CUBE_MAP,GL_TEXTURE_CUBE_MAP_ARRAYGL_TEXTURE_BUFFERGL_TEXTURE_2D_MULTISAMPLE or GL_TEXTURE_2D_MULTISAMPLE_ARRAY
      https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindTexture.xhtml
      1. GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY
        1. 단일 객체로 합쳐진 텍스쳐 이미지 배열
      2. GL_TEXTURE_BUFFER
        1. 1D 텍스쳐와 유사하지만 스토리지가 실제 버퍼 객체로 표현되는 특별한 텍스쳐 타입
        2. 1D 텍스쳐의 최대크기보다 훨씬 더 크다.
      3. GL_TEXTURE_2D_MULTISAMPLE
        1. 멀티 샘플 안티엘리어싱에서 사용
  7. 쉐이더에서 텍스쳐 읽기
    1. 텍스쳐는 쉐이더에서 샘플러 변수로 사용됨
    2. 2차원 텍스쳐를 나타내는 샘플러 타입은 sampler2D
    3. Example
      1. #version 430 core

      2. uniform sampler2D s;

      3. out vec4 color;

      4. void main(void)
      5. {
      6.   color = texelFetch(s, ivec2(gl_FragCoord.xy), 0);
      7. }
        1. gl_FragCoord로 부터 구한 텍스쳐 좌표를 사용하여 유니폼 샘플러로부터 읽음.
        2. gvec4 texelFetch(gsampler2D sampler,
           ivec2 P,
           int lod);
          1. lod: 밉맵 레벨 (1 레벨일 경우 0)
    4. 샘플러 타입
      1. GLSL samplerOpenGL texture enumTexture type
        gsampler1DGL_TEXTURE_1D1D texture
        gsampler2DGL_TEXTURE_2D2D texture
        gsampler3DGL_TEXTURE_3D3D texture
        gsamplerCubeGL_TEXTURE_CUBE_MAPCubemap Texture
        gsampler2DRectGL_TEXTURE_RECTANGLERectangle Texture
        gsampler1DArrayGL_TEXTURE_1D_ARRAY1D Array Texture
        gsampler2DArrayGL_TEXTURE_2D_ARRAY2D Array Texture
        gsamplerCubeArrayGL_TEXTURE_CUBE_MAP_ARRAYCubemap Array Texture
        (requires GL 4.0 or ARB_texture_cube_map_array)
        gsamplerBufferGL_TEXTURE_BUFFERBuffer Texture
        gsampler2DMSGL_TEXTURE_2D_MULTISAMPLEMultisample Texture
        gsampler2DMSArrayGL_TEXTURE_2D_MULTISAMPLE_ARRAYMultisample Array Texture
        https://www.khronos.org/opengl/wiki/Sampler_(GLSL)
      2. 정수형 샘플러는 앞에 i를 붙이고, unsigned는 u
        (ex. isampler2D, usampler2D)

2018년 4월 18일 수요일

5.4 어토믹 카운터


  1. 여러 쉐이더에서 공유되는 특별한 변수 타입
  2. 버퍼 객체에 의해 채워진다.
  3. 버퍼에 저장된 값을 증가시키거나 감소시키며, Atomic 한 특징이 있다.
  4. 쉐이더에서 동시에 호출하는 경우 Atomic은 보장되지만, 연산이 수행되는 순서는 보장되지 않으므로 특정 값을 받을 것이라고 기대하면 안된다.
  5. 버퍼 바인딩 인덱스 및 버퍼 내 오프셋은 binding 및 offset 레이아웃 지시어로 설정할 수 있다.
  6. 선언
    1. layout (binding = 3, offset = 8) uniform atomic_unit my_variable;
      1. binding: 어토믹 카운터 바인딩 포인트
      2. 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 쉐이더 스토리지 블록


  1. 쉐이더 스토리지 블록
    1. 유니폼 블록과 쉐이더 스토리지 블록의 차이점
      1. 쉐이더가 쉐이더 스토리지 블록에 쓸 수 있다.
      2. 쉐이더 스토리지 공간의 멤버에 대해 어토믹 연산을 수행할 수 있다.
      3. 쉐이더 스토리지 블록은 크기 제한값이 훨씬 더 크다.
      4. std430의 새로운 패킹 규칙을 통해 데이터가 유니폼 블록보다 더 효율적으로 패킹되며 더 직접적인 접근이 가능하다.
    2. 사용
      1. 유니폼 블록을 선언하듯이 쉐이더에서 블록을 선언하면 된다.
        단, uniform 키워드 대신 buffer 키워드 사용.
      2. std140 뿐만 아니라 std430 패킹 레이아웃도 지원한다.
        1. 패킹 레이아웃 이름은 쉐이딩 언어 버전을 따라 지어졌다. std140은 GLSL1.40(OpenGL 3.1), std430은 GLSL 4.30(OpenGL 4.3)
      3. Example
        1. #version 430 core

        2. struct my_structure
        3. {
        4.   int pea;
        5.   int carrot;
        6.   vec4 potato;
        7. };

        8. layout (binding = 0, std430) buffer my_storage_block
        9. {
        10.   vec4 foo;
        11.   vec3 bar;
        12.   int baz[24];
        13.   my_structure veggies;
        14. };
    3. glBufferData를 사용하여 버퍼에 데이터를 전달 할 수 있다.
    4. glMapBuffer를 GL_READ_ONLY로 사용하여 쉐이더에서 생산한 데이터를 읽을 수 있다.
    5. 버텍스 속성 대신 쉐이더 스토리지 블록 사용
      1. #version 430 core

      2. struct vertex
      3. {
      4.   vec4 position;
      5.   vec3 color;
      6. };

      7. layout (binding = 0, std430) buffer my_vertices
      8. {
      9.   vertex vertices[];
      10. };

      11. uniform mat4 transform_matrix;

      12. out VS_OUT
      13. {
      14. vec3 color;
      15. };

      16. void main(void)
      17. {
      18.   gl_Position = transform_matrix * vertices[gl_VertexID].position;
      19.   vs_out.color = vertices[gl_VertexID].color;
      20. }
    6. 유니폼 블록이나 버텍스 속성에 비해 최적화가 어려움.
  2. 어토믹 메모리 연산
    1. 메모리 읽기와 쓰기 중간에 인터럽트가 일어나지 않아 결과가 보장되는 것
    2. 같은 메모리 위치의 값을 동시에 접근하여 수정하면서 충돌이 발생함.
    3. 어토믹 메모리 함수
      1. int atomicAdd(inout int mem,
         int data);
         
        uint atomicAdd(inout uint mem,
         uint data);
        1. mem을 읽어 data에 더한다. 결과를 mem에 쓰고, 원래 mem에 저장되어 있던 값을 리턴한다.
      2. atomicAnd, atomicOr, atomicXor 등
      3. 리턴형이 int와 unit가 있다.
      4. 어토믹 연산이 동일 위치에 동시에 발생하는 경우 각 호출은 직렬화 됨.
    4. 메모리 관련 문제
      1. RAW(Read-After-Write): 메모리에 쓴 다음 읽기를 시도하는 경우, 시스템에서 순서가 조정되어 쓰기가 완료되기 전에 읽기를 수행하는 경우.
      2. WAW(Write-After-Write): 쓰는 도중에 쓰는 경우
      3. WAR(Write-After-Read): 병렬 처리 시스템에서 다른 쓰레드간 동일 위치에 쓰기를 수행하는 경우.
    5. OpenGL의 병렬성 때문에 메모리 문제를 피하기 위한 여러가지 방법이 필요하다.
    6. 메모리 베리어(Memory Barrier)
      1. void glMemoryBarrier(GLbitfield barriers);
        1. GL_ALL_BARRIER_BITS
          1. 모든 메모리 연산 순서를 동기화
        2. GL_SHADER_STORAGE_BARRIER_BIT
          1. 다음 쉐이더가 데이터를 액세스 하기 전에 이전 접근(쓰기)를 완료하도록 함.
          2. 베리어 이후에 실행하는 쉐이더는 쓰기 동작이 완료된 후의 데이터를 보게 됨.
        3. GL_UNIFORM_BARRIER_BIT
          1. 데이터를 쓴 메모리가 베리어 이후에 유니폼 버퍼로 사용되는 경우, 쓰기가 완료 될 때 까지 기다렸다가 유니폼 버퍼를 사용하도록 함.
        4. GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT
          1. 버텍스 속성을 통해 버텍스 데이터로 버퍼를 사용하기 전에 버퍼 쓰기 작업을 완료하도록 함.
      2. GLSL
        1. uint memoryBarrier(void);
          1. 쉐이더 내에서 호출 시 기존에 수행했던 메모리 읽기/쓰기 동작을 완료시킴.

2018년 3월 27일 화요일

5.2.2 유니폼 블록


  1. 유니폼 블록
    1. 목적
      1. glUniform 함수를 매번 호출하여 발생하는 성능 저하 예방
      2. 유니폼 업데이트를 단순하게 할 수 있도록
      3. 프로그램간의 유니폼을 쉽게 공유할 수 있음
    2. 유니폼들을 유니폼 블록으로 그룹화 하는 방법
    3. 버퍼 객체에 저장
      1. 블록 전체를 버퍼에 저장 가능
      2. 유니폼 버퍼 객체 (UBO : Uniform Buffer Object)
        1. 프로그램을 변경해도 버퍼 바인딩은 그대로 둘 수 있기에,
          프로그램들은 유니폼 값을 공유할 수 있다.
      3. 일반적으로 사용하는 쉐이더 전역구간에 선언한 유니폼은
        '디폴트 유니폼 블록'에 있게 된다.
    4. Example
      1. uniform TransformBlock
        {
          float scale;
          vec3 translation;
          float rotation[3];
          mat4 projection_matrix;
        } transform;
    5. 유니폼 블록 만들기
      1. 표준 방법
        1. 데이터의 레이아웃에 의존
        2. 애플리케이션이 버퍼로 그냥 데이터를 복사하고, 멤버의 블록 내 위치가 그대로 일치한다고 가정
        3. Example
          1. layout(std140) uniform TransformBlock
            {
              float scale;
              vec3 translation;
              float rotation[3];
              mat4 projection_matrix;
            } transform;
            1. 표준 또는 std140으로 선언되면, 각 블록의 멤버는 버퍼에 정의된 양만큼의 공간을 차지함.
            2. std140 레이아웃과 C++ 컴파일러의 패킹 룰(타입 별 차지하는 공간, 배치에 대한 룰) 사이에는 차이가 있다. 따라서 C배열의 데이터를 그대로 복사할 수 없음.
      2. 공유 레이아웃 방법
        1. 데이터가 어디에 위치할지 OpenGL이 결정하게 하는 방법
        2. 성능은 좋지만 불편하고 코드가 많이 필요함
        3. OpenGL이 성능 및 접근성에 최적화된 형태로 버퍼에 배치
        4. 사용하기 위해서는 유니폼 블록 멤버에 대한 버퍼 객체 상의 위치를 애플리케이션이 결정해야 함?
        5. 사용하기 위해서는 OpenGL이 블록 멤버에 할당한 오프셋을 알아내야 한다.
        6. glGetUniformIndices
          1. void glGetUniformIndices(GLuint program,
            GLsizei uniformCount,
            const GLchar **uniformNames,
            GLuint *uniformIndices);
            1. uniformCount: 인덱스를 얻어올 유니폼의 개수
            2. uniformNames: 유니폼의 이름
            3. uniformIndices: 유니폼 인덱스 리턴값
          2. Example
            1. static const GLchar * uniformNames[4] = {
                "TransformBlock.scale",
                "TransformBlock.translation",
                "TransformBlock.rotation",
                "TransformBlock.projection_matrix"
              }

              GLuint uniformIndices[4];
              glGetUniformIndices(program, 4, uniformNames, uniformIndices);
            2. uniformIndices 배열 안에는 유니폼 블록의 네 멤버에 대한 인덱스가 들어감.
          3. glGetActiveUniformsiv
            1. void glGetActiveUniformsiv(GLuint program,
              GLsizei uniformCount,
              const GLuint *uniformIndices,
              GLenum pname,
              GLint *params);
            2. 버퍼 내 멤버의 오프셋, 배열 stride, 행렬 stride 등을 알아낼 수 있음
            3. Example
              1. 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);
            4. pname: 얻는 정보
              1. 타입
              2. 사이즈
              3. 유니폼 이름의 길이
              4. 유니폼이 속한 블록 내 인덱스
              5. 블록 내 유니폼의 오프셋
              6. 배열에서 다음 요소까지의 바이트수
                1. 배열이 아니라면 0이다.
              7. 유니폼이 행렬일 때 (열우선 행렬: 열, 행우선 행렬: 행)의 각 첫번째 요소들 간의 바이트수.
                1. 행렬이 아니라면 0이다.
              8. 행우선 행렬이면 1, 열우선 행렬 또는 행렬이 아닌경우 0
          4. 값 설정
            1. scale
              1. 단일 부동소수점 (float)
              2. Example
                1. unsigned char * buffer = (unsigned char *)malloc(4096);

                  *((float *)(buffer + uniformOffsets[0]))  = 3.0f;
                2. TransformBlock.scale이 블록의 uniformOffsets[0]에 위치한다.
            2. translation
              1. vec3
              2. Example
                1. *((float *)(buffer + uniformOffsets[1]))[0]  = 1.0f;
                  *((float *)(buffer + uniformOffsets[1]))[1]  = 2.0f;
                  *((float *)(buffer + uniformOffsets[1]))[2]  = 3.0f;
            3. rotation
              1. 3요소 배열
              2. Example
                1. const GLfloat rotations[] = {30.0f, 40.0f, 50.0f};
                2. unsigned int offset = uniformOffsets[2];

                3. for (int i = 0; i < 3; i++)
                4. {
                5.   *((float *)(buffer + offset))  = rotations[i];
                6.   offset += arrayStrides[2];
              3. }
            4. projection_matrix
              1. Example
                1. const GLflost matrix[] =
                2. {
                3.   // 4x4 array
                4. };

                5. for (int i = 0; i < 4; i++)
                6. {
                7.   GLuint offset = uniformOffsets[3] + matrixStride[3] * i;
                8.   for (int j = 0; j < 4; j++)
                9.   {
                10.     *((float *)(buffer + offset)) = matrix[i*4 + j];
                11.     offset += sizeof(GLfloat);
                12.   }
                13. }
    6. 데이터 버퍼를 프로그램 유니폼 블록에 바인딩
      1. 프로그램 내 각 유니폼 블록은 컴파일러가 할당한 인덱스를 가짐
      2. 유니폼 블록의 최대 개수에는 제한이 있음
        1. glGetIntegerv()에 GL_MAX_UNIFORM_BUFFERS로 확인
      3. glGetUniformBlockIndex
        1. GLuint glGetUniformBlockIndex(GLuint program,
          const GLchar *uniformBlockName);
          1. uniformBlockName: 유니폼 블록의 이름
      4. 바인딩 순서
        1. [버퍼 - 유니폼 버퍼 바인딩(바인딩 포인트) - 프로그램(유니폼 블록)]
        2. 유니폼 블록을 바인딩 포인트에 할당
        3. 바인딩 포인트에 버퍼를 바인딩
      5. 유니폼 블록에 바인딩 포인트를 할당
        1. glUniformBlockBinding
          1. void glUniformBlockBinding(GLuint program,
            GLuint uniformBlockIndex,
            GLuint uniformBlockBinding);
          2. program: 유니폼 블록이 위치하는 프로그램
          3. uniformBlockIndex: 유니폼 블록 인덱스
          4. uniformBlockBinding: 유니폼 블록 바인딩 포인트의 인덱스
      6. 유니폼 블록의 인덱스를 쉐이더에서 직접 지정
        1. Example
          1. layout(std140, binding =2) uniform TransformBlock
          2. {
          3.   ...
          4. }
      7. 바인딩 포인트에 버퍼를 바인딩
        1. glBindBufferBase
          1. void glBindBufferBase(GLenum target,
            GLuint index,
            GLuint buffer);
            1. target: GL_UNIFORM_BUFFER
            2. index: 바인딩 포인트에 대한 인덱스(유니폼 블록의 인덱스가 아니다.)
            3. buffer: attach 시키려는 버퍼 객체의 이름
      8. Example
        1. // 버퍼 A -- 바인딩 포인트 3 -- Bob
        2. // 버퍼 B -- 바인딩 포인트 0 -- Susan
        3. // 버퍼 C -- 바인딩 포인트 1 -- Harry

        4. GLuint harry_index = glGetUniformBlockIndex(program, "Harry");
        5. GLuint bob_index = glGetUniformBlockIndex(program, "Bob");
        6. GLuint susan_index = glGetUniformBlockIndex(program, "Susan");

        7. // 버퍼 바인딩 -- 유니폼 블록
        8. glUniformBlockBinding(program, harry_index, 1);
        9. glUniformBlockBinding(program, bob_index, 3);
        10. glUniformBlockBinding(program, susan_index, 0);

        11. // 버퍼 -- 바인딩 포인트
        12. glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer_b);
        13. glBindBufferBase(GL_UNIFORM_BUFFER, 1, buffer_c);
        14. glBindBufferBase(GL_UNIFORM_BUFFER, 3, buffer_a);
          1. 쉐이더 내부에서 binding으로 유니폼 블록에 대한 바인딩을 설정하면  glUniformBlockBinding 함수호출이 필요없다.
            1. Example
              1. layout (binding = 1) uniform Harry
              2. {
              3. };

              4. layout (binding = 3) uniform Bob
              5. {
              6. };

              7. layout (binding = 0) uniform Susan
              8. {
              9. };
            2. 쉐이더 내에 바인딩을 설정하는 방식은 OpenGL 함수 호출이 적고, 이름을 몰라도 바인딩 포인트에 유니폼 블록을 연결할 수 있음.