Files
sam-docs/features/construction-pmis/bim-viewer.md
김보곤 1330ebac01 docs: [construction-pmis] BIM 뷰어 기술 문서 추가
- Three.js 기반 웹 3D 건물 모델 뷰어 기술 원리 문서화
- 렌더링 파이프라인, 건물 모델 생성, React-Three.js 연동 패턴
- INDEX.md에 문서 등록
2026-03-12 12:50:06 +09:00

11 KiB
Raw Blame History

BIM 뷰어 — Three.js 기반 웹 3D 건물 모델 뷰어

작성일: 2026-03-12 상태: Phase 1 완료 (프로토타입) 소스: mng/resources/views/juil/bim-viewer.blade.php


1. 개요

1.1 목적

건설PMIS(Construction Project Management Information System)의 BIM(Building Information Modeling) 뷰어 기능이다. 웹 브라우저에서 별도 플러그인 없이 3D 건물 모델을 렌더링하고, 요소를 선택하여 속성 정보를 조회할 수 있다.

1.2 핵심 기술 스택

기술 역할 CDN
Three.js r128 WebGL 기반 3D 렌더링 엔진 cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js
OrbitControls 카메라 회전/줌/팬 조작 cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js
React 18 UI 컴포넌트 (사이드바, 툴바, 정보 패널) CDN + Babel 브라우저 트랜스파일
Tailwind CSS UI 스타일링 CDN

1.3 아키텍처

┌─────────────────────────────────────────────────┐
│  Blade 템플릿 (@extends layouts.app)            │
│  ┌─────────────────────────────────────────────┐│
│  │  React (CDN + Babel)                        ││
│  │  ┌──────┐  ┌──────────────┐  ┌───────────┐ ││
│  │  │사이드 │  │  Three.js    │  │ 정보 패널 │ ││
│  │  │ 바   │  │  3D Viewport │  │ (React)   │ ││
│  │  │(React)│  │  (WebGL)     │  │           │ ││
│  │  └──────┘  └──────────────┘  └───────────┘ ││
│  │            ┌──────────────┐                 ││
│  │            │  툴바(React) │                 ││
│  │            └──────────────┘                 ││
│  └─────────────────────────────────────────────┘│
└─────────────────────────────────────────────────┘
  • React: UI 컴포넌트 관리 (사이드바, 툴바, 정보 패널)
  • Three.js: 3D 렌더링 (건물, 조명, 그림자, 카메라)
  • 연결 방식: React useRef로 DOM 요소를 Three.js에 전달, useEffect로 생명주기 관리

2. Three.js 렌더링 파이프라인

2.1 기본 구조

브라우저 (Chrome/Edge)
  └── WebGL API (GPU 가속 그래픽)
       └── Three.js (WebGL 추상화 라이브러리)
            ├── Scene     — 3D 공간 (모든 객체의 컨테이너)
            ├── Camera    — 시점 (PerspectiveCamera, FOV 45도)
            ├── Renderer  — 화면 출력 (WebGLRenderer, 안티앨리어싱)
            ├── Lights    — 조명 (Hemisphere + Directional + Ambient)
            └── Mesh      — 물체 = Geometry(형태) + Material(재질)

2.2 렌더링 루프

Three.js는 매 프레임(~60fps)마다 장면을 다시 그린다:

animate() {
    requestAnimationFrame(() => this.animate());  // 다음 프레임 예약

    // 카메라 전환 애니메이션 (lerp: 선형 보간)
    if (this.targetPos) {
        this.camera.position.lerp(this.targetPos, 0.07);  // 현재→목표, 7%씩 이동
        this.controls.target.lerp(this.targetLook, 0.07);
        if (this.camera.position.distanceTo(this.targetPos) < 0.2)
            this.targetPos = null;  // 도착 시 애니메이션 종료
    }

    this.controls.update();   // 사용자 조작 반영
    this.renderer.render(this.scene, this.camera);  // GPU 렌더링
}

