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

[learn-opengl] Advanced OpenGL: Blending

Blending

  • OpenGL에서 블렌딩은 오브젝트 내의 투명도(transparency)를 구현하는 기술로 알려져 있음.
    • 투명도: 단색(solid color)를 가지지 않는 오브젝트에 관한것이지만, 오브젝트 자체와 그 뒤에 있는 오브젝트의 색상을 다양한 강도로 조합한것.
  • 유리에는 자체 색상이 있지만, 결과적으로는 유리 뒤에 있는 모든 오브젝트의 색상이포함된다.

  • 여러 색상(다른 오브젝트들)을 단일 색상으로 혼합하기 때문에 Blending 으로 부름.

  • 투명도는 통과해서 볼 수 있게 한다.

  • 투명한 오브젝트(Transparent objects)는 완전히 투명(모든 색상 통과)하거나 부분적으로 투명(일부 자신의 색상또한 보여줌)하다.

  • 투명도는 색상의 alpha 값으로 정의된다.

    • 색상 벡터의 4번째 요소
    • ex) a = 0.5는 50%의 투명도
  • 일부 텍스처는 텍셀당 알파 값이 포함된 알파 채널이 포함되어 있음. - 이 알파 값은 텍스처의 어느 부분이 투명성을 갖고 있는지, 얼마만큼의 텍스처가 있는지 정확히 알려줌. - 아래 에서 유리는 0.25, 바깥쪽 코너는 0.0이다. - 유리부분은 75%의 투명도이므로, 이를 통해 본 배경은 더 빨간색으로 보임

  • 완전히 투명(fully transparent) 또는 완전히 불투명(fully opaque)에 대해 다룬다.

Discarding fragments

  • 일부 effects는 부분 투명도를 신경쓰지 않지만, 텍스처의 색상 값을 기준으로 무언가를 표시하거나 전혀 표시하지 않으려는 경우도 았다.

  • 잔디 같은것을 만들려고하면, 2d quad 에 잔디 텍스처를 붙이고, 그 quad를 장면에 배치한다.

    • 잔디는 실제로 이와같은 모양이 아니므로, 잔디 텍스처의 일부분만 표시하고, 나머지는 무시해야한다.
  • 아래의 잔디 텍스처는 완전 불투명과 완전 투명이 섞여 있다.

    • 투명한 배경/ 불투명한 잔디

  • 이 텍스처를 불투명한 부분만 보여줘야한다.

  • 따라서 투명한 부분을 나타내는 fragment를 폐기해야한다.

알파 채널 텍스처 로드

  • stb_image는 알파채널이 있는 경우 알아서 설정한다.
  • OpenGL은 다음과 같이 텍스처 생성할 때 알려줘야한다.
1
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
  • 이제 셰이더도 다음과 같이 vec4로 수정해야함
1
2
3
4
5
void main()
{
    // FragColor = vec4(vec3(texture(texture1, TexCoords)), 1.0);
    FragColor = texture(texture1, TexCoords);
}

잔디 추가

  • 예제는 depth testing의 예제에서 코드를 추가할것이다.

  • 잔디들의 위치 벡터들을 선언하자

1
2
3
4
5
6
vector<glm::vec3> vegetation;
vegetation.push_back(glm::vec3(-1.5f,  0.0f, -0.48f));
vegetation.push_back(glm::vec3( 1.5f,  0.0f,  0.51f));
vegetation.push_back(glm::vec3( 0.0f,  0.0f,  0.7f));
vegetation.push_back(glm::vec3(-0.3f,  0.0f, -2.3f));
vegetation.push_back(glm::vec3( 0.5f,  0.0f, -0.6f));
  • 잔디는 단일 쿼드로, 복잡한 모델인 3D로 표현하는 것보다 효율적이다.

    • 여러 개의 회전된 잔디 쿼드를 추가하면 비슷하게 보인다.
  • 잔디 텍스처가 쿼드 객체에 추가되므로, 다른 VAO를 다시 작성해야함.

    • 적절한 정점 속성 포인터를 설정
1
2
3
4
5
6
7
8
9
glBindVertexArray(vegetationVAO);
glBindTexture(GL_TEXTURE_2D, grassTexture);
for(unsigned int i = 0; i < vegetation.size(); i++)
{
    model = glm::mat4(1.0f);
    model = glm::translate(model, vegetation[i]);
    shader.setMat4("model", model);
    glDrawArrays(GL_TRIANGLES, 0, 6);
}
  • 이제 실행하면 다음과 같다.
    • 문제: OpenGL이 기본적으로 알파 값을 처리할 것인지, 버릴 것인지 모르기 때문에 발생

  • 셰이더를 사용하여, fragment를 폐기하자
    • discard: fragment 를 폐기함.
    • 특정 임계값보다 작으면 폐기하도록할 수 있음.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D texture1;

