티스토리 뷰
three.js 곡선교 모델링: 곡선 반경 R과 편경사로 휘는 교량 [교량 BIM 바이브코딩 8편]
마구자바 2026. 10. 9. 17:54
7편까지 만든 교량은 모두 일직선이었다. 하지만 실제 도로는 산을 돌고 강을 건너며 휘어진다. 인터체인지 연결로, 산악 도로, 도심 고가도로에서는 교량이 도로를 따라 곡선으로 놓이는 경우가 많다.
8편에서는 도로 중심선(평면 선형)을 따라 휘는 곡선교 모델을 만든다. 곡선 반경 R로 교량 위 모든 점의 좌표를 계산하고, 단면을 곡선을 따라 쓸어서(sweep) 바닥판과 거더를 만들고, 편경사만큼 바닥판을 기울인다. 반경 슬라이더 하나로 직선교부터 급곡선교까지 바꿀 수 있다.
이번 편에서 만들 것
▲ 실제로 동작하는 데모. 곡선 반경과 편경사를 바꾸면 교량 전체가 다시 만들어진다. 부재를 클릭하면 속성이 나온다. (드래그는 회전)
○ 반경을 줄이면 교량이 더 크게 휘고, 바깥쪽 거더가 안쪽 거더보다 길어진다. 아래 표의 G1·G4 길이를 보면 R = 150 m에서 한 경간에 2 m나 차이가 난다.
○ 바닥판 보이기를 끄면 곡선 거더와 반경 방향으로 놓인 가로보·교각이 보인다. 편경사를 올리면 거더 높이가 위치마다 달라지고 코핑이 기운다.
○ 맨 아래 평면도는 three.js 모델을 만드는 데 쓴 같은 좌표 함수로 그렸다. 모델과 도면이 같은 계산에서 나온다.
평면 선형: 직선 · 완화곡선 · 원곡선
○ 도로 중심선의 평면 모양을 평면 선형이라고 한다. 직선에서 바로 원으로 꺾이면 핸들을 갑자기 돌려야 하므로, 그 사이에 곡률이 서서히 변하는 완화곡선(주로 클로소이드)을 넣는다.

| 선형 요소 | 곡률 (1/R) | 특징 | 이번 편 |
| 직선 | 0 | 반경 ∞ | 직선교 (1~7편) |
| 완화곡선 (클로소이드) | 0 → 1/R 로 선형 증가 | 곡선 길이에 비례해 곡률 변화 | 다루지 않음 |
| 원곡선 | 1/R 일정 | 반경 R 하나로 형상 결정 | 데모 교량 |
○ 교량 구간 전체가 원곡선 위에 있으면 계산이 가장 간단하다. 반경 R과 교량 중심에서 잰 곡선 길이 s만 알면 모든 점의 좌표가 나온다. 이번 편은 이 경우를 다룬다.
1단계: 원곡선 위의 좌표 계산
○ 교량 중심을 원점으로 두고, 곡선 길이 s만큼 간 점의 중심각은 θ = s / R이다. 원의 중심에서 R만큼 떨어진 점이므로 좌표는 삼각함수로 바로 계산된다.

