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

5.2.1 디폴트 블록 유니폼


  1. 유니폼
    1. 애플리케이션이 직접 쉐이더 스테이지로 데이터를 전달하는 방법
    2. 디폴트 블록 유니폼
      1. 위치, 텍스처 좌표 등 버텍스별로 필요한 데이터가 아닌,
        전체 프리미티브 배치를 렌더링하는 동안 또는 그 후에도 변하지 않는 데이터를 쉐이더에 전달하는 방법 (ex. 변환행렬)
      2. 유니폼은 어떤 쉐이더 스테이지에도 들어갈수있다.
      3. Example
        1. uniform float fTime;
          uniform int iIndex;
      4. 항상 상수로 간주. 선언시에 디폴드 값 초기화 외에는 쉐이더 내에서 변경 불가능.
      5. 동일한 유니폼을 여러 쉐이더 스테이지에 선언한다면, 각각의 스테이지는 동일한 유니폼값을 가진다.
      6. Example
        1. layout (location = 17) uniform vec4 myUniform;
          1. 위치 17에 할당됨
          2. 설정하지 않을 시에 OpenGL이 자동으로 설정
          3. glGetUniformLocation
            1. uniform 변수에 어떤 위치가 할당되었는지 확인
            2. GLint glGetUniformLocation(GLuint program,
               const GLchar *name);
            3. Example
              1. GLint loc = glGetUniformLocation(myProgram, "vColor");
            4. 유니폼 이름이 프로그램에 없는 경우 -1 리턴
              1. 쉐이더에서 해당 유니폼을 사용하지 않을 경우,
                컴파일시 사라질 수도 있다.
            5. 대소문자 유의
      7. 설정
        1. 스칼라 및 벡터
          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);
          2. Example
            1. glUseProgram(myShader);
              glUniform1f(locTime, 12.3f);
              glUniform1i(locFlag, GL_TRUE);
              glUniform4f(locColor, 0.0f, 1.0f, 0.0f, 1.0f);
        2. 배열
          1. 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);
          2. Example
            1. GLfloat vColors[2][4] = {{1.0f, 1.0f, 1.0f, 1.0f,
                                                    1.0f, 1.0f, 1.0f, 1.0f}};
              glUniform4fv(locColor, 2, vColors);
        3. 행렬
          1. 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);
             
            1. count: 포인터 인자 value가 가리키는 행렬 개수
            2. transpose: GL_FALSE(열 우선, 기본), GL_TRUE(행 우선)

2018년 3월 26일 월요일

