본문으로 건너뛰기
3D 아틀리에
판테온 3D 모형

끌어서 회전, 휠·두 손가락으로 확대

이 모형에 대하여

판테온은 로마 시내 로톤다 광장에 있는 고대 로마의 신전이다. 기원전 1세기 마르쿠스 아그리파가 처음 세운 건물이 불탄 뒤, 하드리아누스 황제 시기인 서기 125년 무렵 지금의 모습으로 다시 지어졌다. 정면 프리즈에는 처음 건립자를 기리는 명문 'M·AGRIPPA·L·F·COS·TERTIVM·FECIT'가 남아 있다. 609년 성당으로 봉헌된 덕분에 오늘날까지 거의 온전하게 쓰이고 있다.

건물은 원통형 본당(로툰다), 박공을 얹은 직사각형 포르티코, 그리고 둘을 잇는 중간 블록으로 이루어진다. 로툰다 내부는 지름과 바닥에서 천장 꼭대기까지의 높이가 모두 약 43.3m여서 지름이 같은 구 하나가 들어맞는다. 철근 없는 콘크리트 돔으로는 지금도 세계에서 가장 크며, 꼭대기에는 지름 약 8m의 오쿨루스가 뚫려 있어 유일한 채광창 구실을 한다.

이 모델은 회색 이집트산 화강암 기둥 8개를 앞줄에, 분홍빛 화강암 기둥을 안쪽 두 줄에 4개씩 세운 포르티코 배치, 돔 아래쪽의 계단식 고리, 벽돌 벽에 드러난 하중분산 아치, 세 단의 코니스, 청동 문과 양옆 벽감을 표현했다. 오쿨루스 아래로는 반구형 안쪽 천장과 격자 천장(코퍼)이 내려다보인다.

만든 방법

로툰다 벽, 코니스, 돔의 계단식 고리와 납판 돔, 내부 반구 천장은 모두 단면 프로필을 Z 축으로 돌린 회전체(lathe)로 만들었다. 납판 돔은 계단 고리 끝과 오쿨루스 테두리를 지나는 구면 호로 계산해 실제처럼 낮고 넓은 실루엣을 냈다. 코린트식 기둥은 엔타시스를 준 회전체 기둥 몸통에 바깥으로 말린 아칸서스 잎 16장과 볼류트를 bmesh 로 붙인 뒤, 회색·분홍 두 종류의 메시를 16개 위치에 공유 복제했다. 엔타블러처와 박공은 상자와 삼각 단면 돌출로 쌓고, 지붕 리브·모딜리온·벽돌 아치·내부 코퍼는 수학적 배열로 배치했다. 벽돌의 질감, 기둥머리 조각의 세부, 박공의 옛 청동 장식 흔적과 주변 광장은 생략했다.

build.py

이 파일 하나를 Blender 에서 실행하면 위의 모형이 만들어집니다. 334줄.

