Home [learn-opengl] Advanced OpenGL: Advanced GLSL
Post
Cancel

[learn-opengl] Advanced OpenGL: Advanced GLSL

Advanced GLSL

  • 흥미로운 내장 변수들과 shader의 입력과 출력을 구성하는 새로운 방법, uniform buffer objects라고 불리는 유용한 도구를 다룰 것이다.

GLSL’s built-in variables

  • 셰이더는 극도로 파이프라인되어 있다.
    • shader 밖의 다른 곳의 데이터가 필요한 경우, 데이터를 전달해야 함.
    • vertex attributes, uniforms, sampler
  • GLSL은 추가적인 여러 내장 변수들을 가짐.

    • gl_ 접두사가 붙어있음.
    • 데이터를 모으거나 작성하는 추가적인 의미를 가짐
    • 앞에서 본 gl_Positiongl_FragCoord 같은것들이 이에 해당.
  • 여기에서 모든 내장 변수를 볼 수 있다.

Vertex shader variables

  • Vertex shader의 clip-space 출력 위치 벡터인 gl_Position은 렌더링하기위해 필수적인 변수이다.

gl_PointSize

  • 기초 도형을 렌더링할 때, GL_POINTS를 선택할 수 있다.

    • 하나의 vertex가 점으로 렌더링되는것.
    • 이 점의 크기를 glPointSize 함수를 사용하여 설정할 수 있다.
    • 하지만 우리는 이 값을 vertex shader에서도 영향을 줄 수 있다.
  • gl_PointSize 의 GLSL 출력 내장 변수는 float 타입 변수이며, 너비와 높이를 픽셀 단위로 설정할 수 있다.

    • Vertex shader에서 점의 크기를 설정하면, vertex 마다 점의 값을 설정 가능.
  • 기본적으로 점 크기 수정은 비활성화
    • OpenGL에서 GL_PROGRAM_POINT_SIZE를 활성화 시켜야함
      1
      
      glEnable(GL_PROGRAM_POINT_SIZE);
      
  • 간단한 예제는 점의 크기를 viewer와 vertex 사이의 거리인 clip-space 위치의 z값과 동일하게 설정하는 것.

    • 이 점의 크기는 viewr와 멀리 떨어진 vertex일수록 더 커진다.
    1
    2
    3
    4
    5
    
      void main()
      {
          gl_Position = projection * view * model * vec4(aPos, 1.0);
          gl_PointSize = gl_Position.z;
      }
    
    • 결과는 다음과 같다. (큰 점이 더 멀리있음)
  • 이를 사용하여 particle을 만들어낼 수 있다.

gl_VertexID

  • gl_Positiongl_PointSize는 출력 변수

    • 결과에 영향을 줌
  • gl_VertexID는 입력 변수

    • 정수 변수로, 지금 그리고 있는 vertex의 ID를 가짐.
    • 인덱스된 렌더링(glDrawElements)을 수행할 때, 이 변수는 우리가 그리는 정점의 현재 인덱스를 보유한다.
    • 인덱스 없이 그릴때 (glDrawArrays) 이 변수는, 렌더 호출이 시작된 이후 현재 처리된 꼭짓점의 번호를 보유한다.(처리된 vertex 개수)

Fragment shader variables

gl_FragCoord

  • gl_FragCoord 벡터의 z요소가 특정 fragment의 깊이값과 동일

    • 이 벡터의 x, y 요소를 사용해보자
  • x,y 요소는 fragment의 window-space 좌표이다.

    • 이 좌표는 좌측 하단부터 시작한다.
    • glViewport 함수로 800x600을 설정하면, x값은 0~800, y값은 0~600 사이의 값을 가짐.
  • 이 fragment shader를 사용하여 fragment의 윈도우 좌표를 기반으로 다른 컬러 값을 계산할 수 있다.

  • gl_FragCoord변수가 자주 쓰이는 용도는 다른 fragment 연산과 시각적 효과를 비교하기 위해 쓰인다.

    • ex) 하나의 출력은 화면 왼쪽에 렌더링하고, 다른 출력은 화면에 오른족에 렌더링하여 분할된 화면을 만들 수 있다.
