[2023 동계 모각소] 알구자구 5주차

jungizz_·2024년 2월 5일
0

모각소

목록 보기
11/12
post-thumbnail

📝 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를 생성함

진행 과정

  1. baseFBO에 3d 모델을 띄우고, 텍스처링 (이전까지 한 과정)
  2. baseFBO의 colorTexture를 사용하여 gaussianFBO에서 gaussian blur 적용
  3. gaussianGBO의 color texture를 사용하여 기본 Framebuffer에 그리기

✔️ main.cpp

  • FBO struct 생성

. . .

// Framebuffer Object structure
struct FBO {
    GLuint frameBuffer = 0;
    GLuint colorTexBuffer = 0;
    GLuint depthBuffer = 0;
};

FBO baseFBO; //기존 framebuffer object도 struct로 수정
FBO gaussianFBO; // gaussian blur를 적용할 FBO

Program program;
Program screenProgram;
Program gaussianProgram; // gaussian blur를 적용할 shader를 사용하기 위한 program 생성

. . .

void init() {

    . . .
    
    program.loadShaders("shader.vert", "shader.frag");
    screenProgram.loadShaders("screenShader.vert", "screenShader.frag");
    gaussianProgram.loadShaders("gaussianBlur.vert", "gaussianBlur.frag"); 
    
    // (VBO)
    
    // (VAO)
    
    // (element Buffer)
    
    // (load Texture)
    
    // (quad plane VAO)
    
    // (baseFBO: struct에 맞춰 변수 수정)
	
    // gaussian Frame Buffer Object (baseFBO와 같은 과정)
    glGenFramebuffers(1, &gaussianFBO.frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, gaussianFBO.frameBuffer);

    // texture buffer object to be used for colorbuffer
    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); // image data를 load하여 연결할 필요 없으므로 마지막 인자에 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); // attachment

    // texture buffer object to be used for depth buffer
    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); // image data를 load하여 연결할 필요 없으므로 마지막 인자에 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); // attachment

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
}

void render(GLFWwindow* window) 
{
    // (1. draw on framebuffer object)
    
    . . .
    
    // 2. draw on gaussianFBO
    glBindFramebuffer(GL_FRAMEBUFFER, gaussianFBO.frameBuffer); // gaussianFBO binding
    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); // gaussianBlur shader 사용

    // baseFBO의 colortexure에 gaussian blur를 적용하기 위해 fragment shader에게 넘겨주기
    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);

	// window size 정보를 fragment shader에게 넘겨주기
    GLuint sizeLocation = glGetUniformLocation(gaussianProgram.programID, "size");
    glUniform2f(sizeLocation, static_cast<float>(nowSize.x), static_cast<float>(nowSize.y));

    // quad VAO binding하여 vertex shader에게 quad vertex정보 넘겨주기
    // (Draw a quad to apply Gaussian blur)
    glBindVertexArray(quadArrrayBuffer);
    glDrawArrays(GL_TRIANGLES, 0, 6);
    glBindVertexArray(0);


    // 3. draw on default framebuffer (quad plane with the attached framebuffer color texture)
    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);

    // gaussianFBO의 color texture(blur 된 상태)를 그리기 위해 fragment shader에게 넘겨주기
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, gaussianFBO.colorTexBuffer);
    GLuint blurTexLocation = glGetUniformLocation(screenProgram.programID, "colorTex");
    glUniform1i(blurTexLocation, 0);

	// window size 정보를 fragment shader에게 넘겨주기
    sizeLocation = glGetUniformLocation(screenProgram.programID, "size");
    glUniform2f(sizeLocation, static_cast<float>(nowSize.x), static_cast<float>(nowSize.y));

	// quad VAO binding하여 vertex shader에게 quad vertex정보 넘겨주기
    // (Draw a quad to display the final result)
    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이 된 상태이므로 컬러를 입히는 과정은 이후에 진행
  1. diffFBO: diffuse BRDF로만 모델 렌더링 (normal, light)
  2. specFBO: specular BRDF로만 모델 렌더링 (color 포함)
  3. GaussianFBO: diffFBO의 texture에 Gaussian blur
  4. 기본 FB: GaussianFBO + specFBO로 화면에 띄우기

✔️ diffShader.frag

  • color는 diffuse texture대신 vec4(1)을 사용
  • diffuse BRDF로만 모델 렌더링
  • normal mapping
  • PBR light
void main(void)
{
	vec3 L = lightPosition - worldPosition;				// light unit vector
	vec3 l = normalize(L);							    // light unit vector
	vec3 n = normalize(normal);							// normal unit vector
	vec3 v = normalize(cameraPosition - worldPosition); // view unit vector
	vec3 h = normalize(l+v);							// half unit vector

	// normal mapping
	mat3 TBN = getTBN(n);
	vec3 normVec = texture(normTex,texCoords).rgb*2-1; // [0, 1] -> [-1, 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);
	

	// diffuse BRDF
	vec4 diffColor = vec4(1); // color는 diffuse texture가 아닌 vec4(1) 사용
	vec3 Fd = diffColor.rgb * Fd_Lambert();
	

	// final
	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;				// light unit vector
	vec3 l = normalize(L);							    // light unit vector
	vec3 n = normalize(normal);							// normal unit vector
	vec3 v = normalize(cameraPosition - worldPosition); // view unit vector
	vec3 h = normalize(l+v);							// half unit vector


	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); // 'skin' specular reflectance at normal incidnece angle
	float roughness = texture(roughTex, texCoords).r;
	roughness *= roughness; // remapping roughness (alpha)

	// specular BRDF
	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;

	// color
	vec4 color = texture(diffTex, texCoords);
	color.rgb = pow(color.rgb, vec3(2.2)); // gamma correction (to linear space)
	
	// final
	vec3 c = color.rgb + Fr;
	out_Color = vec4(pow(c, vec3(1/2.2)), 1); // gamma correction (to srgb)
}

✔️ 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);
}

profile
( •̀ .̫ •́ )✧

0개의 댓글