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 함수 호출이 적고, 이름을 몰라도 바인딩 포인트에 유니폼 블록을 연결할 수 있음.

댓글 없음:

댓글 쓰기