Tutorial

WebGL 2.0 레이마칭 렌더링 원리

WebGL 2.0 레이마칭 기술을 깊이 있게 살펴보고, SDF 부호 거리 필드를 사용해 실시간 볼륨 렌더링을 구현하는 방법과 이 기술이 GPU 성능을 한계까지 끌어올리는 방식을 설명합니다.

🚀 volumeshadertest
12월 3일
10분 소요

WebGL 2.0 레이마칭, SDF, 실시간 볼륨 렌더링 심화 탐구

WebGL 2.0은 강력한 GPU 기능 세트를 해제하여 전통적으로 네이티브 API가 필요했던 고급 렌더링 기술을 가능하게 합니다. 그 중에서도 레이마칭은 최소한의 지오메트리로 복잡한 장면을 렌더링하는 가장 표현력이 풍부하고 수학적으로 우아한 방법 중 하나로 두각을 나타냅니다.

이 기사는 WebGL 2.0 레이마칭의 핵심 개념을 살펴보고, **부호 거리 필드(SDF)**가 어떻게 효율적인 렌더링을 가능하게 하는지 설명하며, 이 기술이 GPU에서 놀라운 성능을 끌어내는 방법을 보여줍니다.


1. 레이마칭이란?

레이마칭은 지오메트리를 화면에 투영하는 대신, 카메라에서 픽셀당 광선을 발사하고, 가상 장면을 단계적으로 이동하며, 광선이 표면에 닿거나 최대 범위에 도달할 때까지 거리를 평가하는 렌더링 방법입니다.

1.1 레이마칭 작동 방식 (시각적 다이어그램)

카메라
  |
  | 광선
  v
  +--------------------------------- 화면 픽셀
  |
  | 마칭 스텝 1      (거리 d1)
  |
  |---------> 마칭 스텝 2      (거리 d2)
  |
  |-----------------------> 마칭 스텝 3 ...
  |
거리 < ε 일 때 표면 도달

레이마칭은 본질적으로 루프입니다:

for (int i = 0; i < MAX_STEPS; i++) {
    float dist = sdf(currentPos);
    if (dist < EPSILON) hitSurface();
    currentPos += rayDir * dist;
}

마법의 요소는 우리가 전달하는 SDF 함수입니다.


2. 부호 거리 필드(SDF)란?

부호 거리 필드는 다음을 반환하는 함수입니다:

> 0  객체 외부 거리  
= 0  정확히 표면에  
< 0  객체 내부 거리  

2.1 SDF 다이어그램

            외부
        +------------+
        |   d = 0.5  |
        |            |
내부    |     SDF    | 표면 d = 0
d < 0   |            |
        |   d = -0.3 |
        +------------+

2.2 예제: 구 SDF

float sdSphere(vec3 p, float r) {
    return length(p) - r;
}

이러한 기본 모양을 부울 연산(합집합, 차집합, 교집합)과 결합하면, 전통적인 지오메트리 없이 복잡한 장면을 구축할 수 있습니다.


3. 왜 WebGL 2.0에서 SDF 레이마칭을 사용하는가?

3.1 장점

  • 정점 버퍼나 메시가 필요 없음 모든 것이 프래그먼트 셰이더에서 수학적으로 계산됩니다.

  • 무한한 기하학적 세부사항 SDF는 부드럽고 연속적인 표면을 정의합니다.

  • 동적이고 변형 가능한 객체 수학으로 모양을 쉽게 애니메이션화할 수 있습니다.

  • 컴팩트한 장면 완전한 3D 장면은 단 몇 줄의 GLSL로 정의될 수 있습니다.

3.2 이를 가능하게 하는 WebGL 2.0 기능

  • 고정밀도 부동소수점
  • 완전한 GLSL ES 3.0 셰이더 지원
  • UBO(유니폼 버퍼)
  • 부동소수점 텍스처
  • 더 유연한 루프 및 분기

이를 통해 WebGL 1.0에서 어려웠던 안정적이고 고성능 레이마칭이 가능해집니다.


4. WebGL 2.0 레이마처 단계별 구축

4.1 장면 광선 설정

각 픽셀에 대해 광선을 계산:

vec3 rayOrigin = cameraPos;
vec3 rayDir = normalize(uv.x * camRight + uv.y * camUp + camForward);

4.2 레이마칭 루프

float marchRay(vec3 ro, vec3 rd) {
    float totalDist = 0.0;

    for (int i = 0; i < MAX_STEPS; i++) {
        vec3 p = ro + rd * totalDist;
        float d = map(p);     // 당신의 SDF 함수

        if (d < EPSILON) break;    // 히트
        if (totalDist > MAX_DISTANCE) break;  // 미스

        totalDist += d;
    }
    return totalDist;
}

4.3 SDF 장면 정의

float map(vec3 p) {
    float s = sdSphere(p, 1.0);
    float box = sdBox(p - vec3(2.0, 0.0, 0.0), vec3(0.7));
    return min(s, box); // 합집합
}

4.4 법선 계산

기울기 근사 사용:

vec3 calcNormal(vec3 p) {
    const vec2 e = vec2(0.001, 0.0);
    return normalize(vec3(
        map(p + e.xyy) - map(p - e.xyy),
        map(p + e.yxy) - map(p - e.yxy),
        map(p + e.yyx) - map(p - e.yyx)
    ));
}

4.5 조명 모델

간단한 확산 셰이딩:

float diffuse = max(dot(normal, lightDir), 0.0);

5. 볼륨 렌더링 (안개, 구름, 연기)

SDF 레이마칭은 볼륨 렌더링으로 확장될 수 있으며, 표면에서 멈추는 대신 각 단계가 색상과 밀도에 기여합니다.

5.1 볼륨 다이어그램