const R = 150; // 곡선 반경 (m)
function align(s) { // s: 교량 중심에서 잰 곡선 길이
const th = s / R; // 중심각 (rad)
return {
x: R * Math.sin(th), // 교축 방향
z: R * (1 - Math.cos(th)), // 직각 방향 (곡선 중심 쪽이 +)
th, // 이 점에서의 접선 방향 각도
nx: Math.sin(th), nz: -Math.cos(th), // 곡선 바깥쪽 단위벡터 n
};
}
// 중심선에서 바깥쪽으로 u만큼 떨어진 점 (거더 위치 등)
const p = align(15), u = 5;
const gx = p.x + p.nx * u, gz = p.z + p.nz * u;
| 기호 | 식 | 의미 |
| θ | s / R | 중심각 (라디안). 곡선 길이 ÷ 반경 |
| x | R · sin θ | 교량 중심에서 교축 방향 거리 |
| z | R · (1 − cos θ) | 접선에서 곡선 중심 쪽으로 벗어난 거리 |
| n | (sin θ, −cos θ) | 곡선 바깥쪽 단위벡터 (반경 방향) |
| P + n · u | - | 중심선에서 u만큼 떨어진 점. 거더·방호벽 위치 |
○ 데모 교량(R = 150 m, 3경간 × 30 m)의 지점 좌표를 위 식으로 계산하면 다음과 같다. 교량 중심이 원점이다.
| 지점 | 곡선 길이 s (m) | 중심각 θ | x (m) | z (m) |
| A1 | -45 | -17.189° | -44.328 | 6.700 |
| P1 | -15 | -5.730° | -14.975 | 0.749 |
| P2 | +15 | +5.730° | +14.975 | 0.749 |
| A2 | +45 | +17.189° | +44.328 | 6.700 |
○ A1~A2 직선 거리(현)는 88.656 m지만 곡선 길이는 90 m다. 곡선교에서 경간 길이는 중심선을 따라 잰 곡선 길이로 정하는 것이 일반적이다.
2단계: 단면을 곡선을 따라 쓸기 (sweep)
○ 직선교에서는 BoxGeometry나 ExtrudeGeometry로 부재를 만들었다. 곡선교에서는 단면을 정하고, 선형을 따라 조금씩 옮기면서 꼭짓점을 이어 형상을 만든다. 이것을 sweep(스윕)이라고 한다.
// profile: 단면 꼭짓점 [u, v] 배열 (u: 바깥쪽 거리, v: 높이)
function sweep(profile, s0, s1, { u0 = 0, y0 = 0 } = {}) {
const seg = Math.ceil((s1 - s0) / 1.5); // 1.5 m마다 단면 하나
const np = profile.length, pos = [], idx = [];
for (let i = 0; i <= seg; i++) {
const a = align(s0 + (s1 - s0) * i / seg); // ① 선형 위의 점
for (const [u, v] of profile) {
const uu = u0 + u; // ② 중심선에서 바깥쪽 거리
pos.push(a.x + a.nx * uu, y0 + v, a.z + a.nz * uu);
}
}
for (let i = 0; i < seg; i++) for (let j = 0; j < np; j++) { // ③ 이웃 단면끼리 사각형으로 연결
const a = i * np + j, b = i * np + (j + 1) % np;
idx.push(a, a + np, b, b, a + np, b + np);
}
// ④ 양 끝 단면 막기 (I형처럼 오목한 단면도 OK)
const tri = THREE.ShapeUtils.triangulateShape(profile.map(([u, v]) => new THREE.Vector2(u, v)), []);
for (const [a, b, c] of tri) { idx.push(a, b, c); const o = seg * np; idx.push(o + a, o + c, o + b); }
const g = new THREE.BufferGeometry();
g.setAttribute('position', new THREE.Float32BufferAttribute(pos, 3));
g.setIndex(idx);
g.computeVertexNormals();
return g;
}
| 순서 | 하는 일 | 포인트 |
| ① | 곡선 길이 s를 일정 간격으로 나눠 선형 위의 점 계산 | 간격이 짧을수록 매끈 (데모 1.5 m) |
| ② | 단면 꼭짓점을 반경 방향 n으로 u만큼 이동 | 단면이 항상 곡선에 직각 |
| ③ | 앞뒤 단면의 같은 꼭짓점끼리 삼각형 2개로 연결 | 옆면 생성 |
| ④ | 시작·끝 단면을 삼각형으로 채움 | ShapeUtils.triangulateShape |
○ 바닥판은 직사각형 단면, 거더는 5편의 I형 단면, 방호벽은 작은 직사각형 단면을 같은 함수에 넣는다. 단면만 바꾸면 어떤 부재든 곡선을 따라간다.
ExtrudeGeometry의 extrudePath를 쓰지 않은 이유
○ three.js의 ExtrudeGeometry에도 곡선 경로(extrudePath)를 따라 단면을 밀어내는 옵션이 있다. 하지만 단면 방향을 곡선의 Frenet 프레임으로 정하기 때문에 단면이 비틀리거나 뒤집히는 경우가 있고, 편경사처럼 단면을 원하는 각도로 기울이기가 어렵다. 직접 만든 sweep은 단면의 위쪽(Y)과 바깥쪽(n)을 명확하게 정할 수 있다.
3단계: 편경사 적용하기
○ 차량이 곡선을 돌 때는 바깥쪽으로 원심력을 받는다. 그래서 도로는 바깥쪽을 높게 기울이는데, 이 횡단 경사를 편경사라고 한다. 편경사 6 %면 폭 1 m당 6 cm 높아진다.