5.1.3 버퍼로부터 버텍스 쉐이더에 입력 전달하기


  1. 버텍스 배열 객체 (VAO : Vertex Array Object)
    1. 생성 및 바인딩
      1. GLuint vao;
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
    2. 상태 설정
      1. glVertexAttribPointer
        1. void glVertexAttribPointer(GLuint index,
          GLint size,
          GLenum type,
          GLboolean normalized,
          GLsizei stride,
          const GLvoid * pointer);
          1. index : 버텍스 속성의 인덱스
            1. 셰이더에서 location index
            2. #version 430 core
              layout (location = 0) in vec4 offset;
              ~
          2. size : 각 버텍스에 대해 버퍼에 저장된 컴포넌트의 개수
          3. type: 데이터 타입
          4. normalized: 데이터를 정규화 할 것인지 (0.0~1.0 사이)
          5. stride: 버텍스 데이터의 시작과 다음 시작점 사이에 얼마나 많은 바이트가 있는지
          6. pointer: GL_ARRAY_BUFFER에 바인딩된 버퍼상에서 버텍스  속성데이터가 시작하는 위치에 대한 오프셋
        2. Example
          1. glBindBuffer(GL_ARRAY_BUFFER, buffer);
            glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);
            1. 속성 0
            2. 4 컴포넌트
            3. 부동소수점
            4. 정규화 안함
            5. 촘촘히 패킹된 데이터
            6. 오프셋 0 (NULL 포인터)
        3. Shader
          1. #version 430 core

            layout (location = 0) in vec4 position;

            void main(void)
            {
              gl_Position = position;
            }
            1. 하드 코딩된 데이터가 삭제되고, position에 버텍스 데이터가 들어가게 된다.
    3. 설정
      1. void glEnableVertexAttribArray(GLuint index);
      2. 버퍼의 데이터를 사용해서 버텍스 속성을 채우라고 지시
      3. 몇 번째 인덱스의 속성을 활성화 할 것인지 설정 (셰이더의 속성 활성화)
    4. 해제
      1. void glDisableVertexAttribArray(GLuint index);
      2. 버텍스 속성 채우기 후 호출하여 속성 비활성화
    5. 그 외
      1. 여러 개의 버텍스 쉐이더 입력 사용하기
        1. Shader Example
          1. layout (location = 0) in vec3 position;
            layout (location = 1) in vec3 color;
        2. 입력 위치 알아내기
          1. glGetAttribLocation
            1. GLint glGetAttribLocation(GLuint program,
              const GLchar *name);
              1. program: 버텍스 쉐이더 프로그램
              2. name: 속성 이름 (쉐이더 내 변수명)
          2. 위의 쉐이더에서 "position"시 0을, "color"시 1을 리턴. 그 외에는 -1 리턴
          3. 쉐이더 코드에 위치를 설정하지 않은 경우, OpenGL이 자동으로 설정해주며 이 경우 위 함수를 사용하여 위치를 알아내야 한다.
      2. 버텍스 쉐이더 입력을 데이터로 연결시키는 방법
        1. 독립 속성
          1. 다른 버퍼에 위치해 있을 수 있거나, 적어도 동일한 버퍼에서 다른 위치에 있는 경우
          2. Example
            1. 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);
          3. 두 속성에 값을 전달하기 위해 촘촘히 패킹된 데이터 배열을 사용
          4. 배열-구조체 (SoA : Structure of Arrays) 데이터 라고 함.
        2. 인터리브 속성
          1. 구조체-배열 (AoS : Array of Structures) 데이터 사용
            1. struct vertex
              {
                float x;
                float y;
                float z;

                float r;
                float g;
                float b;
              }
            2. 두 개의 입력이 단일 구조체 안에 번갈아 들어 있음.
            3. stride 인자를 사용해야 한다.
          2. Example
            1. 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);
      3. 파일로부터 객체 로딩하기
        1. 모델 데이터를 파일에 저장하고 애플리케이션으로 로딩하는 방법

2018년 3월 20일 화요일

