Graphics Programming
단편 셰이더(Fragment Shader)와 프레임버퍼(Framebuffer) 본문
전부 back end에 대한 이야기. 즉 래스터화(rasterization) 이후
- 단편 셰이더로 할 수 있는 흥미로운 것들
- 데이터가 단편 셰이더에 도착하면 일어나는 일들
- 그 데이터를 다시 애플리케이션으로 얻어오는 방법
- 이미지 품질을 향상시키는 방법
- HDR 렌더링
- 안티앨리어싱 기법들
- 다른 색공간들
보간(interpolation)
flat in vec4 foo // 보간 없음(default: smooth)
- 기본적으로 정수는 flat, float은 smooth
- 입력이 점이면 아무 문제 없다
- 선이나 삼각형이면 어느 정점을 택하는가?
void glProvokingVertex(GLenum provokeMode)
GL_FIRST_VERTEX_CONVENTION, GL_LAST_VERTEX_CONVENTION(기본)
- OpenGL은 perspective-correct interpolation 사용
비활성화하려면 noperspective out vec2 texCoord 처럼
단편별 검사
- scissor test
glEnable(GL_SCISSOR_TEST)
glScissorIndexed(index, L,B,W,H)
* glClear()에도 적용됨
- stencil test
glEnable(GL_STENCIL_TEST)
glStencilFuncSeparate(face, func, ref, mask)
- 스텐실 테스트가 성공/실패하는 조건을 제어
- face: GL_FRONT, GL_BACK, GL_FRONT_AND_BACK
- func: GL_NEVER, ALWAYS, LESS, LEQUAL, EQUAL, GEQUAL, GREATER, NOTEQUAL
- (ref & mask)와 (현재스텐실값 & mask)를 비교
glStencilOpSeperate(face, sfail, dpfail, dppass)
- 테스트가 성공/실패했을 때 무엇을 할 지를 제어
- sfail: 스텐실 테스트 실패
- dpfail: 깊이 버퍼 테스트 실패
- dppass: 깊이 버퍼 테스트 성공
- operations = GL_KEEP, ZERO, REPLACE, INCR, DECR, INVERT, INCR_WRAP, DECR_WRAP
glStencilFunc(), glStencilOp() : face = GL_FRONT_AND_BACK
glStencilMaskSeparate(face, mask)
- 깊이 테스트
glEnable(GL_DEPTH_TEST)
void glDepthFunc(GLenum func)
Early Testing
스텐실 테스트 & 깊이 테스트는 해당 단편의 색상이 정해진 후에 수행된다
대부분의 그래픽스 하드웨어는, 셰이더에 부수효과가 없고 테스트 결과에 영향을 주지 않으면, 셰이더 실행 전에 테스트를 수행할 수 있다
내장 변수: gl_FragDepth
블렌딩
glEnable(GL_BLEND)
glBlendFunc()
glBlendColor()
Off-Screen Rendering
- 단편 셰이더의 출력은 back buffer로 들어간다
- 이 버퍼는 OS 또는 윈도우 시스템 소유
- 직접 버퍼를 만들어서 여기에 그릴 수 있다 (FBO)
glGenFramebuffers(n, id_ary)
glBindFramebuffer(target, framebuffer)
target: GL_FRAMEBUFFER, GL_READ_FRAMEBUFFER, GL_WRITE_FRAMEBUFFER
프레임버퍼의 저장 공간: 텍스처
glFramebufferTexture(target, attachment, texture, level)
- attachment: GL_[DEPTH/STENCIL/COLOR]_ATTACHMENT
- level: 밉맵 수준
하나의 FBO에 최소 8개의 텍스처를 붙일 수 있다
프레임버퍼 검사
glCheckFramebufferStatus(target)
지금까지 다룬 프레임버퍼
1. 기본 프레임버퍼
2. 프레임버퍼로서의 텍스처(GL_RGBA8 포맷)
8비트 부호 없는 정규화된 정수 형식
0.0 ~ 1.0을 256단계로 표현하는 것이 고작
- 하지만 단편 셰이더의 출력은 vec4
- 프레임버퍼 attachment는 1~4개의 성분을 가진다
- 이 성분들은 부동소수점 수 또는 정수 형식
- 음수를 저장할 수 있다
- 8비트보다 클 수 있다
고급 프레임버퍼 포맷
attachment 없이 렌더링
- 텍스처 여러 개를 하나의 프레임버퍼에 붙이고 하나의 셰이더로 모든 텍스처에 렌더링할 수 있다
- 프레임버퍼를 만들고는 아무 텍스처도 붙이지 않는 것이 가능하다
- 그럼 내 데이터는 어디로? -> 아무 효과 없음. 단편 셰이더에 선언된 출력들은 기각된다
- 하지만 단편 셰이더는 출력 변수에 기록하는 것 외에도 부수효과를 가질 수 있다
- imageStore 함수로 메모리에 기록
- atomicCounterIncrement/Decrement로 아토믹 카운터 증감
- FBO에 하나 이상의 attachment가 있으면, 그것들로부터 최대 가로/세로, 레이어 카운트, 샘플 카운트 등을 도출해낸다
- 이 속성들은 뷰포트를 클램핑할 크기를 결정한다
- attachment가 없으면 텍스처의 가용 메모리로 인한 제약이 사라진다
- 하지만 FBO는 이 정보들이 필요하다. 그 정보를 제공하는 다른 방법이 필요
glFramebufferParameteri(GLenum target, GLenum pname, GLint param)
- target: GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, GL_FRAMEBUFFER
- pname: GL_FRAMEBUFFER_DEFAULT, WIDTH, HEIGHT, LAYERS, SAMPLES, FIXED_SAMPLE_LOCATIONS
Floating-Point Framebuffers
- OpenGL 파이프라인 내부에서는 부동소수점 데이터를 이용
- 하지만 소스(텍스처)와 타겟(프레임버퍼 attachment)는 고정소수점 -> 정확도 손실
- 정점 셰이더에 전달되는 데이터는 대개 vec4
- 정점 셰이더의 출력이 기하구조를 따라 보간되어 단편 셰이더에 전달된다
- 256개 값 대신 1.1 * 10^(-38) ~ 3.4 * 10^38 에 이르는 값 사용 가능
- 색상별로 8비트만 지원하는 윈도우나 모니터에서는 어떻게 되는가?
- 출력이 [0, 1]로 잘리고 고정소수점 값으로 매핑됨
- 하지만 여전히 텍스처에 완전한 부동소수점 정확도로 렌더링하는 것이 가능 (HDR의 개념)
- Floating-point 포맷 사용
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA16F,32F, width, height)
다른 포맷들: RGB32F, RGB16F, RG32F, RG16F, R32F, R16F, R11F_G11F_B10F, DEPTH_COMPONENT32F, DEPTH_COMPONENT32F_STENCIL8
- HDR: light bloom, lens flare, light reflection, light refraction, crepuscular ray...
- 톤 매핑
- 0에서 255까지 값으로만 표시해야 하는 이미지를 부동소수점 데이터를 가지고 동적 생성하는 방법
- y = 1 - exp(-xp)