레슨 6 / 8·20분
포스트 프로세싱
포스트 프로세싱 셰이더
포스트 프로세싱은 3D 씬이 렌더링된 후 최종 이미지에 셰이더 효과를 적용하는 기법입니다. 렌더링 결과를 텍스처(FBO/Framebuffer Object)에 저장한 뒤, 이 텍스처를 화면 전체 사각형(Quad)에 매핑하면서 셰이더로 블러, 비네팅, 색상 보정, 글리치 등의 효과를 적용합니다.
glsl
precision mediump float;
uniform sampler2D u_scene; // 렌더링된 씬 텍스처
uniform vec2 u_resolution;
uniform float u_time;
// ── 박스 블러 (Box Blur) ──
vec4 boxBlur(sampler2D tex, vec2 uv, vec2 texelSize, float radius) {
vec4 color = vec4(0.0);
float count = 0.0;
for (float x = -4.0; x <= 4.0; x += 1.0) {
for (float y = -4.0; y <= 4.0; y += 1.0) {
if (length(vec2(x, y)) <= radius) {
color += texture2D(tex, uv + vec2(x, y) * texelSize);
count += 1.0;
}
}
}
return color / count;
}
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
vec2 texelSize = 1.0 / u_resolution; // 1 픽셀 크기
// 블러 적용
vec4 blurred = boxBlur(u_scene, uv, texelSize, 4.0);
vec4 original = texture2D(u_scene, uv);
// 원본과 블러 블렌딩
gl_FragColor = mix(original, blurred, 0.5);
}glsl
precision mediump float;
uniform sampler2D u_scene;
uniform vec2 u_resolution;
uniform float u_time;
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
// ── 비네팅 (Vignette) ──
// 화면 가장자리를 어둡게 하는 효과
float dist = distance(uv, vec2(0.5));
float vignette = smoothstep(0.5, 0.2, dist);
// ── 색수차 (Chromatic Aberration) ──
// RGB 채널을 약간 분리하여 렌즈 효과
float offset = 0.005;
float r = texture2D(u_scene, uv + vec2(offset, 0.0)).r;
float g = texture2D(u_scene, uv).g;
float b = texture2D(u_scene, uv - vec2(offset, 0.0)).b;
vec3 color = vec3(r, g, b);
// ── 색상 보정 ──
// 밝기 (brightness)
color *= 1.1;
// 대비 (contrast)
color = (color - 0.5) * 1.2 + 0.5;
// 채도 (saturation)
float gray = dot(color, vec3(0.299, 0.587, 0.114));
color = mix(vec3(gray), color, 1.3);
// 비네팅 적용
color *= vignette;
gl_FragColor = vec4(color, 1.0);
}glsl
precision mediump float;
uniform sampler2D u_scene;
uniform vec2 u_resolution;
uniform float u_time;
// ── 의사 랜덤 ──
float random(vec2 st) {
return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453);
}
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
// ── 글리치 효과 ──
// 랜덤한 시간에 수평 줄이 어긋남
float glitchStrength = step(0.95, random(vec2(floor(u_time * 10.0))));
float lineNoise = step(0.5, random(vec2(floor(uv.y * 50.0), u_time)));
float offset = glitchStrength * lineNoise * 0.1;
uv.x += offset;
vec4 color = texture2D(u_scene, uv);
// 글리치 시 RGB 분리 강화
if (glitchStrength > 0.5) {
float shift = 0.02;
color.r = texture2D(u_scene, uv + vec2(shift, 0.0)).r;
color.b = texture2D(u_scene, uv - vec2(shift, 0.0)).b;
}
// ── 스캔라인 (CRT 효과) ──
float scanline = sin(gl_FragCoord.y * 1.5) * 0.04;
color.rgb -= scanline;
// ── 필름 그레인 ──
float grain = (random(uv + u_time) - 0.5) * 0.08;
color.rgb += grain;
gl_FragColor = color;
}- •Box Blur — 주변 픽셀의 평균으로 흐림 효과
- •Vignette — distance(uv, center)로 가장자리 어둡게
- •Chromatic Aberration — RGB 채널별 UV 오프셋으로 색수차
- •Color Grading — 밝기, 대비, 채도 조절
- •Glitch — 랜덤 수평 오프셋과 RGB 분리로 디지털 왜곡
- •Scanline — sin(y)로 CRT 모니터 스캔라인 효과
- •Film Grain — random()으로 필름 노이즈 추가
💡
포스트 프로세싱 효과를 체이닝(연쇄)하려면 FBO를 여러 개 사용하여 핑퐁 렌더링을 합니다. 예를 들어 FBO-A에 블러를 적용한 결과를 FBO-B에 저장하고, FBO-B에 비네팅을 적용한 결과를 화면에 출력합니다.