📝 4주차
- Framebuffer Object
- Screen-Space Perceptual Rendering of Human Skin
1. Frame buffer Object
- Frame buffer: 렌더링의 최종 결과가 기록되는 buffer 집합
- GLFW가 만들어둔 기본 frame buffer가 아닌, 사용자가 동적으로 생성한 임의의 frame buffer object(FBO)에 렌더링할 수 있음
- 다른 frame buffer object에 렌더링하면, 씬에 거울을 만들거나 post processing 등 가능
- 렌더링 전 bind되어있는 버퍼에 그려짐
glBindFramebuffer(GL_FRAMEBUFFER, 0)
glBindFramebuffer(GL_FRAMEBUFFER, (생성한 프레임버퍼 이름))
- FBO를 생성 시 요구사항: 최소한 하나의 buffer(depth, stencil 등)와 최소한 하나의 color texture를 attach
- FBO에 렌더링하면 화면에 보이진 않고(off-screen rendering) attach한 color texture와 depth buffer에 결과 정보가 저장됨
- 실제 윈도우 화면에 보이도록하기 위해서는 FBO에 렌더링하고 나온 color texture를 사용하여 기본 frame buffer에 그려야 함
make FBO and rendering
- FBO를 생성하고 기존 렌더링 과정을 FBO에서 진행
- FBO에서 생성된 color texture를 사용해서 기존 frame buffer에 띄우기
-> 화면에 color texture 이미지 한 장을 띄우게 되므로, 이미지를 그릴 quad 모델을 기존 framebuffer에 그려야함
✔️ main.cpp
- FBO를 생성하고, 비어있는 color texture buffer와 depth buffer를 framebuffer에 attach
- FBO를 binding해줬기 때문에 기본 frame buffer가 아닌 새로운 FBO에 렌더링
-> 기존 화면에 아무것도 뜨지 않음
void init() {
. . .
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
GLuint colorTexBuffer;
glGenTextures(1, &colorTexBuffer);
glBindTexture(GL_TEXTURE_2D, colorTexBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, 640, 480, 0, GL_RGB, GL_UNSIGNED_BYTE, 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, colorTexBuffer, 0);
GLuint depthBuffer = 0;
glGenTextures(1, &depthBuffer);
glBindTexture(GL_TEXTURE_2D, depthBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, 640, 480, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 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, 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, frameBuffer);
. . . (기존 렌더링 과정)
}
draw FBO color texture on default framebuffer
- 기본 framebuffer를 binding하여 FBO의 color texture를 렌더링
- texture를 적용시킬 quad plane 모델 및 새로운 shader code 필요
✔️ main.cpp
. . .
GLuint quadVertexBuffer = 0;
GLuint quadArrrayBuffer = 0;
float quadVertices[] = {
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f
};
GLuint frameBuffer = 0;
GLuint colorTexBuffer = 0;
GLuint depthBuffer = 0;
Program program;
Program screenProgram;
void init() {
. . .
program.loadShaders("shader.vert", "shader.frag");
screenProgram.loadShaders("screenShader.vert", "screenShader.frag");
. . .
glGenBuffers(1, &quadVertexBuffer);
glGenVertexArrays(1, &quadArrrayBuffer);
glBindVertexArray(quadArrrayBuffer);
glBindBuffer(GL_ARRAY_BUFFER, quadVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0);
}
void render(GLFWwindow* window)
{
. . .
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, colorTexBuffer);
GLuint colorTexLocation = glGetUniformLocation(screenProgram.programID, "colorTex");
glUniform1i(colorTexLocation, 0);
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);
glBindTexture(GL_TEXTURE_2D, 0);
}
✔️ screenShader.vert
- 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);
}
✔️ screenShader.frag
#version 150 core
uniform sampler2D colorTex;
uniform vec2 size;
out vec4 out_Color;
void main(void)
{
out_Color = texture(colorTex, gl_FragCoord.xy / size);
}
2. Screen-Space Perceptual Rendering of Human Skin
- subsurface scattering을 texture space가 아닌 screen space에서 하는 이유와 관련된 논문
Introduction
- scattering effects from texture to screen space
- 장점: reduce the problem of simulating translucency to a postprocess
- 단점: have less information to work
The Diffusion Approximation
Diffuse Profile
- 피부에 하얀색 라이트를 비췄을 때, 가운데 점을 중심으로 빛이 산란
- x축: 빛의 입사 지점부터 산란된 거리
- y축: 반사 강도
- 파장마다 방출 강도가 다름, R>G>B 순서로 중심에서 거리가 멀어지더라도 빛이 표면에서 방출됨
- 빛이 입사한 지점은 RGB 모두 값이 높아 하얀색 점이고, 거리가 멀어질 수록 빨간색 빛이 많이 남아 빨간색 빛이 보이는 것
Dipole model
- R: diffusion profile
- contribution of the incoming light ray는 두 개의 virtual source 중 하나로 간주
- 표면 아래 음수
- 표면 위의 양수
→ 이래서 dipole(양극의)이라고 하는 것
Bidirectional Scattering Surface Reflectance Distribution Function (BSSRDF)
- Sd(xi,ωi;xo,ωo)=π1Ft(xi,ωi)R(∣∣xi−xo∣∣2)Ft(xo,ωo)
- xi,ωi: incident light의 위치 및 각도
- xo,ωo: radiated light의 위치 및 각도
- Ft: Fresnel transmittance
- R: diffusion profile of the material
- dipole model을 multipole appriximation으로 확장하고, sum of dioples로 정의
→ better simulation of multilayered materials
Translucency
- transparency처럼 빛이 통과할 수 있지만, scattering될 수 있는 성질
- ex) 치아 끝부분에 약간 투명한 부분
Screen-space Algorithm
- texture space가 아닌, screen space에서 diffusion approximation
- 현재의 피부 실시간 렌더링 방법은 texture space에 기반하지만, texture space에서 irradiance map을 렌더링하긴 어렵다
- SSS가 적용 안된 matte, diffuse, depth(rendered image)를 input으로 받고, diffusion profiles을 rendered diffuse image에 적용(=SSS 적용)
- SSS는 specular highlight가 아닌 diffuse component of the illumination에만 적용되기 때문
- diffuse와 specular components를 따로 저장
Two-Pass Rendering (고렌특 강노)
- Geometry pass1
- diffuse lighting을 계산하고 framebuffer에 저장
- SSS pass (image space)
- diffuse lighted image를 블러 (using diffusion profile as the blur kernel)
- considering surface discontinuity
- using sampling method
- Geometry pass2
- SSS의 결과를 diffuse lighting으로 사용
- spcular lighting 추가
- 이미지에서 필요한 부분에만 convolution을 적용하면 되므로, matte mask에 의존하여 선택적인 방법으로 pixel shader에 적용
- Screen space에서 작업하려면, kernel 크기가 조절되야함
- 멀리 떨어진 물체를 나타내는 픽셀일수록 좁은 커널
- depth map에서 gradient가 클수록 좁은 커널 (edge에서는 작은 범위에서 convolution하여 피부/배경 픽셀이 섞이지 않도록)
- kernel width에 아래 stretch factors를 곱해서 조절
- sx=d(x,y)+β⋅abs(∇xd(x,y))α,sy=d(x,y)+β⋅abs(∇yd(x,y))α
- texture space skin shader(위) vs screen space skin shader(아래)
- stretch map, irradiance map 필요 없고, convolution도 screen space에서 한번만 함
- 마지막 bloom pass는 최적화도 안하고, 마지막 이미지에 적용되기 때문에 오브젝트 수가 많아져도 계산 시간이 늘어나지도 않음