5.1 버퍼


  1. 버퍼
    1. 타입이 정해져 있지 않은 데이터의 연속 공간
    2. 이름으로 구별된다
    3. 버퍼를 사용하기 전에 사용할 이름을 예약 후 메모리 할당, 데이터 저장.
    4. 버퍼 객체를 위해 할당한 메모리를 데이터 스토어 라고 부른다.
    5. 버퍼를 사용하여 메모리 할당하기
      1. glBufferData
        1. void glBufferData(GLenum target,
          GLsizeiptr size,
          const GLvoid * data,
          GLenum usage);
          1. target : 버퍼가 어떤 타깃에 바인딩될지?
            1. Buffer Binding TargetPurpose
              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
          2. size : 버퍼 사이즈
          3. data : 초기화 데이터 (NULL 가능)
          4. usage : 버퍼를 어떤 목적으로 사용 할 것인지. 
            1. The symbolic constant must be GL_STREAM_DRAWGL_STREAM_READGL_STREAM_COPYGL_STATIC_DRAWGL_STATIC_READGL_STATIC_COPYGL_DYNAMIC_DRAW,GL_DYNAMIC_READ, or GL_DYNAMIC_COPY.
        2. glGenBuffers
          1. void glGenBuffers(GLsizei n,
            GLuint * buffers);
            1. 버퍼를 생성하고 GLuint 형의 핸들러를 생성한다.
        3. Example
          1. GLuint buffer;

            glGenBuffers(1, &buffer);

            glBindBuffer(GL_ARRAY_BUFFER, buffer);

            glBufferData(GL_ARRAY_BUFFER, 1024*1024, NULL, GL_STATIC_DRAW);
            1. 버퍼 생성 후 바인드. 해당 버퍼에는 1MB의 공간 할당.
            2. NULL대신에 데이터 포인터 삽입시, 해당 데이터로 초기화 가능.
        4. glBufferSubData
          1. 버퍼에 데이터를 저장
          2. void glBufferSubData(GLenum target,
            GLintptr offset,
            GLsizeiptr size,
            const GLvoid * data);
            1. target : 위와 동일한 바인딩 타겟
            2. offset : 데이터 시작 위치 (byte)
            3. size : offset으로부터 저장할 데이터 크기
            4. data : 저장할 데이터의 포인터
          3. Example
            1. static const float data[] = {~};
              glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(data), data);
        5. glMapBuffer
          1. 버퍼에 데이터를 저장
          2. void *glMapBuffer(GLenum target,
            GLenum access);
            1. target : 위와 동일한 바인딩 타겟
            2. access : access policy.
              The symbolic constant must be GL_READ_ONLYGL_WRITE_ONLY, or GL_READ_WRITE.
          3. Example
            1. static const float data[] = {~};

              void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);

              memcpy(ptr, data, sizeof(data));

              glUnmapBuffer(GL_ARRAY_BUFFER);
          4. 호출 당시 모든 데이터가 준비되지 않은 상황에서 유용하다. 데이터를 생성하거나 읽어서 메모리에 담아 둘 필요 없이, 파일 내용을 매핑된 버퍼로 직접 읽어들일 수 있음.
          5. glUnmapBuffer 호출 시 데이터 복사본을 유지하지 않음.
        6. glClearBufferSubData
          1. 버퍼에 저장하려는 데이터가 하나의 상수값일 때 유용함.
          2. void glClearBufferSubData(GLenum target,
            GLenum internalformat,
            GLintptr offset,
            GLsizeiptr size,
            GLenum format,
            GLenum type,
            const void * data);
            1. target : 위와 동일한 바인딩 타겟
            2. internalformat
              1. Sized Internal FormatBase TypeComponentsNorm0123
                GL_R8ubyte1YESR001
                GL_R16ushort1YESR001
                GL_R16Fhalf1NOR001
                GL_R32Ffloat1NOR001
                GL_R8Ibyte1NOR001
                GL_R16Ishort1NOR001
                GL_R32Iint1NOR001
                GL_R8UIubyte1NOR001
                GL_R16UIushort1NOR001
                GL_R32UIuint1NOR001
                GL_RG8ubyte2YESRG01
                GL_RG16ushort2YESRG01
                GL_RG16Fhalf2NORG01
                GL_RG32Ffloat2NORG01
                GL_RG8Ibyte2NORG01
                GL_RG16Ishort2NORG01
                GL_RG32Iint2NORG01
                GL_RG8UIubyte2NORG01
                GL_RG16UIushort2NORG01
                GL_RG32UIuint2NORG01
                GL_RGB32Ffloat3NORGB1
                GL_RGB32Iint3NORGB1
                GL_RGB32UIuint3NORGB1
                GL_RGBA8uint4YESRGBA
                GL_RGBA16short4YESRGBA
                GL_RGBA16Fhalf4NORGBA
                GL_RGBA32Ffloat4NORGBA
                GL_RGBA8Ibyte4NORGBA
                GL_RGBA16Ishort4NORGBA
                GL_RGBA32Iint4NORGBA
                GL_RGBA8UIubyte4NORGBA
                GL_RGBA16UIushort4NORGBA
                GL_RGBA32UIuint4NORGBA
            3. format : GL_RED, GL_RG, GL_RGB, GL_RGBA 중 하나로 지정
            4. type : 컴포넌트의 데이터 타입 (GL_UNSIGNED_BYTE, GL_FLOAT, GL_UNSIGNED_INT 등)
        7. glCopyBufferSubData
          1. 버퍼 간 데이터의 복사
          2. void glCopyBufferSubData(GLenum readTarget,
            GLenum writeTarget,
            GLintptr readOffset,
            GLintptr writeOffset,
            GLsizeiptr size);
            1. readTarget, writeTarget : 버퍼 바인딩 타겟
            2. readOffset, writeOffset : 데이터 시작지점(offset)
            3. size : 크기
            4. readtarget, writeTarget은 서로 데이터를 복사 할 두 버퍼가 바인딩된 타깃이다.
              1. 가용한 버퍼 바인딩 포인트에 바인딩되어 있어야 한다.
              2. 하지만 버퍼는 한 번에 오직 한 버퍼만 바인딩 가능
                1. GL_ARRAY_BUFFER 타깃인 두 버퍼간에는 복사가 불가능
              3. 이를 위해 OpenGL에서는 GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER 타깃을 제공함.