[BEFORE BLENDING]
- OpenGL에서 Blending이란 흔히 투명한 object를 구현하는 기술로 알려져있다
- 투명 : 하나의 컬러를 가지고 있는 것이 아니라 object 자체가 가지고 있는 컬러와 뒤에 있는 다른 object의 컬러를 여러 비율로 혼합하는 것
- 투명한 object들은 완전히 투명하여 모든 컬러들이 통과하거나 불완전하게 투명하여 컬러들이 본연의 컬러 일부를 가지고 통과할 수도 있다
- object의 투명도는 alpha 값으로 정의된다 (color vector의 4번째 요소)
: alpha값이 0.0이면 완전 투명, 1.0이면 불투명, 0.5면 object가 본연의 컬러 50% 뒤에 있는 object의 컬러 50%으로 혼합되어 출력된다
- 지금까지 사용한 texture들은 모두 rgb로만 구성되어 있었지만, 이번장에서는 모서리는 0.0, 유리부분은 0.25의 alpha 값을 가지고 있는 창문 이미지를 scene에 추가하여 사용한다
[DISCARDING FRAGMENTS]
- 일부 이미지는 완전히 투명하거나 완전히 불투명한 부분만으로 이루어져 불투명한 부분만 출력하도록 되어있다
: texture의 투명한 부분의 fragment를 color buffer에 저장하지 않고 폐기해야한다
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
- alpha 값과 함께 텍스처를 불러오기 위해 glTexImage2D의 옵션을 GL_RGB에서 GL_RGBA로 수정한다
void main()
{
// FragColor = vec4(vec3(texture(texture1, TexCoords)), 1.0);
FragColor = texture(texture1, TexCoords);
}
- 그 후 fragment shader에서 텍스처의 컬러 요소들을 RGB가 아닌 RGBA, 총 4가지의 컬러 요소를 얻어야한다
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));
- 투명 텍스처를 불러왔으므로 현재 scene에 위 이미지의 잔디를 깔아보려고 한다
- 잔디의 위치를 나타내는 glm::vec3 변수들에 대한 작은 vector를 생성한다
- 각 잔디 object들은 잔디 texture를 입힌 사각형으로 렌더링된다
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);
}
- 잔디 texture가 컨테이너에 추가되었기 때문에 다른 VAO를 생성하고 VBO를 채우고 적절한 vertex attribute pointer들을 설정해야한다
#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가 처리되지 않고 color buffer에 저장하지 않게 해주는 discard 명령어를 사용하여 fragment shader가 alpha 값을 가지고 있다면 fragment를 폐기할 수 있도록 한다
: 위 코드에서는 샘플링 된 텍스처 컬러가 1.0보다 작은 alpha 값을 가지고 있는지 확인하고, 그렇다면 fragment를 폐기한다
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- texture 사각형 주위를 감싸는 모서리가 완전히 투명하지 않게 되는 것을 방지하기 위해 alpha texutre를 사용할 때마다 texture warpping 방법을 GL_CLAMP_TO_EDGE로 설정해주어야 한다
- 코드 실행 결과
[BLENDING]
glEnable(GL_BLEND);
- 다양한 투명도를 가진 이미지를 렌더링하기 위해 blending을 활성화해야한다
- OpenGL에서 blending은 위 방정식을 사용하여 실행된다
C_source : 원본 color vector로, texture의 원래 색상이다
F_source : 원본 지수 값으로, 원본 color alpha값의 영향력을 설정한다
C_destination : 목적 color vector로, color buffer에 현재 저장된 color vector다
F_destination : 목적 지수 값으로, 목적 color alpha 값의 영향력을 설정한다
- fragment shader가 실행되고 모든 테스트들이 처리되고 난 후에 blend 방정식이 현재 color buffer에 저장된 값(현재 fragment 전에 저장된 컬러)과 함께 fragment의 컬러 출력을 옅게 만든다
: 원본/목적 컬러는 OpenGL에 의해 자동적으로 설정되지만, 원본/목적 지수는 직접 설정할 수 있다
- 위 이미지에서 빨간 사각형은 목적 컬러가 되고, 그 위에 녹색 사각형을 그리고자 한다
- 녹색 사각형에 alpha 값을 곱하기를 원하므로 F_src 값을 원본 color vector alpha 값인 0.6으로 설정한다
- 이후 F_des 값은 alpha 값의 나머지로 설정한다
- 녹색 사각형이 60%의 영향력을 가지고 있다면
빨간 사각형은 40%의 영향력을 가지고자 한다
: F_des 값은 1 - 원본 color vector alpha으로 계산하여 설정한다
- 결과적으로 60%의 녹색과 40%의 빨간색을 포함하고 있는
사각형 fragment가 출력된다 (우측 이미지)
- 최종 컬러는 color buffer에 저장되어 이전의 컬러를 대체한다
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(sfactor, dfactor)
- 원본 지수와 목적 지수에 대한 옵션을 설정한다
- 해당 함수에서 사용할 수 있는 option들이다
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
- glBlendFuncSeprate 함수를 사용하여 RGB와 alpha 채널을 다른 옵션으로 따로 설정하는 것도 가능하다
- 이 함수는 결과 alpha 요소를 원본 alpha 값에 영향을 받게 하는 것 뿐만 아니라 RGB 요소들에 대해서도 설정된다
- glBlendEquation 함수를 사용하면 원본과 목적 부분 사이의 연산자를 바꿀 수도 있지만, 일반적으로 GL_FUNC_ADD가 대부분의 연산에 필요한 방정식이기 때문에 glBlendEquation는 잘 사용하지 않는다
- glBlendEquation에서 사용할 수 있는 option들이다
[RENDERING SEMI-TRANSPARENT TEXTURES]
- 이번 파트에서는 잔디 대신에 반투명한 창문 이미지를 사용한다
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- blending을 활성화하고 적절한 blending 함수를 설정하여 초기화한다
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D texture1;
void main()
{
FragColor = texture(texture1, TexCoords);
}
- blending을 활성화했기 때문에 fragment를 폐기할 필요가 없으므로 fragment shader를 원래의 버전으로 수정한다
- OpenGL이 fragment를 렌더링할 때마다 alpha값을 기반으로 하여 현재 fragment의 컬러와 color buffer에 저장되어 있는 fragment 컬러를 혼합한다
: 창문의 유리 부분은 반투명이기 때문에 창문의 뒷 배경이 보여야한다
- 그러나 여기까지 실행을 하면 앞에 있는 창문의 투명한 부분이 뒤에 있는 창문을 가리고 있는 것처럼 보인다
: 이 이유는 depth testing이 blending과 함께 사용되기 곤란하기 때문이다
- depth buffer를 작성할 때 이 fragment가 투명한지 투명하지 않은지를 생각하지 않고 작성된다
: 결과적으로 창문의 전체 사각형은 투명도에 이무 관계없이 depth testing이 실행된다
- 이를 해결하기 위해 창문의 거리에 따라 정렬하여 뒤에 있는 창문들을 먼저 그려야한다
[DON'T BREAK THE ORDER]
- 일반적인 blend 되지 않은 object들은 depth buffer를 사용하여 평소대로 그려지게 되므로 정렬할 필요가 없다
: 정렬된 object들보다 먼저 그려주어야한다
- 투명하지 않은 object와 투명한 object가 공존하는 scene을 그릴 때의 일반적인 과정은 아래와 같다
01] 불투명한 object들을 그린다
02] 투명한 object들을 정렬한다
03] 투명한 object를 정렬한 순서대로 그린다
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];
}
- 투명한 object를 정렬하는 방법 중 하나는 시점으로부터 object까지의 거리를 얻는 것이다
: 카메라의 위치 vector와 object의 위치 vector사이의 거리를 취하여 얻을 수 있다
- 계산한 거리 값을 위치 vector와 함꼐 STL 라이브러리의 map 자료 구조에 저장한다
: map은 key 값을 기반으로 자동으로 정렬해준다
- map에서 거리 값을 key 값으로 삽입하고 그에 해당하는 모든 위치 값을 삽입한다면 자동적으로 거리에 따라 정렬이 된다
- 결과는 distance key 값을 기반으로 짧은 거리부터 먼 거리까지 정렬된 순서의 창문 위치가 저장된 객체이다
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);
}
- 렌더링할 때 map 값을 먼 창문에서 가까운 창문으로(반대순서) 취하고 해당 창문들을 그린다
: map으로부터 reverse iterator를 얻어 요소들을 반대 순서로 얻고 각 창문 사각형들을 해당 창문 위치로 이동시킨다
- 실행 결과
'OpenGL Study' 카테고리의 다른 글
OpenGL "FrameBuffers" (0) | 2023.02.07 |
---|---|
OpenGL "Face Culling" (0) | 2023.02.01 |
OpenGL "Stencil testing" (0) | 2023.02.01 |
OpenGL "Depth Testing" (0) | 2023.01.31 |
OpenGL "Model" (0) | 2023.01.25 |