lerp (Linear Interpolation): 현재 값에서 목표 값으로 매 프레임 일정 비율만큼 이동하는 방식이다. 처음엔 빠르게, 목표에 가까울수록 느리게 이동하여 자연스러운 이징 효과를 만든다.

2.3 조명 구성

// 반구광 — 하늘(파랑) + 지면(갈색) 사이 자연 채광
HemisphereLight(0xb1e1ff, 0xb97a20, 0.55)

// 태양광 — 그림자 생성, 건물에 방향성 빛
DirectionalLight(0xffffff, 0.85)  // 위치: (45, 50, 35)
  └── Shadow Map: 2048×2048 (PCFSoftShadowMap)

// 환경광 — 그림자 영역의 최소 밝기
AmbientLight(0xffffff, 0.25)

3. 건물 모델 생성

3.1 건물 사양

항목
건물 용도 물류센터 (데모)
크기 60m (가로) × 30m (세로) × 12m (높이)
층수 지상 3층 (각 4m)
기둥 격자 7 × 4 (간격 10m × 10m)
좌표계 X = 가로(길이), Y = 높이(상향), Z = 세로(깊이)

3.2 핵심 원리 — 모든 요소는 박스의 조합

// 기둥 하나 = 가로 0.45m × 높이 3.7m × 세로 0.45m 박스
new THREE.BoxGeometry(0.45, 3.7, 0.45)

// 바닥 슬래브 = 60m × 0.3m × 30m 얇은 판
new THREE.BoxGeometry(60.6, 0.3, 30.6)

// 창문 = 반투명 재질 + 얇은 박스
new THREE.MeshPhongMaterial({ color: 0x00BCD4, transparent: true, opacity: 0.35 })

이것을 반복문으로 배치하면 건물이 완성된다:

// 7 × 4 격자 × 3층 = 기둥 84개
for (let floor = 0; floor < 3; floor++)
    for (let x = 0; x < 7; x++)
        for (let z = 0; z < 4; z++)
            this.box(0.45, 3.7, 0.45, [x * 10, floor * 4 + 2, z * 10], {...}, 'column');

3.3 요소 색상 체계

요소 색상 코드 시각적 개수
바닥 슬래브 0x81C784 연두 3
지붕 0x4CAF50 초록 1
기둥 0xFF9800 주황 84
0xFFC107 황금 33
벽체 0x42A5F5 파랑 ~50
창호 0x00BCD4 시안 (반투명) 18
출입문 0x8B4513 갈색 3
계단실 0xFFAB91 살몬 15
합계 ~200+

3.4 건물 구성 상세

지붕 슬래브 ────── y = 12.0m
  │
3F 기둥/벽/창 ─── y = 8.0 ~ 12.0m
  │
2F 바닥 슬래브 ── y = 8.0m
  │
2F 기둥/벽/창 ─── y = 4.0 ~ 8.0m
  │
1F 바닥 슬래브 ── y = 4.0m
  │
1F 기둥/벽/창 ─── y = 0.0 ~ 4.0m (후면: 하역장 도어 3개)
  │
기초 슬래브 ───── y = 0.0m
  │
지면 (그라운드) ─ y = -0.02m

4. 사용자 인터랙션

4.1 카메라 조작 (OrbitControls)

조작 동작 원리
마우스 드래그 카메라가 건물 중심을 공전 Orbit (구면 좌표계 회전)
스크롤 (휠) 줌 인/아웃 카메라 거리 조절
우클릭 드래그 시점 평행 이동 Pan (카메라 + 타겟 동시 이동)

OrbitControls는 damping (감쇠)이 적용되어 조작 후 부드럽게 멈춘다:

this.controls.enableDamping = true;
this.controls.dampingFactor = 0.08;

4.2 요소 클릭 선택 (Raycaster)