1
2
3
4
5
6
7
void main()
{
    if(gl_FragCoord.x < 400)
        FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    else
        FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}

  • 이제 다른 fragment shader 결과를 화면 양쪽에 보여줄 수 있게 되었다.
    • 이는 다른 조명 기술들을 테스팅할 때 유용하다.

gl_FrontFacing

  • Face culling을 사용하지 않는다면, gl_FrontFacing 변수로 전면인지 후면인지 알려준다.

    • 이를 사용해 전면에만 다른 컬러 설정 가능
  • gl_FrontFacing 변수는 이 fragment가 전면이면 true 후면이면 false 를 가지는 bool 타입 변수이다.

    • 예를 들어 안과 밖이 다른 텍스처를 입힌 큐브를 만들 수 있다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    #version 330 core
    out vec4 FragColor;
    
    in vec2 TexCoords;
    
    uniform sampler2D frontTexture;
    uniform sampler2D backTexture;
    
    void main()
    {
        if(gl_FrontFacing)
            FragColor = texture(frontTexture, TexCoords);
        else
            FragColor = texture(backTexture, TexCoords);
    }
    
    • 아래처럼 컨테이너 내부에는 다른 텍스처가 보인다.
  • 만약 face culling 을 활성화시키면 gl_FrontFacing은 쓸모없어짐

gl_FragDepth

  • gl_FragCoord는 read-only 변수이다.

  • 하지만, fragment의 depth 값은 설정 가능하다.

    • gl_FragDepth 변수로 shader내에서 depth를 설정할 수 있다.
1
gl_FragDepth = 0.0; // this fragment now has a depth value of 0.0
  • Shader에서 이 값을 작성하지 않으면, 자동으로 gl_FragCoord.z값으로 설정됨

  • 이 설정은 early depth testing 을 비활성화 시킨다.
    • OpenGL이 fragment shader가 실행되기 전에 이 fragment 가 어떤 depth 값을 가질지 알 수 없기 때문.
  • gl_FragDepth 는 이와같은 성능 패널티를 고려해야함.

    • 하지만 OpenGL4.2 부터는 fragment shader 시작지점에 depth condition과 함께, gl_FragDepth를 재정의함으로써 둘 사이를 조정할 수 있다.
    1
    
    layout (depth_<condition>) out float gl_FragDepth;
    
  • condition 값은 다음 값들을 취할 수 있다.
ConditionDescription
anyThe default value. Early depth testing is disabled.
greaterYou can only make the depth value larger compared to gl_FragCoord.z.
lessYou can only make the depth value smaller compared to gl_FragCoord.z.
unchangedIf you write to gl_FragDepth, you will write exactly gl_FragCoord.z.
  • greaterless를 지정하여, 오직 fragment의 depth 값보다 크거나 작은 값만을 작성할 수 있도록 할 수 있다.

    • 이 방법으로, early depth test를 fragment의 depth 값보다 작거나 큰 값에 대해서 수행할 수 있다.
  • depth 값을 증가시키지만, 일부 early depth testing을 수행하는 예제는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
#version 420 core // note the GLSL version!
out vec4 FragColor;
layout (depth_greater) out float gl_FragDepth;

void main()
{
    FragColor = vec4(1.0);
    gl_FragDepth = gl_FragCoord.z + 0.1;
}

Interface blocks

  • vertex 에서 frag로 데이터 보내고 싶을때, 여러 입력/출력 변수들을 선언했었다.

    • 보낼 데이터가 많을 수 있음
  • 이 변수들을 묶을 수 있도록, GLSL은 interface blocks라고 불리는 것을 제공한다.

    • 이 역시 in, out 키워드를 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out VS_OUT
{
    vec2 TexCoords;
} vs_out;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    vs_out.TexCoords = aTexCoords;
}
  • 이를 통해 입/출력을 체계화해줄 수 있다.

  • frag에서도 interface block을 선언해보자

    • 이름은 동일해야함.
    • 하지만 instance 이름은 원하는대로 지정 가능.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#version 330 core
