📝 5주차
- Screen space Gaussian blur
- diffuse에만 blur 적용
1. Screen Space Gaussian blur
- 위의 이유로 subsurface scattering을 위한 Gaussian blur 과정을 texture space가 아닌 screen space에서 진행
- screen space에서 진행한다는 것은 FBO의 color texture를 그릴 때 영상처리 과정처럼 후처리(post-processing)을 진행하는 것
- 새로운 FBO가 또 필요하므로, FBO를 관리하는 struct를 생성함
진행 과정
- baseFBO에 3d 모델을 띄우고, 텍스처링 (이전까지 한 과정)
- baseFBO의 colorTexture를 사용하여 gaussianFBO에서 gaussian blur 적용
- gaussianGBO의 color texture를 사용하여 기본 Framebuffer에 그리기
✔️ main.cpp
. . .
struct FBO {
GLuint frameBuffer = 0;
GLuint colorTexBuffer = 0;
GLuint depthBuffer = 0;
};
FBO baseFBO;
FBO gaussianFBO;
Program program;
Program screenProgram;
Program gaussianProgram;
. . .
void init() {
. . .
program.loadShaders("shader.vert", "shader.frag");
screenProgram.loadShaders("screenShader.vert", "screenShader.frag");
gaussianProgram.loadShaders("gaussianBlur.vert", "gaussianBlur.frag");
glGenFramebuffers(1, &gaussianFBO.frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, gaussianFBO.frameBuffer);
glGenTextures(1, &gaussianFBO.colorTexBuffer);
glBindTexture(GL_TEXTURE_2D, gaussianFBO.colorTexBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, windowSize.x, windowSize.y, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gaussianFBO.colorTexBuffer, 0);
glGenTextures(1, &gaussianFBO.depthBuffer);
glBindTexture(GL_TEXTURE_2D, gaussianFBO.depthBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, windowSize.x, windowSize.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, gaussianFBO.depthBuffer, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
}
void render(GLFWwindow* window)
{
. . .
glBindFramebuffer(GL_FRAMEBUFFER, gaussianFBO.frameBuffer);
glViewport(0, 0, nowSize.x, nowSize.y);
glClearColor(0.1, 0.1, 0.1, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(gaussianProgram.programID);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, baseFBO.colorTexBuffer);
GLuint colorTexLocation = glGetUniformLocation(gaussianProgram.programID, "colorTex");
glUniform1i(colorTexLocation, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, baseFBO.depthBuffer);
GLuint depthTexLocation = glGetUniformLocation(screenProgram.programID, "depthTex");
glUniform1i(depthTexLocation, 1);
GLuint sizeLocation = glGetUniformLocation(gaussianProgram.programID, "size");
glUniform2f(sizeLocation, static_cast<float>(nowSize.x), static_cast<float>(nowSize.y));
glBindVertexArray(quadArrrayBuffer);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, nowSize.x, nowSize.y);
glClearColor(0.1, 0.1, 0.1, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(screenProgram.programID);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gaussianFBO.colorTexBuffer);
GLuint blurTexLocation = glGetUniformLocation(screenProgram.programID, "colorTex");
glUniform1i(blurTexLocation, 0);
sizeLocation = glGetUniformLocation(screenProgram.programID, "size");
glUniform2f(sizeLocation, static_cast<float>(nowSize.x), static_cast<float>(nowSize.y));
glBindVertexArray(quadArrrayBuffer);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
✔️ gaussianBlur.vertex
- quad VAO의 position 정보를 fragment shader에게 넘겨줌
#version 410 core
layout(location=0) in vec3 in_Position;
void main(void)
{
gl_Position = vec4(in_Position, 1.0);
}
✔️ gaussianBlur.frag
- texture(이미지)를 사용해 gaussian blur 적용
- quad 위에 blur된 값 적용
#version 150 core
uniform sampler2D colorTex;
uniform sampler2D depthTex;
uniform vec2 size;
out vec4 out_Color;
const int kernelSize = 11;
const float sigma = 3.0;
void main(void)
{
vec2 texelSize = 1.0/size;
vec3 resColor = vec3(0.0);
int ws = (kernelSize-1)/2;
float wSum = 0;
for(int dy=-ws; dy<=ws; dy++) for(int dx=-ws; dx<=ws; dx++)
{
float xx = gl_FragCoord.x / size.x + (dx * texelSize.x);
float yy = gl_FragCoord.y / size.y + (dy * texelSize.y);
float w = exp(-(dx*dx+dy*dy)/(2.0 * sigma * sigma));
wSum += w;
resColor += w*texture(colorTex, vec2(xx, yy)).rgb;
}
out_Color = vec4(resColor/wSum, 1.0);
}
2. Subsurface scattering in diffuse
- subsurface scattering은 metallic이 아닌 non-metallic에만 적용
-> diffuse + specular인 BRDF 계산 과정에서 diffuse만 분리하여 subsurface scattering 적용 (diffuse에만 blur가 적용되고, specular는 적용되지 않아 선명)
- 또한, 실제 사람을 촬영하여 만든 diffuse texture(color)는 subsurface scattering이 된 상태이므로 컬러를 입히는 과정은 이후에 진행
- diffFBO: diffuse BRDF로만 모델 렌더링 (normal, light)
- specFBO: specular BRDF로만 모델 렌더링 (color 포함)
- GaussianFBO: diffFBO의 texture에 Gaussian blur
- 기본 FB: GaussianFBO + specFBO로 화면에 띄우기
✔️ diffShader.frag
- color는 diffuse texture대신 vec4(1)을 사용
- diffuse BRDF로만 모델 렌더링
- normal mapping
- PBR light
void main(void)
{
vec3 L = lightPosition - worldPosition;
vec3 l = normalize(L);
vec3 n = normalize(normal);
vec3 v = normalize(cameraPosition - worldPosition);
vec3 h = normalize(l+v);
mat3 TBN = getTBN(n);
vec3 normVec = texture(normTex,texCoords).rgb*2-1;
n = normalize(TBN * normVec);
float NoV = abs(dot(n, v)) + 1e-5;
float NoL = clamp(dot(n, l), 0.0, 1.0);
float NoH = clamp(dot(n, h), 0.0, 1.0);
float LoH = clamp(dot(l, h), 0.0, 1.0);
vec4 diffColor = vec4(1);
vec3 Fd = diffColor.rgb * Fd_Lambert();
vec3 c = Fd * (lightColor/dot(L, L)) * NoL;
out_Color = vec4(c, 1);
}
✔️ GaussianBlur.frag
✔️ specShader.frag
- specular BRDF로만 모델 렌더링
- diffuse texture를 사용해 color 적용 (Gamma correction 필요)
void main(void)
{
vec3 L = lightPosition - worldPosition;
vec3 l = normalize(L);
vec3 n = normalize(normal);
vec3 v = normalize(cameraPosition - worldPosition);
vec3 h = normalize(l+v);
float NoV = abs(dot(n, v)) + 1e-5;
float NoL = clamp(dot(n, l), 0.0, 1.0);
float NoH = clamp(dot(n, h), 0.0, 1.0);
float LoH = clamp(dot(l, h), 0.0, 1.0);
vec3 f0 = vec3(0.028, 0.028, 0.028);
float roughness = texture(roughTex, texCoords).r;
roughness *= roughness;
float D = D_GGX(NoH, NoH * roughness);
float V = V_SmithGGXCorrelated(NoV, NoL, roughness);
vec3 F = F_Schlick(LoH, f0);
vec3 Fr = (D * V) * F;
vec4 color = texture(diffTex, texCoords);
color.rgb = pow(color.rgb, vec3(2.2));
vec3 c = color.rgb + Fr;
out_Color = vec4(pow(c, vec3(1/2.2)), 1);
}
✔️ screenShader.frag
- gaussian blur된 diffuse와 specular 합쳐서 화면에 띄우기
void main(void)
{
vec3 d = texture(gaussianDiffTex, gl_FragCoord.xy / size).rgb;
vec3 s = texture(specTex, gl_FragCoord.xy / size).rgb;
out_Color = vec4(vec3(d+s), 1);
}