const e = 0.06; // 편경사 6 %
const alpha = Math.atan(e); // 기울기 각도
// 바닥판: 단면을 중심선 기준으로 alpha만큼 회전 (sweep 안에서)
const uu = u0 + u * Math.cos(alpha) - v * Math.sin(alpha);
const vv = y0 + u * Math.sin(alpha) + v * Math.cos(alpha);
// 거더: 수직으로 세우고, 위치 u에 따라 높이만 바꾼다
const yGirder = P.H + u * e; // 바깥쪽(u > 0) 거더일수록 높다
| 부재 | 편경사 반영 방법 | 데모 (e = 6 %, B = 12 m) |
| 바닥판 · 방호벽 | 단면 전체를 중심선 기준으로 회전 | 좌우 높이차 0.72 m |
| 거더 | 수직 유지, 위치 u에 따라 높이 u·e | G1 −0.30 m / G4 +0.30 m |
| 교각 코핑 | 바닥판과 같은 기울기로 회전 | 받침 높이를 같게 유지 |
| 받침 | 거더 아래, 높이 u·e 반영 | 16개 모두 다른 높이 |
| 교각 기둥 | 수직, 중심선 높이 기준 | 변화 없음 |
○ 도로 설계에서 편경사는 설계속도와 곡선 반경에 따라 정하며, 최대값은 도로 조건에 따라 보통 6~8 % 정도다. 데모 슬라이더는 0~8 %로 두었다. 실제 값은 해당 도로의 설계 기준을 따른다.
4단계: 지점과 가로보는 반경 방향으로
○ 교각·교대·가로보·받침처럼 BoxGeometry로 만드는 부재는 위치만 옮기면 곡선과 어긋난다. 그 지점의 중심각 θ만큼 회전시켜서 반경 방향(곡선에 직각)으로 맞춘다.
const p = align(sPier); // 교각 위치의 선형 정보
const col = new THREE.Mesh(new THREE.BoxGeometry(1.8, 6.65, 1.8), conc);
col.position.set(p.x, 6.65 / 2, p.z);
col.rotation.y = -p.th; // ★ 접선 방향으로 회전 (three.js는 y축 회전이 반시계)
○ 곡선교에서는 반경이 큰 바깥쪽이 더 길다. 같은 경간이라도 거더마다 길이가 다르므로, 7편처럼 userData에 길이를 넣을 때 거더별로 계산해야 한다.
| 거더 | 중심선에서 거리 u | 거더 반경 R + u | 경간 길이 L·(R+u)/R | 중심선 대비 |
| G1 (안쪽) | −5.000 m | 145.000 m | 29.000 m | −1.000 m |
| G2 | −1.667 m | 148.333 m | 29.667 m | −0.333 m |
| G3 | +1.667 m | 151.667 m | 30.333 m | +0.333 m |
| G4 (바깥쪽) | +5.000 m | 155.000 m | 31.000 m | +1.000 m |
○ R = 150 m, 경간 30 m 기준이다. 물량도 거더별 길이로 계산되므로 7편의 물량 집계를 그대로 쓰면 곡선교 물량이 자동으로 나온다.
곡선 거더와 직선 거더
○ 곡선교라도 반경이 크면 거더는 직선으로 만들고 경간마다 꺾어서 놓기도 한다. 이때 판단 기준이 되는 값이 중앙종거다. 경간 양 끝을 이은 직선(현)과 원호가 가장 많이 벌어지는 거리로, M = R·(1 − cos(L / 2R)) ≈ L² / 8R 이다.
▲ 경간이 길고 반경이 작을수록 현과 원호의 차이가 커진다.
| 곡선 반경 R | 경간 1개 중심각 | 중앙종거 (L = 30 m) | 거더 형식 (경향) |
| 100 m | 17.19° | 1.123 m | 곡선 거더 |
| 150 m | 11.46° | 0.749 m | 곡선 거더 |
| 300 m | 5.73° | 0.375 m | 곡선 거더 또는 직선 거더 검토 |
| 500 m | 3.44° | 0.225 m | 직선 거더 + 바닥판 캔틸레버 조정 |
| 1000 m | 1.72° | 0.112 m | 직선 거더 |
○ 직선 거더를 쓰면 거더와 도로 중심선이 경간 중앙에서 중앙종거만큼 어긋나므로, 그만큼 바닥판 캔틸레버 길이를 조정해야 한다. 어느 쪽을 택할지는 제작성·경제성·미관을 함께 보고 정한다. 위 표의 형식은 일반적인 경향이며 정해진 기준은 아니다.
전체 코드
○ 메모장에 붙여넣고 bridge8.html로 저장한 뒤 더블클릭하면 실행된다. 맨 위 P의 R과 e를 바꾸면 곡선과 편경사가 바뀌고, F12 콘솔에는 거더별 반경과 경간 길이가 출력된다. (데모의 I형 단면·가로보·받침·클릭 선택은 빠진 간단한 버전)
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>three.js 곡선교 모델 - 곡선 반경과 편경사</title>
<style>body { margin: 0; overflow: hidden; }</style>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.186.1/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.186.1/examples/jsm/"
}
}
</script>
</head>
<body>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 1. 장면 기본 설정
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xdfe8f0);
const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 2000);
camera.position.set(10, 50, -70);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
scene.add(new THREE.HemisphereLight(0xffffff, 0x6b7f63, 0.9));
const sun = new THREE.DirectionalLight(0xffffff, 2.0);
sun.position.set(40, 80, -45);
scene.add(sun);
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 5, 0);
controls.update();
// 2. 교량 제원 (단위 m)
const P = { nSpan: 3, L: 30, B: 12, n: 4, H: 8, R: 150, e: 0.06 }; // R: 곡선 반경, e: 편경사
const h = P.L / 20; // 거더 높이
const s = (P.B - 2) / (P.n - 1); // 거더 간격
const Lt = P.nSpan * P.L; // 교량 길이 (곡선 길이)
const alpha = Math.atan(P.e); // 편경사 각도
const zc = P.R * (1 - Math.cos(Lt / 2 / P.R)) / 2; // 교량을 화면 가운데로
// 3. 평면 선형: 곡선 길이 sv 지점의 좌표와 바깥쪽 방향
function align(sv) {
const th = sv / P.R; // 중심각 θ = s / R
return {
x: P.R * Math.sin(th),
z: P.R * (1 - Math.cos(th)) - zc,
th,
nx: Math.sin(th), nz: -Math.cos(th), // 곡선 바깥쪽 단위벡터
};
}
// 4. 단면(직사각형)을 선형을 따라 쓸어서 형상 만들기
// u: 중심선에서 바깥쪽 거리, v: 높이, tilt: 편경사만큼 기울이기
function sweep(u1, u2, v1, v2, s0, s1, { u0 = 0, y0 = 0, tilt = false } = {}) {
const prof = [[u1, v1], [u2, v1], [u2, v2], [u1, v2]];
const seg = Math.ceil(Math.abs(s1 - s0) / 1.5);
const a = tilt ? alpha : 0, pos = [], idx = [];
for (let i = 0; i <= seg; i++) {
const p = align(s0 + (s1 - s0) * i / seg);
for (const [u, v] of prof) {
const uu = u0 + u * Math.cos(a) - v * Math.sin(a);
const vv = y0 + u * Math.sin(a) + v * Math.cos(a);
pos.push(p.x + p.nx * uu, vv, p.z + p.nz * uu);
}
}
for (let i = 0; i < seg; i++) for (let j = 0; j < 4; j++) {
const k = i * 4 + j, k2 = i * 4 + (j + 1) % 4;
idx.push(k, k + 4, k2, k2, k + 4, k2 + 4);
}
idx.push(0, 1, 2, 0, 2, 3); // 시작 단면
const o = seg * 4; idx.push(o, o + 2, o + 1, o, o + 3, o + 2); // 끝 단면
const g = new THREE.BufferGeometry();
g.setAttribute('position', new THREE.Float32BufferAttribute(pos, 3));
g.setIndex(idx);
g.computeVertexNormals();
return g;
}
const conc = new THREE.MeshStandardMaterial({ color: 0xb8bcc2, side: THREE.DoubleSide });
const steel = new THREE.MeshStandardMaterial({ color: 0x3f6e9e, metalness: 0.3, side: THREE.DoubleSide });
const add = (geo, mat) => { const m = new THREE.Mesh(geo, mat); scene.add(m); return m; };
const s0 = -Lt / 2;
// 5. 바닥판 (편경사만큼 기울어짐)
add(sweep(-P.B / 2, P.B / 2, 0, 0.25, s0, s0 + Lt, { y0: P.H + h, tilt: true }), conc);
// 6. 거더 - 곡선을 따라 휘고, 편경사만큼 높이가 달라짐
for (let i = 0; i < P.n; i++) {
const u = -(P.B / 2 - 1) + i * s;
const y = P.H + u * P.e; // 바깥쪽 거더일수록 높다
for (let k = 0; k < P.nSpan; k++) {
const a = s0 + k * P.L;
add(sweep(-0.2, 0.2, 0, h, a + 0.05, a + P.L - 0.05, { u0: u, y0: y }), steel);
}
const len = P.L * (P.R + u) / P.R; // 반경이 크면 거더도 길다
console.log(`G${i + 1}: 반경 ${(P.R + u).toFixed(1)} m, 경간 길이 ${len.toFixed(3)} m`);
}
// 7. 교각 - 반경 방향으로 회전
for (let k = 1; k < P.nSpan; k++) {
const p = align(s0 + k * P.L);
const col = add(new THREE.BoxGeometry(1.8, P.H - 1.35, 1.8), conc);
col.position.set(p.x, (P.H - 1.35) / 2, p.z);
col.rotation.y = -p.th; // 교각을 곡선에 직각으로
add(sweep(-(P.B - 1) / 2, (P.B - 1) / 2, -1.2, 0, s0 + k * P.L - 1.2, s0 + k * P.L + 1.2,
{ y0: P.H - 0.15, tilt: true }), conc); // 코핑도 편경사만큼 기울임
}
// 8. 교대
for (const sv of [s0 - 1, s0 + Lt + 1]) {
const p = align(sv);
const ab = add(new THREE.BoxGeometry(2, P.H, P.B + 1), conc);
ab.position.set(p.x, P.H / 2, p.z);
ab.rotation.y = -p.th;
}
// 9. 도로 중심선 (빨간 점선)
const pts = [];
for (let sv = s0 - 20; sv <= s0 + Lt + 20; sv += 1) { const p = align(sv); pts.push(new THREE.Vector3(p.x, P.H + h + 0.3, p.z)); }
const cl = new THREE.Line(new THREE.BufferGeometry().setFromPoints(pts),
new THREE.LineDashedMaterial({ color: 0xff3300, dashSize: 1.5, gapSize: 1 }));
cl.computeLineDistances();
scene.add(cl);
renderer.setAnimationLoop(() => { controls.update(); renderer.render(scene, camera); });
addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
</script>
</body>
</html>
Claude에게 이렇게 요청했다
three.js로 원곡선을 따라 휘는 3경간 곡선교 모델을 만들어줘.
- HTML 파일 하나, three.js 0.186.1 CDN importmap
- 3경간 × 30 m (중심선 곡선 길이), 폭 12 m, 거더 4개, 교각 높이 8 m, 단위 m, Y축 위
- 평면 선형은 반경 R = 150 m 원곡선. 교량 중심에서 잰 곡선 길이 s로 점 좌표를 계산하는 align(s) 함수를 만들 것 (θ = s/R)
- 바닥판·거더는 단면을 선형을 따라 쓸어서(sweep) BufferGeometry로 생성. ExtrudeGeometry의 extrudePath는 쓰지 말 것
- 편경사 e = 6 %, 곡선 바깥쪽이 높게. 바닥판·코핑은 기울이고, 거더는 수직으로 두고 위치에 따라 높이만 다르게
- 교각·교대는 그 지점의 반경 방향으로 회전
- 거더별 반경과 경간 길이를 console에 출력
○ 곡선교는 "곡선 길이 s로 좌표를 계산하는 함수"를 먼저 만들게 하는 것이 핵심이다. 이 함수 없이 "곡선교 만들어줘"라고만 하면 AI가 원을 대충 나눈 직선 토막을 이어 붙이거나, 반경과 경간 길이가 맞지 않는 모델을 만드는 경우가 많다.
자주 겪는 문제
| 증상 | 원인 | 해결 |
| 교량이 반대 방향으로 휨 | 곡선 중심 방향(좌·우) 부호 혼동 | z, n 부호를 함께 뒤집기 (좌곡선 / 우곡선) |
| 교각이 곡선과 어긋나게 놓임 | 위치만 옮기고 회전 안 함 | rotation.y = -θ |
| 바닥판 면이 일부 안 보임 | sweep 삼각형 방향(앞·뒷면)이 뒤집힘 | side: THREE.DoubleSide 또는 인덱스 순서 바꾸기 |
| 단면이 비틀려 보임 | extrudePath의 Frenet 프레임 | 직접 sweep, 단면 방향을 Y축·반경 방향으로 고정 |
| 편경사를 주니 거더가 바닥판을 뚫음 | 거더도 같이 회전하거나 높이 미반영 | 거더는 수직, 높이만 u·e 만큼 |
| 반경을 크게 하면 모델이 사라짐 | R = ∞를 그대로 계산 (0/0) | 직선은 아주 큰 R(1e9)로 처리 |
정리
○ 원곡선 위의 점은 θ = s / R, x = R·sin θ, z = R·(1 − cos θ)로 계산한다.
○ 중심선에서 바깥쪽 단위벡터 n으로 u만큼 옮기면 거더·방호벽 위치가 나온다.
○ 단면을 선형을 따라 sweep하면 어떤 단면이든 곡선 부재가 된다.
○ 편경사는 바닥판·코핑을 회전시키고, 거더는 수직으로 두고 높이만 바꾼다.
○ 곡선교에서는 바깥쪽 거더가 더 길다. 길이와 물량은 거더별로 계산한다.
다음 편 예고
곡선교 코드는 지금까지 중 가장 길고, AI가 부호나 회전 방향을 틀리기도 쉽다. 9편에서는 바이브코딩 디버깅을 다룬다. 화면이 하얗게 나오거나, 모델이 엉뚱하게 뒤집히거나, 콘솔에 빨간 오류가 뜰 때 AI에게 무엇을 어떻게 알려줘야 빨리 고쳐지는지 실제 사례로 정리한다.
관련 글
→ [교량 BIM 바이브코딩 1편] three.js 입문: Claude 바이브코딩으로 3D 교량 만들기
→ [교량 BIM 바이브코딩 2편] three.js 기초: 장면·카메라·렌더러
→ [교량 BIM 바이브코딩 3편] OrbitControls·조명·그림자
→ [교량 BIM 바이브코딩 4편] AI 코딩 프롬프트 작성법: Claude에게 3D 모델 정확히 요청하기
→ [교량 BIM 바이브코딩 5편] three.js ExtrudeGeometry: I형 거더 단면 3D 모델링
→ [교량 BIM 바이브코딩 6편] three.js 파라메트릭 모델링: 슬라이더로 바뀌는 교량
→ [교량 BIM 바이브코딩 7편] three.js 교량 BIM 모델: 클릭하면 속성이 나오는 부재 조립
'엔지니어링 코딩 > three.js·AI 코딩' 카테고리의 다른 글
| 바이브코딩 디버깅: AI가 만든 three.js 코드가 틀렸을 때 고치는 법 [교량 BIM 바이브코딩 9편] (0) | 2026.10.11 |
|---|---|
| three.js 교량 BIM 모델: 거더·바닥판·교각 조립하고 클릭해서 속성 보기 [교량 BIM 바이브코딩 7편] (0) | 2026.10.09 |
| three.js 파라메트릭 모델링: 슬라이더로 바뀌는 교량 [교량 BIM 바이브코딩 6편] (0) | 2026.10.09 |
| AI 코딩 프롬프트 작성법: Claude에게 3D 모델 정확히 요청하기 [교량 BIM 바이브코딩 4편] (0) | 2026.10.07 |
| three.js ExtrudeGeometry: I형 거더 단면 3D 모델링 [교량 BIM 바이브코딩 5편] (0) | 2026.10.07 |