out vec4 FragColor;

in VS_OUT
{
    vec2 TexCoords;
} fs_in;

uniform sampler2D texture;

void main()
{
    FragColor = texture(texture, fs_in.TexCoords);
}
  • interface block의 이름이 동일하기만 하면, 서로 연결됨.
    • 이는 코드를 체계화해주고, geometry shader와 같은 특정 shader 단계를 거칠 때 유용하다.

Uniform buffer objects

  • shader를 사용할 때, 계속해서 각 shader에서 정확히 동일한 값을 가지는 uniform들을 설정해야함.

  • UBO는 여러 shader program에 걸쳐 동일하게 유지되는 전역 uniform 변수의 모음을 선언할 수 있도록 한다.

  • UBO를 사용할 때 고정 GPU 메모리에서 관련 uniform을 한 번만 설정한다.

    • 셰이더별로 고유한 uniform을 수동으로 설정해야 한다.
  • UBO는 다른 버퍼들과 같은 버퍼

    • glGenBuffers()로 생성
    • GL_UNIFORM_BUFFER에 바인딩
    • 모든 연관 uniform 데이터들을 버퍼에 저장가능.
  • 먼저, 간단한 vertex shader에서 projection, view 행렬을 uniform block에 저장할 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#version 330 core
layout (location = 0) in vec3 aPos;

layout (std140) uniform Matrices
{
    mat4 projection;
    mat4 view;
};

uniform mat4 model;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

  • 반복문이 돌때마다 projection, view 행렬을 설정하므로, 이를 하나로 저장한것.

  • layout (std140)의 의미: 현재 정의된 uniform block이 특정한 메모리 layout을 사용한다는 뜻.

    • 이 코드는 uniform block layout을 설정한다.

Uniform block layout

  • uniform block 내용은 버퍼 객체에 저장되며, 이는 사실상 글로벌 GPU 메모리의 예약된 부분.
    • 이 메모리 조각에는 어떤 종류의 데이터가 있는지에 대한 정보가 없음
    • 메모리의 어떤 부분이 셰이더의 uniform 변수에 해당하는지 OpenGL에 알려줘야함.
  • shader에 아래와 같은 uniform block 이 선언되어 있다고 생각해보자
1
2
3
4
5
6
7
8
9
layout (std140) uniform ExampleBlock
{
    float value;
    vec3  vector;
    mat4  matrix;
    float values[3];
    bool  boolean;
    int   integer;
};
  • 여기서 알아야하는것은 크기(바이트)와 변수들의 offset(block의 시작위치로부터), 그래야 이들을 buffer에 각자의 순서로 위치시킬 수 있다.

    • 크기: OpenGL에 명시되어 있음, C++ 데이터 타입에 해당
    • 변수 사이의 간격(spacing)은 명확히 명시되어 있지 않음.
      • 이는 하드웨어가 변수들을 적합하다고 생각하는 곳에 위치시키기 때문.
      • ex)일부 하드웨어는 vec3을 float와 인접하게 위치시킨다. 모든 하드웨어가 이렇게 처리할 수 있는것이 아니며, float을 추가하기 전에 vec3을 float[4] 로 만들어 채운다.
  • 기본적으로 GLSL은 shared layout이라는 uniform 메모리 layout을 사용한다.(offset이 하드웨어에 의해 정의되면, 여러 프로그램 간에 일관되게 공유되기 때문)

    • 이를 사용하면, 변수의 순서가 그대로 유지되는 한, GLSL이 최적화를 위해 uniform 변수의 위치를 변경할 수 있다.
    • 우리는 각 uniform 변수들이 가질 offset을 모르기 때문에, uniform buffer를 정확히 채우는 방법을 모른다.
    • 이 정보를 glGetUniformindices 같은 함수를 사용하여 쿼리할 수 있지만, 이 접근 방법은 이 챕터에 맞지 않다.
  • shared layout이 일부 공간을 절약하는 최적화를 주지만, 각 변수들에 대한 offset들을 알아야한다.
  • 하지만, 일반적으로 shared layout 대신 std140 layout을 사용하기도 한다.

    • std140 layout은 각 변수 타입에 대해 룰에 따라 저마다의 offset을 명시하여 메모리 layout을 분명하게 명시한다.
    • 이 것이 명확하게 명시하기 때문에, 수작업으로 각 변수들의 offset을 알아낼 수 있다.
  • 각 변수는 uniform block 내에서 변수가 가질 수 있는 공간(여백을 포함)인 base alignment를 가진다.
    • 이 base alignment는 std140 layout 규칙을 사용하여 계산된다.
    • 그 다음, 각 변수에 대해 우리는 block의 시작으로 부터 해당 변수까지의 바이트 offset인 aligned offset을 계산한다.
    • 이 aligned 바이트 offset은 base alignment의 배수이다.
  • 정확한 layout 규칙은 여기에서 확인할 수 있다.
    • 가장 많이 쓰이는 규칙들은 아래 표에서 찾을 수 있다.
    • int, float, bool 과 같은 GLSL 변수 타입들은 4바이트 타입으로 정의된다. 4바이트의 각 요소들은 N으로 표시됨.
