표면 연속성(G0/G1/G2)

Surface continuity (G0/G1/G2)

두 곡선·곡면이 이어지는 매끄러움의 등급 — 위치만 맞는 G0, 접선까지 맞는 G1, 곡률까지 맞는 G2.

다른 이름: Curvature continuityClass-A surface커브 연속성
···
html
<div class="sc-wrap">
  <div class="sc-panel">
    <svg viewBox="0 0 100 70" class="sc-svg">
      <path class="sc-curve" d="M6,50 C30,50 40,20 50,20"/>
      <path class="sc-curve" d="M50,20 C60,20 72,55 94,55"/>
      <g class="sc-comb sc-jump"></g>
    </svg>
    <span class="sc-cap">G0 — kink</span>
  </div>
  <div class="sc-panel">
    <svg viewBox="0 0 100 70" class="sc-svg">
      <path class="sc-curve" d="M6,50 C30,50 42,22 50,22"/>
      <path class="sc-curve" d="M50,22 C58,22 72,52 94,52"/>
      <g class="sc-comb sc-step"></g>
    </svg>
    <span class="sc-cap">G1 — comb steps</span>
  </div>
  <div class="sc-panel">
    <svg viewBox="0 0 100 70" class="sc-svg">
      <path class="sc-curve sc-smooth" d="M6,50 C30,50 34,22 50,22 C66,22 70,50 94,50"/>
      <g class="sc-comb sc-flow"></g>
    </svg>
    <span class="sc-cap">G2 — comb flows</span>
  </div>
</div>
css
.sc-wrap{display:flex;gap:clamp(6px,3vmin,18px);align-items:center;justify-content:center;width:100%;height:100%;padding:clamp(8px,3vmin,14px)}
.sc-panel{flex:1;max-width:190px;text-align:center}
.sc-svg{width:100%;height:auto}
.sc-curve{fill:none;stroke:var(--fg);stroke-width:1.6}
.sc-smooth{stroke:var(--accent-3)}
.sc-comb line{stroke:var(--accent-2);stroke-width:.8}
.sc-cap{display:block;margin-top:.5em;font-size:clamp(8px,2.2vmin,11px);color:var(--muted)}
js
function buildComb(el, ticks) {
  el.innerHTML = ticks.map(([x, y, len, ang]) => {
    const rad = ang * Math.PI / 180;
    const dx = Math.cos(rad) * len, dy = Math.sin(rad) * len;
    return `<line x1="${x}" y1="${y}" x2="${x + dx}" y2="${y + dy}"/>`;
  }).join('');
}
const jump = [];
for (let i = 0; i <= 6; i++) jump.push([10 + i * 6, 42, 3 + i * 0.4, -70]);
for (let i = 0; i <= 6; i++) jump.push([54 + i * 6, 35 - i, 9 - i * 1.1, 120]);
buildComb(document.querySelector('.sc-jump'), jump);
const step = [];
for (let i = 0; i <= 6; i++) step.push([10 + i * 6, 42 - i * 0.5, 2 + i * 0.5, -80]);
for (let i = 0; i <= 6; i++) step.push([54 + i * 6, 34 - i * 0.3, 7 - i * 0.8, 100]);
buildComb(document.querySelector('.sc-step'), step);
const flow = [];
for (let i = 0; i <= 13; i++) {
  const x = 10 + i * 6;
  const len = 2 + 5 * Math.sin((i / 13) * Math.PI);
  flow.push([x, 42 - i * 1.1, len, -80 + i * 12]);
}
buildComb(document.querySelector('.sc-flow'), flow);

두 곡선을 이어붙일 때 "잘 이어졌다"는 말은 사실 세 단계로 나뉩니다. G0(위치 연속)은 두 곡선의 끝점이 그냥 만나기만 하면 됩니다 — 방향이 완전히 달라도 꺾여 보이는 각(kink)이 남습니다. G1(접선 연속)은 만나는 지점에서 두 곡선의 방향(탄젠트)까지 일치해서 꺾임은 없지만, 얼마나 휘어 있는지(곡률)는 갑자기 바뀔 수 있어 반사 하이라이트가 그 지점에서 툭 끊깁니다. G2(곡률 연속)는 곡률까지 매끄럽게 이어져서, 표면에 반사되는 빛의 줄무늬(하이라이트)가 그 경계를 지나도 끊기지 않고 흐릅니다.

이 차이를 눈으로 확인하는 도구가 곡률 콤(curvature comb)입니다 — 곡선을 따라 일정 간격으로 수직인 짧은 빗살을 곡률 크기에 비례한 길이로 그리면, G0 경계에서는 빗살 방향 자체가 어긋나고, G1 경계에서는 빗살 길이가 뚝 끊기고, G2 에서는 빗살이 매끄럽게 변합니다. 자동차 바디, 가전제품 외피처럼 표면에 빛이 반사되는 것 자체가 품질로 읽히는 제품("Class-A 서피스")은 G2 이상을 요구하는 경우가 흔합니다.

Alias, Fusion 360, Rhino 같은 서피스 모델링 툴은 이 연속성을 곡률 콤이나 등고선(zebra stripe) 분석으로 시각화해서, 눈으로는 매끈해 보이지만 실제로는 G1 에서 끊겨 있는 지점을 잡아냅니다. 디자인 시스템의 이징 곡선을 이어붙일 때도 같은 문제가 생깁니다 — 두 애니메이션 구간의 속도(1차 도함수)는 맞아도 가속도(2차 도함수)가 갑자기 바뀌면 눈에는 "매끈해 보이지만 어딘가 뚝뚝 끊기는" 모션이 됩니다.

언제 쓰나

자동차·가전처럼 반사 품질이 중요한 서피스를 모델링할 때, 이어붙인 곡선이 매끈해 보이는지 의심될 때.