void main()
{
    vec4 texColor = texture(texture1, TexCoords);
    if(texColor.a < 0.1)
        discard;
    FragColor = texColor;
}
  • 위 셰이더는 거의 투명하지 않은 fragment 만 렌더링하게 해준다.(아래 이미지 참고)

GL_REAPEAT로 텍스처 래핑 방식을 설정하면, 다음 repeat 값으로 border를 보간한다. 만약 투명한 값이 있을 경우, 투명한 값과 단색 값으로 보간된 투명값을 가져올 수 있다. 그려면 약간 반투명한 색 테두리가 텍스처 쿼드에 래핑될 수 있다. 이를 방지하기 위해 래핑 방식을 GL_CLAMP_TO_EDGE로 설정해야한다.

1
2
3
4
```cpp
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
```

Blending

  • 위처럼 폐기하는것은 반 투명 이미지를 렌더링할 때 유연성을 주지 못한다.

  • 서로 다른 투명도로 이미지를 렌더링하려면 블렌딩을 활성화해야한다.

  • GL_BLEND를 활성화해 블렌딩을 활성화 할 수 있다.

1
glEnable(GL_BLEND);
  • 블렌딩을 활성화 했으므로, 혼합되어야 하는 방법을 알려줄 필요가 있다.

  • OpenGL은 다음 방정식으로 블렌딩을 수행한다.

    • $\bar{\color{green}C}_{source}$: 소스 색상 벡터, 텍스처에서 온 색 벡터
    • $\bar{\color{red}C}_{destination}$: 목적지 색상 벡터, 현재 컬러 버퍼에 저장된 색상 벡터
    • $\color{green}F_{source}$: 소스 factor 값, 소스 색상에 대한 알파 값의 영향을 설정
    • $\color{red}F_{destination}$: 대상 factor 값, 알파 값이 대상 색상에 미치는 영향을 설정한다.
\[\begin{equation}\bar{C}_{result} = \bar{\color{green}C}_{source} * \color{green}F_{source} + \bar{\color{red}C}_{destination} * \color{red}F_{destination}\end{equation}\]
  • fragment shader가 실행되고 모든 테스트가 통과된 후, 이 혼합 방정식이 프래그먼트의 색상 출력과 현재 색상 버퍼에 있는 것들에 영향을 미친다.

예시

  • 붉은 square에 반투명한 녹색 사격형을 그려보자.
    • 빨간색 사각형은 대상 색상이 됨(칼라 버퍼의 첫번째)

  • 녹색의 알파가 60%라고 하면 기여도는 알파값과 동일한 기여도를 부여하는것이 좋으므로 식은 다음과 같다.
\[\begin{equation}\bar{C}_{result} = \begin{pmatrix} \color{red}{0.0} \\ \color{green}{1.0} \\ \color{blue}{0.0} \\ \color{purple}{0.6} \end{pmatrix} * \color{green}{0.6} + \begin{pmatrix} \color{red}{1.0} \\ \color{green}{0.0} \\ \color{blue}{0.0} \\ \color{purple}{1.0} \end{pmatrix} * (\color{red}{1 - 0.6}) \end{equation}\]
  • 결과는 다음과 같다.

glBlendFunc

  • glBlendFunc(GLenum sfactor, GLenum dfactor)

    • 두 매개변수로 소스 및 대상 factor에 대한 옵션을 설정할 수 있다.

    • 소스 factor와 대상 factor는 다음과 같은 값을 가질 수 있다.

      • GL_ZERO: 0
      • GL_ONE: 1
      • GL_SRC_COLOR: 소스 색상
      • GL_ONE_MINUS_SRC_COLOR: 1- 소스 색상
      • GL_DST_COLOR: 대상 색상
      • GL_ONE_MINUS_DST_COLOR: 1- 대상 색상
      • GL_SRC_ALPHA: 소스 알파
      • GL_ONE_MINUS_SRC_ALPHA: 1 - 소스 알파
      • GL_DST_ALPHA: 대상 알파
      • GL_ONE_MINUS_DST_ALPHA: 1 - 대상 알파
      • GL_CONSTANT_COLOR: 상수 색상
      • GL_ONE_MINUS_CONSTANT_COLOR: 1- 상수 색상
      • GL_CONSTANT_ALPHA: 상수 알파
      • GL_ONE_MINUS_CONSTANT_ALPHA: 1 - 상수 알파
      • GL_SRC_ALPHA_SATURATE: 소스 알파 채도(i,i,i,1)
  • 위 사각형 예제를 다음과 같이 glBlendFunc 함수로 나타낼 수 있다.

1
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glBlendFuncSeperate

  • glBlendFuncSeperate 를 사용해 RGB 및 알파 채널에 대해 다른 옵션을 개별적으로 설정할 수 있다.
    • 아래는 RGB는 이전과 같지만, 알파는 원본 값의 영향을 받도록 하는 코드이다.