TypeLayout rule
Scalar e.g. int or boolEach scalar has a base alignment of N.
VectorEither 2N or 4N. This means that a vec3 has a base alignment of 4N.
Array of scalars or vectorsEach element has a base alignment equal to that of a vec4.
MatricesStored as a large array of column vectors, where each of those vectors has a base alignment of vec4.
StructEqual to the computed size of its elements according to the previous rules, but padded to a multiple of the size of a vec4.
  • std140 layout 예제는 다음과 같다. (각 멤버들의 aligned offset 계산)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
layout (std140) uniform ExampleBlock
{
                     // base alignment  // aligned offset
    float value;     // 4               // 0
    vec3 vector;     // 16              // 16  (offset must be multiple of 16 so 4->16)
    mat4 matrix;     // 16              // 32  (column 0)
                     // 16              // 48  (column 1)
                     // 16              // 64  (column 2)
                     // 16              // 80  (column 3)
    float values[3]; // 16              // 96  (values[0])
                     // 16              // 112 (values[1])
                     // 16              // 128 (values[2])
    bool boolean;    // 4               // 144
    int integer;     // 4               // 148
};
  • 이렇게 계산된 offset 값들과 함께, glBufferSubData 함수를 사용하여 변수 데이터를 각 offset에 채울 수 있다.

    • std140 layout은 memory layout이 각 program에 대해서 정의되어진 uniform block 형태를 유지한다는 장점이 있다.
  • uniform block을 정의하기전 layout (std140)을 추가함으로, OpenGL에게 이 uniform block이 이를 사용한다라는것을 알려준다.
  • 버퍼를 채우기전에 offset을 쿼리하는 다른 방법이 두가지 존재
    • shared layout 는 위에서 보았음.
    • packed layout: 컴파일러가 셰이더마다 다를 수 있는 uniform block에서 uniform 변수를 최적화할 수 있기 때문에 layout이 프로그램 간에 동일하게 유지된다는 보장은 없음(공유되지 않음).

Using uniform buffers

  • 먼저 glGenBuffers를 사용하여 UBO를 생성한다.
    • buffer object를 가지게되면, 이것을 GL_UNIFORM_BUFFER 타겟에 바인딩하고, glBufferData 함수를 호출하여 충분한 메모리를 할당해준다.