마우스 클릭 좌표 (2D 화면)
    ↓  NDC 좌표 변환 (-1 ~ +1)
카메라에서 클릭 방향으로 "광선(Ray)" 발사
    ↓  raycaster.intersectObjects()
광선이 3D 메시와 충돌 검사
    ↓
충돌한 메시의 userData (이름, 재질, 층, 치수) 읽기
    ↓
React 상태 업데이트 → 우측 패널에 정보 표시

각 요소에 메타데이터를 미리 저장해두어 클릭 시 즉시 조회된다:

mesh.userData = {
    type: 'column',        // 요소 유형
    name: 'C-001',         // 식별자
    material: 'H형강 (SS400)',  // 재질
    floor: '1F',           // 층
    dimensions: '400×400×3,700mm',  // 치수
    grid: '(1,1)',         // 격자 위치
};

4.3 시점 전환

프리셋 카메라 위치 용도
투시도 (55, 35, 55) 전체 조감
정면 (30, 8, -35) 남측 입면
우측 (85, 8, 15) 동측 입면
상부 (30, 55, 15) 평면도
배면 (30, 8, 55) 북측 (하역장)

lerp 보간으로 현재 위치에서 목표까지 부드럽게 전환된다.

4.4 요소 토글 & 와이어프레임

  • 요소 토글: THREE.Group 단위로 visible 속성을 전환하여 요소 유형별 표시/숨김
  • 와이어프레임: 모든 메시의 material.wireframe 속성을 일괄 전환

5. React-Three.js 연동 패턴

5.1 BimScene 클래스

Three.js 로직을 BimScene 클래스로 캡슐화하여 React와 분리한다:

class BimScene {
    constructor(el) { /* DOM 요소 참조 */ }
    init()          { /* Scene, Camera, Renderer, Controls, 건물 생성 */ }
    onClick(e)      { /* Raycaster로 요소 선택 */ }
    setView(preset) { /* 카메라 전환 */ }
    toggleGroup()   { /* 요소 유형 표시/숨김 */ }
    animate()       { /* 렌더 루프 */ }
    dispose()       { /* 리소스 정리 */ }
}

5.2 React에서 사용

function BimViewerApp() {
    const vpRef = useRef(null);      // 3D 뷰포트 DOM 참조
    const sceneRef = useRef(null);   // BimScene 인스턴스 참조

    useEffect(() => {
        const bim = new BimScene(vpRef.current);
        bim.init();
        bim.onSelect = setSelected;  // 선택 이벤트 → React 상태
        sceneRef.current = bim;
        return () => bim.dispose();  // 언마운트 시 정리
    }, []);

    return (
        <div className="flex">
            <BimSidebar />
            <div ref={vpRef} className="flex-1" />  {/* Three.js 렌더링 대상 */}
            <BimInfoPanel selected={selected} />
        </div>
    );
}

핵심 원칙:

  • Three.js는 React 렌더 사이클 밖에서 자체 애니메이션 루프를 실행한다
  • React → Three.js 통신: sceneRef.current.setView() 등 메서드 직접 호출
  • Three.js → React 통신: onSelect 콜백으로 React setState 호출

6. 파일 구조

파일 위치 설명
bim-viewer.blade.php mng/resources/views/juil/ BIM 뷰어 전체 (Three.js + React)
PlanningController.php mng/app/Http/Controllers/Juil/ bimViewer() 메서드
web.php mng/routes/ GET /juil/construction-pmis/bim-viewer

7. 향후 계획 (Phase 2+)

Phase 기능 기술
Phase 2 IFC 파일 업로드 + 실제 BIM 모델 렌더링 That Open Engine (구 IFC.js)
Phase 3 요소 속성 패널 고도화, 2D 평면도 모드 Three.js Orthographic Camera
Phase 4 모델 파일 관리, 버전 이력, 협력업체 조회 Laravel API + DB

관련 문서


최종 업데이트: 2026-03-12