광선 →
[낮은 밀도] [중간] [높은 밀도] [불투명]
   +--------+---------+---------+-------+
   샘플1     샘플2      샘플3      샘플4

5.2 볼륨 누적 모델

vec3 color = vec3(0);
float absorption = 1.0;

for (int i = 0; i < STEPS; i++) {
    float density = computeDensity(p);
    vec3 sampleColor = density * vec3(0.6, 0.7, 1.0);

    color += sampleColor * absorption;
    absorption *= (1.0 - density * 0.1);

    p += rd * STEP_SIZE;
}

레이마칭 볼륨은 더 비용이 많이 들지만, WebGL 2.0의 성능 개선(특히 모바일 GPU에서)으로 실시간 구름, 안개, 성운이 가능해집니다.


6. 성능 최적화 기술

레이마칭은 무거울 수 있으므로 최적화가 필수적입니다.

6.1 주요 기술

  1. 거리 컬링 정확한 SDF 거리에 의존하여 큰 빈 영역을 건너뜁니다.

  2. 경계 볼륨 광선이 SDF가 존재하는 영역에 들어가는지 테스트합니다.

  3. 적응형 스텝 크기 표면 근처에서는 작은 스텝을 사용하고, 열린 공간에서는 큰 스텝을 사용합니다.

  4. 조기 종료 표면에 닿자마자 마칭을 중지합니다.

  5. MAX_STEPS 감소 실시간 렌더링의 일반적인 값은 64–128 범위입니다.

  6. WebGL 2.0 유니폼 및 버퍼 효율적 사용 변경되지 않는 값을 루프 밖으로 이동합니다.


7. WebGL 2.0 프로젝트의 코드 구조

디렉토리 레이아웃 예제:

shader/
  raymarch.vert
  raymarch.frag
src/
  webgl2-context.js
  camera.js
  renderer.js
index.html

일반적인 프래그먼트 셰이더 섹션:

// 1. 카메라/광선 설정
// 2. SDF 정의
// 3. 재질 및 조명 함수
// 4. 레이마칭 루프
// 5. 최종 색상 출력

8. 언제 레이마칭을 사용해야 하는가?

레이마칭은 다음에 뛰어납니다:

  • 무한한 부드러운 모양
  • 프랙탈
  • 공상과학 장면
  • 부호 거리 소프트 섀도우
  • 절차적 지오메트리
  • 볼륨 효과 (구름, 안개, 갓 레이)

레이마칭을 피해야 하는 경우:

  • 많은 상세한 복잡한 메시
  • 사실적인 스킨 캐릭터
  • 수천 개의 객체가 있는 대규모 동적 장면

레이마칭은 절차적 세계스타일화된 시각 효과에 가장 적합합니다.


9. 결론

WebGL 2.0 레이마칭은 수학과 GLSL만으로 고급 실시간 렌더링을 브라우저에 가져옵니다. SDF를 사용하면 단일 정점 버퍼 없이 복잡한 3D 장면을 만들 수 있습니다. 이 기술은 계산 집약적이지만, 신중한 최적화를 통해 네이티브 GPU 애플리케이션에 필적하는 표현력 있는 시각 효과가 가능합니다.

레이마칭은 수학, 예술, GPU 프로그래밍의 독특한 교차점을 나타내며, 현대 WebGL 개발에서 탐구할 가장 흥미진진한 기술 중 하나입니다.


Volume Shader BM Test과 어떻게 연결되는가

이론을 다 읽었다면, 다시 도구로 돌아가 봅시다. 위 설명을 실제 Volume Shader BM Test에 연결하는 핵심 포인트입니다.

왜 이 테스트는 일반 3D 데모보다 더 무거운가

일반 WebGL 데모는 수천 개의 폴리곤을 그릴 뿐입니다. 이 벤치마크는 GPU에 픽셀마다 레이마칭 루프를 1,000회 이상 돌리며, 지오메트리로 건너뛸 수도 없습니다. 게임에서 60 FPS가 나오는 중급 GPU도 Heavy 모드에서는 30 FPS 아래로 떨어지곤 합니다.

모바일에서 FPS가 떨어지는 이유

스마트폰 SoC는 발열이 생기면 즉시 스로틀링합니다. 처음 10초는 높지만 이후 GPU 클럭이 낮아집니다. 충전기에 연결하고 Light 또는 Medium 모드로 측정한 뒤 안정성으로 비교하세요. 자세한 내용은 모바일 GPU 테스트 가이드.

첫 실행이 느린 이유 (셰이더 컴파일)

처음 로드 시 브라우저는 GLSL을 드라이버용 바이너리로 컴파일하며, 일부 드라이버에서 수백 밀리초가 걸립니다. 시작 직후의 한 번의 끊김이 이것 때문입니다. 첫 번째 결과는 버리고 두 번째를 사용하세요.

결과를 공정하게 비교하는 법

같은 모드, 같은 브라우저 탭(포그라운드), 같은 전원 상태. Light / Medium / Heavy는 다른 셰이더 워크로드라 직접 비교할 수 없습니다. FPS 결과 가이드를 참고.

브라우저가 내장 GPU를 쓸 때

iGPU와 dGPU가 모두 있는 노트북에서 Chrome / Edge는 절전을 위해 iGPU를 기본 선택하기도 합니다. chrome://gpu에서 GL_RENDERER를 확인하고, iGPU가 표시되면 OS 그래픽 설정에서 전용 GPU를 강제하세요. 자세한 방법은 랙 해결 가이드.


직접 측정해 보기

이론은 충분합니다. 실제 벤치마크를 1분 안에 돌려보세요.

Volume Shader BM Test 시작 →

관련 가이드

태그
WebGL레이마칭볼륨 렌더링GPU 성능