1
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);

glBlendEquation

  • 방정식 연산자에 더 많은 유연성을 줌.

  • GL*FUNC_ADD: the default, adds both colors to each other: \(\bar{C}*{result} = \color{green}{Src} + \color{red}{Dst}\)

  • GL*FUNC_SUBTRACT: subtracts both colors from each other: \(\bar{C}*{result} = \color{green}{Src} - \color{red}{Dst}\)

  • GL*FUNC_REVERSE_SUBTRACT: subtracts both colors, but reverses order: \(\bar{C}*{result} = \color{red}{Dst} - \color{green}{Src}\)
  • GL*MIN: takes the component-wise minimum of both colors: \(\bar{C}*{result} = min(\color{red}{Dst}, \color{green}{Src})\)
  • GL*MAX: takes the component-wise maximum of both colors: \(\bar{C}*{result} = max(\color{red}{Dst}, \color{green}{Src})\)

  • 보통 기본값인 ADD 사용한다.(가장 선호됨.)

Rendering semi-transparent textures

  • 반투명 윈도우를 여러개 추가할것이다.

  • 잔디 대신 투명 창 텍스처를 사용할 것임.

  • 먼저 초기화하는 동안 블렌딩을 활성화하고, 적절한 블렌딩 함수를 설정한다.
1
2
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  • 블렌딩을 활성화했으므로, frag를 폐기할 필요가 없어서 fragment shader를 다음과 같이 되돌린다.
1
2
3
4
5
6
7
8
9
10
11
#version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D texture1;

void main()
{
    FragColor = texture(texture1, TexCoords);
}
  • 현재 fragment 색상과 알파 값에 따라 현재 칼라 버퍼에 결합된다.

  • 이제 다음과 같은 결과가 나온다.

  • 여기서 문제가 하나 생긴다.
    • 앞에있는 창에서 뒷부분을 볼 수 없는것.
    • 이는 깊이테스트 때문에 일어난다.
  • 깊이 테스트는 frag가 투명도를 신경쓰지 않아, 투명부분이 다른 값으로 깊이 버퍼에 덮어씌워진다.

    • 그 결과, 투명도에 관계없이 창의 투명도를 무시하고, 다른 불투명 오브젝트와 마찬가지로 깊이테스트가 수행된다.
  • 해결방법
    • 가장 먼 곳에서 가장 가까운 곳으로 수종으로 정렬하고 따라 그려야한다.

잔디 잎같은것은 단순히 폐기하여 이런 문제를 신경쓰지 않게 할 수 있다.

Don’t break the order

  • 여러 투명도를 가진 오브젝트를 그리려면, 가장 먼 객체를 먼저 그리고, 가까운 객체를 마지막에 그려야한다.
  • 그리고, 투명 오브젝트를 드로잉하기전에 일반 오브젝트를 먼저 그려야한다.

  • 일반적인 개요는 다음과 같다.

    1. 모든 불투명 오브젝트를 먼저 그림
    2. 투명 오브젝트를 모두 정렬
    3. 정렬된 이 오브젝트들을 순서대로 그림
  • 정렬 방법

    • 뷰어의 관점에서 거리를 검색
    • 카메라의 위치 벡터와 객체의 위치 벡터 사이의 거리를 취함
    • 이 거리를 해당 위치 벡터와 함께 map에 저장.(key값을 기반으로 자동 정렬)
    1
    2
    3
    4
    5
    6
    
    std::map<float, glm::vec3> sorted;
    for (unsigned int i = 0; i < windows.size(); i++)
    {
        float distance = glm::length(camera.Position - windows[i]);
        sorted[distance] = windows[i];
    }
    
    • 렌더링은 역순으로 가져온다.
    1
    2
    3
    4
    5
    6
    7
    
      for(std::map<float,glm::vec3>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it)
      {
          model = glm::mat4(1.0f);
          model = glm::translate(model, it->second);
          shader.setMat4("model", model);
          glDrawArrays(GL_TRIANGLES, 0, 6);
      }
    
  • 이제 다음과 같이 제대로 렌더링된다.

  • 이러한 정렬은 회전, 크기 조정 등의 다른 변환을 고려하지 않는다.

    • 일반적인 모양의 객체는 단순히 위치 벡터와 다른 측정 기준이 필요하다.
  • 장면에서 오브젝트를 정렬하는 것은 장면에 따라 크게 달라진다.

    • 비용이 많이 소요.
    • 완벽하고 투명한 오브젝트로 장면을 완전히 렌더링하는것은 어려움.
    • 순서 독립적 투명성(order independent transparency)과 같은 고급 기술이 있음.

출처

Blending

blend test 사이트

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

[백준][C++] 2206: 벽부수고 이동하기 (Floyd Warshall)

[learn-opengl] Advanced OpenGL: Face culling