1
2
3
4
5
unsigned int uboExampleBlock;
glGenBuffers(1, &uboExampleBlock);
glBindBuffer(GL_UNIFORM_BUFFER, uboExampleBlock);
glBufferData(GL_UNIFORM_BUFFER, 152, NULL, GL_STATIC_DRAW); // allocate 152 bytes of memory
glBindBuffer(GL_UNIFORM_BUFFER, 0);
  • 이제 buffer에 데이터를 집어넣거나 수정하고 싶을 경우, uboExampleBlock을 바인딩하고 glBufferSubData함수를 사용하여 메모리를 수정한다.

    • 우리는 오직 이 buffer를 한번 수정하면 이 버퍼를 사용하는 모든 셰이더들은 이 수정된 데이터를 사용하게 된다.
  • OpenGL이 어떠한 uniform block이 어떠한 uniform buffer에 해당하는지 어떻게 아는지?

    • OpenGL에는 uniform buffer를 연결시킬 수 있는곳에 정의된 binding points 가 존재
    • uniform buffer가 일단 생성되면, 이 binding points들 중 하나에 연결을 하고 또한 이를 shader의 uniform block을 동일한 binding point에 연결한다.
    • 위 이미지에서 볼 수 있듯이, 여러 버퍼들을 여러 binding point에 바인딩 가능하다.
    • uniform block이 동일한 uniform data를 공유할 수 있다.(위 이미지에서 binding point0 부분) 이 때 요구사항은 두 셰이더가 모두 같은 uniform block을 정의해야한다는 것.

glUniformBlockBinding: shader <-> binding point

  • 특정 binding point에 uniform block을 연결하기 위해 우리는 glUniform BlockBinding 함수를 호출한다.
    • 파라미터1: program object
    • 파라미터2: uniform block index
    • 파라미터3: binding point
  • uniform block index: shader에 정의된 uniform block의 location index
    • glGetUniformBlockIndex 함수를 통해 얻을 수 있다.
    • 이 함수는 program object를 받아들이고, uniform block의 이름을 받는다.
  • 아래는 binding point를 2로 설정한것.
1
2
unsigned int lights_index = glGetUniformBlockIndex(shaderA.ID, "Lights");
glUniformBlockBinding(shaderA.ID, lights_index, 2);
  • 이를 각 shader에 반복해야한다.

OpenGL 4.2 부터 binding point를 shader에 명확히 저장하는게 가능해짐. 이는 또 다른 layout 지정을 사용하여 glGetUniformBlockIndexglUniformBlockBinding 함수를 사용하지 않아도 되게 해준다.

아래 코드는 binding point를 명확하게 설정한다.

layout(std140, binding = 2) uniform Lights { ... };

glBindBufferBase: binding point <-> UBO

  • 그런 다음, UBO를 동일한 binding point에 바인딩해야하고 이는 glBindBufferBaseglBindBufferRange 함수를 통해 수행될 수 있다.
1
2
3
glBindBufferBase(GL_UNIFORM_BUFFER, 2, uboExampleBlock);
// or
glBindBufferRange(GL_UNIFORM_BUFFER, 2, uboExampleBlock, 0, 152);
  • glBindBufferRange
    • 이 함수는 추가적으로 offset 과 size를 받는다.
    • 이는 지정한 uniform buffer의 영역만 binding point에 바인딩 가능
    • 여러 다른 uniform block들을 하나의 ubo에 연결 가능하다.

데이터 추가

  • 이제 모든 것이 세팅되었으므로, uniform buffer에 데이터를 추가할 수 있다.
    • 모든 데이터를 하나의 바이트 배열로 추가하거나 glBufferSubData 함수를 사용하여 buffer의 특정 부분을 수정할 수 있다.
    • uniform 변수 boolean을 수정하기 위해 다음과 같이 ubo를 수정할 수 있다.
1
2
3
4
glBindBuffer(GL_UNIFORM_BUFFER, uboExampleBlock);
int b = true; // bools in GLSL are represented as 4 bytes, so we store it in an integer
glBufferSubData(GL_UNIFORM_BUFFER, 144, 4, &b);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
  • 이와 같은 과정을 uniform block 내부의 모든 다른 uniform 변수들에 적용 가능하지만, range 파라미터는 다를 것이다.

A simple example

  • 계속해서 사용해왔던 3가지 행렬 projection, view, model

    • 이 행렬 중 오직 model 행렬만 자주 변경됨
    • 동일한 행렬의 모음을 사용하는 여러 shader를 가지고 있다면, ubo를 사용하는 것이 좋다.
  • projection, view 를 matrics라는 uniform block에 저장해보자

    • model은 자주 변경되므로, 이점이 없음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#version 330 core
layout (location = 0) in vec3 aPos;

