Learning
레슨 7 / 8·25분

실전: 비주얼 이펙트

셰이더로 비주얼 이펙트 만들기

지금까지 배운 GLSL 기법을 종합하여 인터랙티브 비주얼 이펙트를 만듭니다. 노이즈, 수학 함수, UV 조작, 색상 처리를 조합하여 불꽃, 오로라, 플라즈마, 워터 리플 등의 효과를 구현합니다. 마우스 위치와 시간 기반의 동적인 비주얼을 완성합니다.

glsl
precision mediump float;

uniform float u_time;
uniform vec2 u_resolution;
uniform vec2 u_mouse;

// ── 불꽃(Fire) 이펙트 ──
float random(vec2 st) {
  return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453);
}

float noise(vec2 st) {
  vec2 i = floor(st);
  vec2 f = fract(st);
  vec2 u = f * f * (3.0 - 2.0 * f);
  float a = random(i);
  float b = random(i + vec2(1.0, 0.0));
  float c = random(i + vec2(0.0, 1.0));
  float d = random(i + vec2(1.0, 1.0));
  return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}

float fbm(vec2 st) {
  float v = 0.0; float a = 0.5;
  for (int i = 0; i < 5; i++) {
    v += a * noise(st);
    st *= 2.0; a *= 0.5;
  }
  return v;
}

void main() {
  vec2 st = gl_FragCoord.xy / u_resolution;

  // 불꽃은 아래에서 위로 올라감
  vec2 fireUV = st;
  fireUV.y = 1.0 - fireUV.y; // y축 반전

  // 노이즈로 불꽃 형태 생성
  float fire = fbm(vec2(fireUV.x * 4.0, fireUV.y * 3.0 - u_time * 2.0));

  // 높이에 따라 불꽃 강도 감소
  fire *= smoothstep(1.0, 0.2, fireUV.y);

  // 중앙에 집중
  float centerFade = smoothstep(0.5, 0.0, abs(st.x - 0.5));
  fire *= centerFade;

  // 불꽃 색상 매핑 (검정 → 빨강 → 노랑 → 흰색)
  vec3 color = vec3(0.0);
  color = mix(color, vec3(0.5, 0.0, 0.0), smoothstep(0.1, 0.3, fire));
  color = mix(color, vec3(1.0, 0.3, 0.0), smoothstep(0.3, 0.5, fire));
  color = mix(color, vec3(1.0, 0.8, 0.2), smoothstep(0.5, 0.7, fire));
  color = mix(color, vec3(1.0, 1.0, 0.9), smoothstep(0.7, 0.9, fire));

  gl_FragColor = vec4(color, 1.0);
}
glsl
precision mediump float;

uniform float u_time;
uniform vec2 u_resolution;
uniform vec2 u_mouse;

// ── 오로라(Aurora) 이펙트 ──
float random(vec2 st) {
  return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453);
}

float noise(vec2 st) {
  vec2 i = floor(st);
  vec2 f = fract(st);
  vec2 u = f * f * (3.0 - 2.0 * f);
  float a = random(i);
  float b = random(i + vec2(1.0, 0.0));
  float c = random(i + vec2(0.0, 1.0));
  float d = random(i + vec2(1.0, 1.0));
  return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}

void main() {
  vec2 st = gl_FragCoord.xy / u_resolution;

  // 마우스 영향
  vec2 mouseNorm = u_mouse / u_resolution;

  // 여러 겹의 물결
  float wave1 = sin(st.x * 6.0 + u_time + mouseNorm.x * 3.0) * 0.15;
  float wave2 = sin(st.x * 10.0 - u_time * 0.7) * 0.08;
  float wave3 = noise(vec2(st.x * 3.0, u_time * 0.3)) * 0.12;

  float aurora = wave1 + wave2 + wave3;

  // 오로라 띠 위치 (화면 상단 1/3)
  float band = smoothstep(0.15, 0.0, abs(st.y - 0.65 - aurora));
  band += smoothstep(0.1, 0.0, abs(st.y - 0.7 - aurora * 0.8)) * 0.5;

  // 색상 (녹색 ~ 보라)
  vec3 auroraColor = mix(
    vec3(0.1, 0.8, 0.4),   // 녹색
    vec3(0.5, 0.2, 0.9),   // 보라
    sin(st.x * 3.0 + u_time * 0.5) * 0.5 + 0.5
  );

  // 밤하늘 배경
  vec3 sky = vec3(0.02, 0.02, 0.08);
  // 별
  float star = step(0.998, random(floor(st * 200.0)));
  sky += star * 0.5;

  vec3 color = sky + auroraColor * band;

  gl_FragColor = vec4(color, 1.0);
}
glsl
precision mediump float;

uniform float u_time;
uniform vec2 u_resolution;
uniform vec2 u_mouse;

// ── 워터 리플(Water Ripple) 이펙트 ──
void main() {
  vec2 st = (gl_FragCoord.xy - u_resolution * 0.5) / u_resolution.y;
  vec2 mouseNorm = (u_mouse - u_resolution * 0.5) / u_resolution.y;

  // 마우스 위치에서 동심원 파장
  float dist = length(st - mouseNorm);
  float ripple = sin(dist * 40.0 - u_time * 6.0);
  ripple *= smoothstep(0.5, 0.0, dist); // 거리에 따라 감쇄

  // 여러 파원 추가
  float dist2 = length(st - vec2(0.2, 0.2));
  float ripple2 = sin(dist2 * 30.0 - u_time * 4.0);
  ripple2 *= smoothstep(0.4, 0.0, dist2) * 0.5;

  float totalRipple = ripple + ripple2;

  // 물 표면 굴절 효과
  vec2 distortion = vec2(
    totalRipple * 0.02,
    totalRipple * 0.02
  );

  // 배경색에 굴절 적용
  vec2 bgUV = st + distortion;
  vec3 waterColor = vec3(0.0, 0.2, 0.4);

  // 코스틱(caustics) 패턴
  float caustic = abs(sin(bgUV.x * 15.0 + u_time) *
                      sin(bgUV.y * 15.0 + u_time * 0.8));
  waterColor += vec3(0.1, 0.3, 0.4) * caustic * 0.3;

  // 반사 하이라이트
  float highlight = pow(max(0.0, totalRipple), 3.0) * 0.5;
  waterColor += vec3(highlight);

  gl_FragColor = vec4(waterColor, 1.0);
}
  • Fire Effect — FBM + 높이 감쇠 + 온도 기반 색상 매핑
  • Aurora Effect — sin 파동 중첩 + 띠 형태 마스킹 + 색상 변이
  • Water Ripple — 동심원 sin + 거리 감쇠 + 코스틱 패턴
  • smoothstep으로 부드러운 마스킹과 감쇠 적용
  • mix()로 온도/높이에 따른 색상 그라데이션 매핑
  • u_mouse로 인터랙티브 요소 추가 (파원 위치 등)
💡

셰이더 이펙트를 개발할 때는 ShaderToy(shadertoy.com)나 The Book of Shaders(thebookofshaders.com)를 참고하세요. ShaderToy에서 iResolution, iTime, iMouse는 각각 u_resolution, u_time, u_mouse에 대응합니다.