# 판테온 (로마, 서기 125년경) — Blender Python 코드로만 만든 모델.
# 원형 본당(로툰다)과 얕은 돔은 단면 프로필을 Z 축으로 돌린 회전체(lathe)로 만들고,
# 돔 아래쪽의 계단식 고리, 지름 약 8m 의 오쿨루스, 안쪽 반구 천장과 격자 천장(코퍼)까지 넣었다.
# 정면 포르티코는 화강암 기둥 16개(앞줄 8개 + 안쪽 두 줄 4개씩)를 회전체+아칸서스 잎 배열로 만든
# 기둥 두 종류의 메시 공유 복제로 세우고, 엔타블러처·박공(페디먼트)·납 지붕은 상자와 돌출 단면으로 쌓았다.
# 실제 치수(내부 지름·높이 43.3m, 포르티코 폭 약 33m)를 기준으로 했고, 벽돌 아치 등은 단순화했다.
from atelier import *
SHOT = {"azimuth": -58, "elevation": 13, "lens": 45, "light": "golden", "background": "#27303b"}
# ── 재질 ──────────────────────────────────────────────
M_BRICK = mat('Roman_Brick_Stucco', '#877565', 0.85)
M_ARCH = mat('Relieving_Arch_Brick', '#665243', 0.9)
M_MARBLE = mat('Weathered_Marble', '#d0c6b2', 0.55)
M_TRAV = mat('Travertine_Steps', '#c9bc9f', 0.75)
M_GRAN_G = mat('Egyptian_Grey_Granite', '#86827d', 0.35)
M_GRAN_P = mat('Pink_Aswan_Granite', '#a07f74', 0.35)
M_LEAD = mat('Lead_Dome_Sheet', '#8a8c8a', 0.6, 0.1)
M_STUCCO = mat('Dome_Step_Rings', '#b4ada1', 0.8)
M_ROOF = mat('Portico_Roof_Lead', '#5c6265', 0.55, 0.3)
M_BRONZE = mat('Bronze_Doors', '#5b4630', 0.4, 0.85)
M_INSCR = mat('Inscription_Bronze', '#4a3a28', 0.45, 0.7)
M_INNER = mat('Interior_Coffer_Concrete', '#bfb5a3', 0.8)
M_NICHE = mat('Niche_Shadow_Stone', '#6b5f52', 0.9)
TAN = math.tan(math.radians(23.0)) # 박공 경사
# ── 로툰다 치수 ───────────────────────────────────────
R_OUT = 28.5 # 바깥 벽 반지름
R_IN = 21.65 # 안쪽 반지름 (지름 43.3m)
DRUM_TOP = 31.0
seg = 144
drum = lathe('Rotunda_Drum_Wall', [(R_IN, 0.0), (29.0, 0.0), (29.0, 1.3), (R_OUT, 1.5), (R_OUT, DRUM_TOP), (27.8, DRUM_TOP)],
material=M_BRICK, seg=seg, shade_smooth=True)
# 바깥 코니스 고리 3단(대리석)
def cornice_ring(name, h, proj=0.75):
p = [(28.3, h - 0.95), (R_OUT + 0.15, h - 0.95), (R_OUT + 0.2, h - 0.7), (R_OUT + proj * 0.6, h - 0.5),
(R_OUT + proj, h - 0.2), (R_OUT + proj, h), (28.3, h)]
return lathe(name, p, material=M_MARBLE, seg=seg, shade_smooth=False)
cornice_ring('Rotunda_Cornice_Lower', 19.3)
cornice_ring('Rotunda_Cornice_Middle', 25.5)
cornice_ring('Rotunda_Cornice_Top', DRUM_TOP + 0.05, 0.85)
# 돔 하부 계단식 고리 (7단)
steps = [(27.8, DRUM_TOP)]
나머지 286줄 펼치기
r, z = 27.8, DRUM_TOP
for i in range(7):
z += 0.9
steps.append((r, z))
r -= 0.9
steps.append((r, z))
lathe('Dome_Step_Rings', steps, material=M_STUCCO, seg=seg, shade_smooth=False)
R_CAP0, Z_CAP0 = r, z # (21.5, 37.3)
# 납판 돔 — 두 점을 지나는 구면 호
R_OC, Z_OC = 4.6, 44.3
c = ((R_OC ** 2 + Z_OC ** 2) - (R_CAP0 ** 2 + Z_CAP0 ** 2)) / (2 * (Z_OC - Z_CAP0))
rho = math.hypot(R_OC, Z_OC - c)
a0 = math.atan2(Z_CAP0 - c, R_CAP0)
a1 = math.atan2(Z_OC - c, R_OC)
cap = []
for i in range(25):
a = a0 + (a1 - a0) * i / 24
cap.append((rho * math.cos(a), c + rho * math.sin(a)))
cap += [(R_OC, Z_OC + 0.25)]
lathe('Dome_Lead_Shell', cap, material=M_LEAD, seg=seg)
# 오쿨루스 청동 테
torus('Oculus_Bronze_Rim', R=R_OC - 0.15, r=0.28, loc=(0, 0, Z_OC + 0.25), material=M_BRONZE, seg=64, rseg=10)
# 내부: 원통 벽 + 반구 천장
inner = [(R_OC + 0.1, Z_OC + 0.25), (4.1, Z_OC + 0.25), (4.1, 42.9)]
for i in range(1, 31):
a = math.radians(79.1 - 79.1 * i / 30)
inner.append((R_IN * math.cos(a), R_IN + R_IN * math.sin(a)))
inner += [(R_IN, 0.3)]
lathe('Rotunda_Interior_Surface', inner, material=M_INNER, seg=seg)
cylinder('Rotunda_Floor', r=R_IN + 0.05, h=0.3, material=M_MARBLE, seg=seg)
# 내부 격자 천장(코퍼) — 5단 × 28열, 오쿨루스로 내려다보인다
coffer = box('Coffer_Rib', size=(0.5, 2.2, 0.35), material=M_INNER)
rows = [(25, 2.9), (37, 2.6), (49, 2.3), (61, 1.9), (71, 1.5)]
cof = []
for k, (deg, ln) in enumerate(rows):
el = math.radians(deg)
for j in range(28):
az = 2 * math.pi * (j + 0.5) / 28
rr = (R_IN - 0.15) * math.cos(el)
zz = R_IN + (R_IN - 0.15) * math.sin(el)
o = duplicate(coffer, loc=(rr * math.cos(az), rr * math.sin(az), zz),
rot=(0, math.pi / 2 - el, az), scale=(1, ln / 2.2, 1), name=f'Coffer_{k}_{j}')
cof.append(o)
bpy.data.objects.remove(coffer, do_unlink=True)
# ── 중간 블록(아반코르포) ─────────────────────────────
BX = 17.4
B_FRONT = -33.5
B_BACK = -21.0
B_TOP = 25.5
box('Intermediate_Block', size=(2 * BX, B_BACK - B_FRONT, B_TOP), loc=(0, (B_FRONT + B_BACK) / 2, 0), material=M_BRICK, base=True)
# 블록 코니스(대리석) 두 단
for nm, h in (('Block_Cornice_Lower', 19.3), ('Block_Cornice_Upper', B_TOP + 0.05)):
box(nm, size=(2 * BX + 1.4, B_BACK - B_FRONT + 0.7, 0.95), loc=(0, (B_FRONT + B_BACK) / 2 - 0.35, h - 0.95), material=M_MARBLE, base=True)
# 블록 박공
apex_b = B_TOP + 0.05 + (BX + 0.7) * TAN
BPW = BX + 0.7
extrude_profile('Block_Pediment', [(-BPW, B_TOP), (BPW, B_TOP), (0, B_TOP + BPW * TAN)], depth=8.0,
loc=(0, B_FRONT + 4.0, 0.0), material=M_BRICK)
def gable_roof(prefix, half_w, base_z, y0, y1, thick=0.6):
"""경사 지붕 납판 두 장 + 앞쪽 경사 코니스(대리석). 처마선은 (±half_w, base_z)."""
a = math.radians(23.0)
L = half_w / math.cos(a) + 0.4
mz = base_z + half_w * TAN / 2
out = []
for s in (-1, 1):
side = 'L' if s < 0 else 'R'
for nm, off, size, ya, yb, m in (('Roof', thick / 2, thick, y0, y1, M_ROOF),
('Raking_Cornice', thick / 2 - 0.3, thick + 0.5, y0 - 0.15, y0 + 1.0, M_MARBLE)):
cx = s * (half_w / 2 + math.sin(a) * off)
cz = mz + math.cos(a) * off
out.append(box(f'{prefix}_{nm}_{side}', size=(L if nm == 'Roof' else L - 0.3, yb - ya, size),
loc=(cx, (ya + yb) / 2, cz), rot=(0, s * a, 0), material=m))
# 지붕 덮개 기와 이음(리브)
off = thick + 0.06
cx = s * (half_w / 2 + math.sin(a) * off)
cz = mz + math.cos(a) * off
n = int((y1 - y0 - 1.2) / 0.9)
rib = box(f'{prefix}_Roof_Rib_{side}_0', size=(L - 0.2, 0.2, 0.16), loc=(cx, y0 + 1.2, cz), rot=(0, s * a, 0), material=M_ROOF)
for i in range(1, n):
duplicate(rib, loc=(cx, y0 + 1.2 + i * 0.9, cz), name=f'{prefix}_Roof_Rib_{side}_{i}')
return out
gable_roof('Block', BX + 0.7, B_TOP + 0.05, B_FRONT - 0.35, B_FRONT + 6.5)
# ── 포르티코 ─────────────────────────────────────────
P_FRONT = -48.9 # 기단 앞면
STYLO = 1.5
COL_H = 14.2
ENT_Z = STYLO + COL_H # 15.7
ROW_Y = (-47.6, -43.1, -38.6)
XS = [(i - 3.5) * 4.45 for i in range(8)]
# 기단 + 계단 (단면을 X 방향으로 돌출)
prof = [(B_FRONT + 0.5, 0), (P_FRONT - 2.0, 0)]
for k in range(5):
y = P_FRONT - 2.0 + 0.4 * k
prof += [(y, 0.3 * (k + 1)), (y + 0.4, 0.3 * (k + 1))]
prof[-1] = (P_FRONT, STYLO)
prof += [(B_FRONT + 0.5, STYLO)]
# 마지막 계단 단을 기단 윗면과 맞춘다
extrude_profile('Portico_Podium_Steps', prof, depth=2 * 17.0, axis='X',
loc=(0, 0, 0), material=M_TRAV)
# 기둥 원형 만들기
def make_column(name, shaft_mat):
parts = []
parts.append(box(name + '_Plinth', size=(1.95, 1.95, 0.3), material=M_MARBLE, base=True))
base_p = [(0, 0.3), (0.93, 0.3), (0.96, 0.4), (0.92, 0.5), (0.82, 0.54), (0.80, 0.6), (0.87, 0.66),
(0.85, 0.74), (0.77, 0.78)]
parts.append(lathe(name + '_Base', base_p + [(0, 0.78)], material=M_MARBLE, seg=32))
sh = []
for i in range(10):
t = i / 9
sh.append((0.76 - 0.12 * t ** 1.6, 0.76 + (12.1 - 0.76) * t))
parts.append(lathe(name + '_Shaft', [(0, 0.76)] + sh + [(0, 12.1)], material=shaft_mat, seg=32))
bell = [(0, 12.05), (0.70, 12.05), (0.71, 12.22), (0.64, 12.25), (0.65, 12.6), (0.70, 13.1),
(0.78, 13.55), (0.86, 13.8), (0, 13.8)]
parts.append(lathe(name + '_Capital_Bell', bell, material=M_MARBLE, seg=32))
parts.append(box(name + '_Abacus', size=(2.0, 2.0, 0.42), loc=(0, 0, 13.78), material=M_MARBLE, base=True))
# 아칸서스 잎: 바깥으로 말린 얇은 판
def leaf(nm, h, w):
bm = bmesh.new()
n = 6
fr, bk = [], []
for i in range(n):
t = i / (n - 1)
ww = w * (1 - 0.55 * t) * (0.7 + 0.3 * math.sin(math.pi * min(1, t * 1.6)))
x = 0.32 * t ** 2
zz = h * t
fr.append((bm.verts.new((x + 0.05, -ww / 2, zz)), bm.verts.new((x + 0.05, ww / 2, zz))))
bk.append((bm.verts.new((x - 0.03, -ww / 2, zz)), bm.verts.new((x - 0.03, ww / 2, zz))))
for i in range(n - 1):
bm.faces.new((fr[i][0], fr[i][1], fr[i + 1][1], fr[i + 1][0]))
bm.faces.new((bk[i][1], bk[i][0], bk[i + 1][0], bk[i + 1][1]))
bm.faces.new((bk[i][0], fr[i][0], fr[i + 1][0], bk[i + 1][0]))
bm.faces.new((fr[i][1], bk[i][1], bk[i + 1][1], fr[i + 1][1]))
bm.faces.new((fr[-1][0], fr[-1][1], bk[-1][1], bk[-1][0]))
bm.faces.new((bk[0][0], bk[0][1], fr[0][1], fr[0][0]))
me = bpy.data.meshes.new(nm)
bm.to_mesh(me)
bm.free()
ob = bpy.data.objects.new(nm, me)
link(ob)
return assign(ob, M_MARBLE)
for ring, (z0, h, w, rr, off) in enumerate(((12.25, 0.75, 0.5, 0.64, 0.0), (12.6, 0.85, 0.48, 0.68, 0.5))):
for j in range(8):
a = 2 * math.pi * (j + off) / 8
lf = leaf(f'{name}_Leaf_{ring}_{j}', h, w)
lf.location = (rr * math.cos(a), rr * math.sin(a), z0)
lf.rotation_euler = (0, 0, a)
parts.append(lf)
for j in range(4): # 모서리 소용돌이(볼류트)
a = math.pi / 4 + j * math.pi / 2
parts.append(sphere(f'{name}_Volute_{j}', r=0.17, loc=(0.92 * math.cos(a), 0.92 * math.sin(a), 13.62),
material=M_MARBLE, seg=12, rings=8, scale=(1, 0.6, 1)))
return join(parts, name)
col_g = make_column('Column_Grey_Granite', M_GRAN_G)
col_p = make_column('Column_Pink_Granite', M_GRAN_P)
col_g.location = (XS[0], ROW_Y[0], STYLO)
for i, x in enumerate(XS[1:], 1):
duplicate(col_g, loc=(x, ROW_Y[0], STYLO), name=f'Column_Front_{i + 1}')
col_g.name = 'Column_Front_1'
first = True
for r_i, y in enumerate(ROW_Y[1:], 2):
for i in (0, 2, 5, 7):
if first:
col_p.location = (XS[i], y, STYLO)
col_p.name = f'Column_Row{r_i}_{i + 1}'
first = False
else:
duplicate(col_p, loc=(XS[i], y, STYLO), name=f'Column_Row{r_i}_{i + 1}')
# 블록 앞면 벽기둥(안타)
for s in (-1, 1):
box(f'Anta_Pilaster_{"L" if s < 0 else "R"}', size=(1.7, 0.5, COL_H), loc=(s * (BX - 1.0), B_FRONT - 0.25, STYLO), material=M_MARBLE, base=True)
# 엔타블러처
EX = 16.55
EF = ROW_Y[0] - 0.8
box('Architrave', size=(2 * EX, B_FRONT - EF, 1.3), loc=(0, (EF + B_FRONT) / 2, ENT_Z), material=M_MARBLE, base=True)
box('Frieze', size=(2 * EX - 0.2, B_FRONT - EF - 0.1, 1.25), loc=(0, (EF + B_FRONT) / 2 + 0.05, ENT_Z + 1.3), material=M_MARBLE, base=True)
box('Cornice_Bed', size=(2 * EX + 0.5, B_FRONT - EF + 0.25, 0.4), loc=(0, (EF + B_FRONT) / 2 - 0.125, ENT_Z + 2.55), material=M_MARBLE, base=True)
CX = EX + 0.6
CF = EF - 0.6
box('Cornice_Corona', size=(2 * CX, B_FRONT - CF, 0.65), loc=(0, (CF + B_FRONT) / 2, ENT_Z + 2.95), material=M_MARBLE, base=True)
ENT_TOP = ENT_Z + 3.6 # 19.3
# 모딜리온(코니스 받침) 배열
mod = box('Modillion', size=(0.28, 0.5, 0.25), material=M_MARBLE, base=True)
k = 0
n_front = 46
for i in range(n_front):
x = -CX + 0.4 + (2 * CX - 0.8) * i / (n_front - 1)
duplicate(mod, loc=(x, EF - 0.25 + 0.0, ENT_Z + 2.7), name=f'Modillion_Front_{i}')
for s in (-1, 1):
n_side = 20
for i in range(n_side):
y = EF + 0.4 + (B_FRONT - EF - 0.8) * i / (n_side - 1)
duplicate(mod, loc=(s * (EX + 0.25), y, ENT_Z + 2.7), rot=(0, 0, math.pi / 2), name=f'Modillion_Side_{s}_{i}')
# 경사 코니스 아래 모딜리온
a23 = math.radians(23.0)
for sd in (-1, 1):
for i in range(18):
t = (i + 0.5) / 18
x = sd * (CX - 0.9) * (1 - t)
zz = ENT_TOP + (0.9 + (CX - 0.9) * t) * TAN - 0.62 - 0.25
duplicate(mod, loc=(x, CF + 0.55, zz), rot=(0, sd * a23, 0), name=f'Modillion_Rake_{sd}_{i}')
bpy.data.objects.remove(mod, do_unlink=True)
# 명문: M·AGRIPPA·L·F·COS·TERTIVM·FECIT
txt = text3d('Inscription_Agrippa', 'M·AGRIPPA·L·F·COS·TERTIVM·FECIT', size=0.95, depth=0.04,
loc=(0, EF + 0.08, ENT_Z + 1.62), rot=(math.pi / 2, 0, 0), material=M_INSCR)
txt.data.space_character = 1.15
# 포르티코 박공 + 지붕
PW = CX
extrude_profile('Portico_Pediment_Tympanum', [(-PW + 0.3, 0), (PW - 0.3, 0), (0, (PW - 0.3) * TAN)],
depth=B_FRONT - CF - 0.8, loc=(0, (CF + 0.8 + B_FRONT) / 2, ENT_TOP - 0.05), material=M_MARBLE)
gable_roof('Portico', PW, ENT_TOP, CF, B_FRONT + 0.3)
# 청동 문과 문틀, 양옆 벽감
box('Door_Frame_Marble', size=(7.6, 0.5, 11.4), loc=(0, B_FRONT - 0.2, STYLO), material=M_MARBLE, base=True)
box('Bronze_Door_Leaves', size=(5.6, 0.4, 9.0), loc=(0, B_FRONT - 0.5, STYLO), material=M_BRONZE, base=True)
box('Door_Grille', size=(5.6, 0.3, 1.8), loc=(0, B_FRONT - 0.48, STYLO + 9.2), material=M_BRONZE, base=True)
box('Door_Leaf_Seam', size=(0.08, 0.06, 9.0), loc=(0, B_FRONT - 0.73, STYLO), material=M_INSCR, base=True)
for s in (-1, 1):
x = s * 11.1
side = 'L' if s < 0 else 'R'
box(f'Apse_Niche_{side}', size=(5.0, 0.1, 8.0), loc=(x, B_FRONT - 0.04, STYLO), material=M_NICHE, base=True)
cylinder(f'Apse_Niche_Arch_{side}', r=2.5, h=0.1, loc=(x, B_FRONT - 0.04, STYLO + 8.0), rot=(math.pi / 2, 0, 0),
material=M_NICHE, seg=32, base=False)
# ── 로툰다 벽의 벽돌 하중분산 아치(블라인드 아치) ──────
def arch_mesh(name, rad, tube, ang=math.pi):
bm = bmesh.new()
n, m = 20, 8
rings = []
for i in range(n + 1):
t = (math.pi - ang) / 2 + ang * i / n
cx, cz = rad * math.cos(t), rad * math.sin(t)
ring = []
for j in range(m):
b = 2 * math.pi * j / m
rr = rad + tube * math.cos(b)
ring.append(bm.verts.new((tube * math.sin(b), rr * math.cos(t), rr * math.sin(t))))
rings.append(ring)
for i in range(n):
for j in range(m):
bm.faces.new((rings[i][j], rings[i + 1][j], rings[i + 1][(j + 1) % m], rings[i][(j + 1) % m]))
me = bpy.data.meshes.new(name)
bm.to_mesh(me)
bm.free()
ob = bpy.data.objects.new(name, me)
link(ob)
smooth(ob)
return assign(ob, M_ARCH)
for zone, (zc, rad, count, ang) in enumerate(((9.0, 3.6, 18, math.pi), (21.4, 2.2, 28, math.pi * 0.8), (27.4, 2.0, 32, math.pi * 0.8))):
proto = arch_mesh(f'Relieving_Arch_Z{zone}', rad, 0.22, ang)
used = False
for j in range(count):
az = 2 * math.pi * (j + 0.5) / count
x, y = (R_OUT - 0.08) * math.cos(az), (R_OUT - 0.08) * math.sin(az)
if y < 0 and abs(x) < BX + rad + 0.6 and zc < apex_b: # 중간 블록과 겹치는 곳은 건너뜀
continue
if not used:
proto.location, proto.rotation_euler, used = (x, y, zc), (0, 0, az), True
else:
duplicate(proto, loc=(x, y, zc), rot=(0, 0, az), name=f'Relieving_Arch_Z{zone}_{j}')
# 중간 블록 옆면 벽감
for s in (-1, 1):
side = 'L' if s < 0 else 'R'
for k, yc in enumerate((-30.0, -25.2)):
box(f'Block_Side_Niche_{side}{k}', size=(0.1, 3.2, 6.0), loc=(s * (BX + 0.04), yc, 6.0), material=M_NICHE, base=True)
cylinder(f'Block_Side_Niche_Arch_{side}{k}', r=1.6, h=0.1, loc=(s * (BX + 0.04), yc, 12.0), rot=(0, math.pi / 2, 0),
material=M_NICHE, seg=24, base=False)
box(f'Block_Side_Niche_Sill_{side}{k}', size=(0.3, 3.8, 0.3), loc=(s * (BX + 0.1), yc, 5.7), material=M_MARBLE, base=True)

다른 모형