layout (std140) uniform Matrices
{
    mat4 projection;
    mat4 view;
};
uniform mat4 model;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}
  • 4개의 큐브를 그리고, 각자 다른 shader program을 사용할것임.

    • vertex shader는 같지만, shader마다 다른 오직 하나의 색만 출력하는 다른 fragment shader를 사용한다.
  • 먼저, vertex shader의 uniform block을 binding point 0으로 설정(각 shader에 대해)

1
2
3
4
5
6
7
8
9
unsigned int uniformBlockIndexRed    = glGetUniformBlockIndex(shaderRed.ID, "Matrices");
unsigned int uniformBlockIndexGreen  = glGetUniformBlockIndex(shaderGreen.ID, "Matrices");
unsigned int uniformBlockIndexBlue   = glGetUniformBlockIndex(shaderBlue.ID, "Matrices");
unsigned int uniformBlockIndexYellow = glGetUniformBlockIndex(shaderYellow.ID, "Matrices");

glUniformBlockBinding(shaderRed.ID,    uniformBlockIndexRed, 0);
glUniformBlockBinding(shaderGreen.ID,  uniformBlockIndexGreen, 0);
glUniformBlockBinding(shaderBlue.ID,   uniformBlockIndexBlue, 0);
glUniformBlockBinding(shaderYellow.ID, uniformBlockIndexYellow, 0);
  • 그다음 ubo를 생성하고 binding point 0에 바인딩한다.
1
2
3
4
5
6
7
8
unsigned int uboMatrices
glGenBuffers(1, &uboMatrices);

glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);

glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboMatrices, 0, 2 * sizeof(glm::mat4));
  • 적절히 메모리를 설정, 그리고 binding point를 0으로 설정

  • 그리고 buffer를 채우자

    • field of view 값을 상수로 유지(카메라 줌이 없으면)하고 싶다면, 한번만 정의하면 됨.(한번만 버퍼에 삽입)
    • buffer object에 충분한 메모리를 할당했기 때문에 glBufferSubData함수를 사용하여 게임 루프에 들어가기 전에 projection 행렬을 저장할 수 있다
1
2
3
4
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(projection));
glBindBuffer(GL_UNIFORM_BUFFER, 0);
  • 각 렌더링 루프에서 오브젝트를 그리기 전에, buffer의 두 번째 공간에 view 행렬을 넣는다.
1
2
3
4
glm::mat4 view = camera.GetViewMatrix();
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(view));
glBindBuffer(GL_UNIFORM_BUFFER, 0);
  • 그리고 matrics uniform block을 가지고 있는 각 vertex shader는 이제 uboMatrix에 저장된 데이터를 가지고 있다.
    • 그러므로 4개의 큐브를 그리면, 동일하게 projection, view 행렬이 유지된다.
1
2
3
4
5
6
7
8
9
glBindVertexArray(cubeVAO);
shaderRed.use();
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-0.75f, 0.75f, 0.0f));	// move top-left
shaderRed.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
// ... draw Green Cube
// ... draw Blue Cube
// ... draw Yellow Cube
  • 설정하는 uniform은 model 행렬뿐이므로, 이전보다 약간의 uniform 호출을 줄였다.

전체 소스

  • UBO 의 장점
    1. 한번에 uniform들을 설정하는 것은 하나 하나 설정하는 것보다 빨,ㅁ
    2. 여러 shader에 걸쳐있는 동일한 uniform을 수정하고 싶은 경우, 한번에 수정하기 쉬움.
    3. 아주 많은 uniform들을 사용할 수 있음. (OpenGL은 관리할 수 있는 uniform의 개수가 제한되어 있음. GL_MAX_VERTEX_UNIFORM_COMPONENTS 를 사용하여 확인 가능)
      • uniform 개수의 한계치에 닿았을 때(ex. 스켈레톤 애니메이션)마다 ubo를 사용하면 좋다.

출처

Advanced GLSL: 원문 Advanced GLSL: 번역본

This post is licensed under CC BY 4.0 by the author.

[learn-opengl] Advanced OpenGL: Advanced Data

[그래프] 벨만-포드 최단 경로 알고리즘