302 lines
31 KiB
HTML
302 lines
31 KiB
HTML
<!doctype html>
|
||
<html lang="ko" data-theme="wood">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="theme-color" content="#e6dfcf">
|
||
<meta name="description" content="우리집 멍멍이, 할머니 집 유리문 너머로">
|
||
<meta name="google-adsense-account" content="ca-pub-3181563320128730">
|
||
<title>Behind Glass · 할머니집 창가</title>
|
||
<link rel="preload" href="/fonts/BMKkubulim.woff" as="font" type="font/woff" crossorigin>
|
||
<script>
|
||
if (document.fonts) {
|
||
document.documentElement.dataset.fontLoading = '';
|
||
const showPage = () => { delete document.documentElement.dataset.fontLoading; };
|
||
const fallback = setTimeout(showPage, 3000);
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
document.fonts.load('16px "BM Kkubulim"').then(showPage, showPage).finally(() => clearTimeout(fallback));
|
||
}, { once: true });
|
||
}
|
||
</script>
|
||
<script type="module" crossorigin src="/assets/index-DoRt0pZc.js"></script>
|
||
<link rel="stylesheet" crossorigin href="/assets/index-D7twsXVD.css">
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
<input hidden id="image" type="file" accept="image/jpeg,image/png,image/webp" aria-label="멍멍이 사진 선택">
|
||
<main id="experience" data-flow="entry">
|
||
<section id="pet-stage" class="stage" aria-label="유리문 너머의 멍멍이">
|
||
<div id="canvas-wrap" tabindex="0" aria-label="사진 미리보기. 드래그하거나 방향키로 시점을 움직이세요" aria-describedby="scene-instructions"><div id="empty" hidden></div><span id="door-handle" aria-hidden="true" hidden>↔</span></div>
|
||
<div id="guide-scene-target" aria-hidden="true"></div>
|
||
<div id="door-controls" hidden><button id="door-toggle" type="button">문 열기</button></div>
|
||
<button id="gyro-recenter" type="button" hidden aria-describedby="gyro-status">중앙 맞추기</button>
|
||
<button id="photo-adjust-open" type="button" hidden>사진 위치 조절</button>
|
||
<section id="photo-adjust-panel" hidden aria-label="사진 위치 조절">
|
||
<div class="photo-adjust-heading"><strong>사진 위치 조절 중</strong><button id="photo-adjust-done" type="button">완료</button></div>
|
||
<label for="photo-adjust-zoom">확대 <output id="photo-adjust-value">1.00</output><input id="photo-adjust-zoom" type="range" min="1" max="3" step="0.05" value="1"></label>
|
||
<p id="photo-adjust-help">사진을 확대한 뒤 드래그해서 위치를 맞춰주세요</p>
|
||
<button id="photo-adjust-reset" type="button">처음 구도로</button>
|
||
</section>
|
||
<p id="frame-notice" class="frame-notice" role="status" hidden></p>
|
||
<p id="hint" aria-hidden="true"></p>
|
||
</section>
|
||
<div id="comparison-overlay" hidden aria-label="동기화 유리 렌더러 비교">
|
||
<div id="comparison-label-a" class="comparison-label">A · 기존 코드</div>
|
||
<div id="comparison-label-b" class="comparison-label">굴절 보정 B · 맞춤 굴절</div>
|
||
<div id="comparison-metrics"><p id="comparison-timing">첫 프레임을 준비하는 중…</p><p id="comparison-memory"></p><p>GPU 시간·메모리 미측정</p></div>
|
||
</div>
|
||
<header id="scene-toolbar" class="scene-toolbar">
|
||
<a class="scene-brand" href="#app" aria-label="Behind Glass">behind glass</a>
|
||
<button id="scene-menu-open" type="button" hidden aria-haspopup="dialog" aria-controls="scene-menu" aria-expanded="false">메뉴</button>
|
||
<button id="sample-home" type="button" hidden aria-label="첫 화면으로 가기" title="첫 화면으로 가기" aria-haspopup="dialog" aria-controls="home-dialog"><svg viewBox="0 0 24 24" width="23" height="23" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3 10 9-7 9 7M5 9v12h5v-7h4v7h5V9"/></svg></button>
|
||
<div class="scene-actions"><button id="credits-open" type="button" aria-haspopup="dialog" aria-controls="credits-dialog">만든 사람</button></div>
|
||
<div id="scene-motion" class="scene-motion" hidden>
|
||
<button id="gyro" type="button" aria-pressed="false" aria-describedby="gyro-status gyro-privacy"><span id="gyro-label">기울기 연결</span></button>
|
||
|
||
<p id="gyro-notice" role="status" aria-live="polite" hidden></p>
|
||
<p id="gyro-status" class="sr-only" role="status" aria-live="polite">기울기는 꺼져 있어요. 드래그로 둘러볼 수 있어요.</p>
|
||
</div>
|
||
</header>
|
||
<section id="welcome" class="welcome" data-step="name" aria-labelledby="welcome-title">
|
||
<div class="entry-door" aria-hidden="true"><span></span></div>
|
||
<p class="entry-kicker">behind glass</p>
|
||
<h1 id="welcome-title">할머니 집에<br><span id="welcome-name">멍멍이가 왔어요</span></h1>
|
||
<button id="entry-name-edit" class="entry-samples" type="button" hidden>이름 바꾸기</button>
|
||
<div id="entry-name-step" class="entry-step">
|
||
<div class="pet-name-field entry-name-field">
|
||
<label for="entry-pet-name">멍멍이 이름이 뭐예요?</label>
|
||
<input id="entry-pet-name" type="text" autocomplete="off" maxlength="40" spellcheck="false" placeholder="예시 : 장준" aria-describedby="entry-name-help">
|
||
<small id="entry-name-help">비워두어도 괜찮아요 · 이 브라우저에만 저장돼요</small>
|
||
<small id="entry-name-notice" class="name-storage-notice" role="status" hidden>이름은 지금만 적용돼요. 이 브라우저에 저장하지 못했어요.</small>
|
||
</div>
|
||
<button id="entry-name-next" class="primary-upload" type="button">다음</button>
|
||
<button id="entry-name-skip" class="entry-samples" type="button">이름 없이 둘러보기</button>
|
||
</div>
|
||
<div id="entry-photo-step" class="entry-step" hidden>
|
||
<button id="entry-photo-button" class="upload primary-upload" type="button" data-pick-photo>멍멍이 사진 고르기</button>
|
||
<button id="entry-samples" class="entry-samples" type="button">샘플로 먼저 둘러보기</button>
|
||
</div>
|
||
<div id="entry-notices" hidden role="group" aria-label="사진 선택 도움말">
|
||
<button id="notice-prev" type="button" aria-label="이전 안내">‹</button>
|
||
<div class="notice-slides">
|
||
<p id="photo-tip" class="entry-notice-slide" data-active="true">정면 사진이면 더 귀여워요</p>
|
||
<p id="privacy-tip" class="entry-notice-slide" data-active="false" aria-hidden="true">사진은 이 브라우저 안에서만 처리해요</p>
|
||
<p id="paste-tip" class="entry-notice-slide" data-active="false" aria-hidden="true" hidden>복사한 사진도 붙여넣을 수 있어요<br><span>Ctrl+V · ⌘V</span></p>
|
||
</div>
|
||
<button id="notice-next" type="button" aria-label="다음 안내">›</button>
|
||
<span id="notice-position" aria-hidden="true">1 / 2</span>
|
||
<span id="notice-status" class="sr-only" role="status" aria-live="polite" aria-atomic="true"></span>
|
||
</div>
|
||
</section>
|
||
<section id="flow-overlay" class="flow-overlay" hidden aria-labelledby="flow-title" aria-busy="false">
|
||
<div id="journey" class="journey" hidden>
|
||
<div class="journey-scenery" aria-hidden="true"><span class="journey-sun"></span><span class="journey-cloud"></span><span class="journey-hill"></span><span class="journey-tree"></span></div>
|
||
<svg class="journey-house" viewBox="0 0 140 160" aria-hidden="true" focusable="false">
|
||
<path d="M19 65h103v86H19z" fill="#e5d6b8" stroke="#735943" stroke-width="3"/>
|
||
<path d="M9 66 71 18l61 48z" fill="#87775c" stroke="#645640" stroke-width="4" stroke-linejoin="round"/>
|
||
<path d="M105 37V17h12v31" fill="#ae8870" stroke="#735943" stroke-width="3"/>
|
||
<path d="M76 88h30v63H76z" fill="#98734e" stroke="#715239" stroke-width="3"/>
|
||
<path d="M29 82h31v37H29z" fill="#c1d1bd" stroke="#7d684b" stroke-width="4"/>
|
||
<path d="M44 82v37M29 100h31" stroke="#7d684b" stroke-width="3"/>
|
||
<circle cx="99" cy="122" r="2.5" fill="#f3d495"/>
|
||
<path d="M11 152h121" stroke="#867858" stroke-width="5" stroke-linecap="round"/>
|
||
</svg>
|
||
<div class="journey-road" aria-hidden="true"></div>
|
||
<div class="journey-traveler">
|
||
<div class="journey-passenger"><img id="flow-photo" class="flow-photo" alt="선택한 멍멍이 사진" hidden></div>
|
||
</div>
|
||
<div class="journey-car">
|
||
<svg class="journey-car-body" viewBox="0 0 300 180" aria-hidden="true" focusable="false">
|
||
<path d="M48 104 62 55q4-15 20-15h88q13 0 20 15l27 49" fill="#d6e0ce" stroke="#785c42" stroke-width="5" stroke-linejoin="round"/>
|
||
<path d="M170 52h4q5 0 9 10l15 31h-28z" fill="#f3f1df" stroke="#8e9c8d" stroke-width="3"/>
|
||
<path d="M31 101h181l22 9h25q21 0 23 20v22H18v-27q0-20 13-24z" fill="#bc7250" stroke="#78513d" stroke-width="5" stroke-linejoin="round"/>
|
||
<path d="M27 129h245M156 104v42" stroke="#934e38" stroke-width="3"/>
|
||
<path d="M173 115h14" stroke="#f7dbb4" stroke-width="4" stroke-linecap="round"/>
|
||
<path d="M18 146h21m217 0h29" stroke="#e2cbb2" stroke-width="8" stroke-linecap="round"/>
|
||
<rect x="263" y="116" width="14" height="12" rx="3" fill="#ffe5a3"/>
|
||
<rect x="21" y="116" width="7" height="10" rx="2" fill="#e6a092"/>
|
||
<g class="journey-wheel"><circle cx="66" cy="150" r="24" fill="#514b43"/><circle cx="66" cy="150" r="12" fill="#d4cab3"/><path d="M66 141v18m-9-9h18" stroke="#8a806f" stroke-width="3"/></g>
|
||
<g class="journey-wheel"><circle cx="231" cy="150" r="24" fill="#514b43"/><circle cx="231" cy="150" r="12" fill="#d4cab3"/><path d="M231 141v18m-9-9h18" stroke="#8a806f" stroke-width="3"/></g>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
<h2 id="flow-title" role="status" aria-live="polite">멍멍이를 할머니 집으로 데려가는 중…</h2>
|
||
<p id="flow-detail" aria-live="polite"></p>
|
||
<div id="flow-actions" class="flow-actions"><button id="flow-retry" type="button" hidden>다시 시도하기</button><button id="flow-flat" type="button" hidden>평면 사진으로 보기</button><button class="upload" type="button" data-pick-photo>다른 사진 고르기</button><button id="flow-samples" type="button" hidden>다른 샘플 고르기</button></div>
|
||
<p class="arrival-count" data-arrival-count hidden></p>
|
||
</section>
|
||
</main>
|
||
<dialog id="home-dialog" aria-labelledby="home-title" aria-describedby="home-detail">
|
||
<h2 id="home-title">정말 첫 화면으로 갈까요?</h2>
|
||
<p id="home-detail">돌아오려면 사진을 다시 골라야 해요.</p>
|
||
<div class="home-actions"><button id="home-cancel" type="button" autofocus>계속 볼게요</button><button id="home-confirm" type="button">첫 화면으로</button></div>
|
||
</dialog>
|
||
<dialog id="scene-menu" aria-labelledby="scene-menu-title">
|
||
<div class="scene-menu-heading"><h2 id="scene-menu-title">메뉴</h2><button id="scene-menu-close" type="button" autofocus>닫기</button></div>
|
||
<div class="scene-menu-items">
|
||
<button id="export-open" type="button" hidden aria-haspopup="dialog" aria-controls="export-dialog">내려받기</button>
|
||
<button id="scene-photo-change" class="upload" type="button" hidden data-pick-photo>사진 바꾸기</button>
|
||
<button id="settings-open" type="button" hidden aria-haspopup="dialog" aria-controls="settings-dialog">설정</button>
|
||
<button id="scene-zoom-reset" type="button" hidden>전체 보기</button>
|
||
<button id="comparison-exit" type="button" hidden>비교 끝내기</button>
|
||
<button id="scene-guide-replay" type="button" hidden>사용법 다시 보기</button>
|
||
</div>
|
||
</dialog>
|
||
<dialog id="sample-dialog" aria-labelledby="sample-title" aria-describedby="sample-description">
|
||
<div class="sample-heading"><h2 id="sample-title">어떤 멍멍이를 만나볼까요?</h2><button id="sample-close" type="button" aria-label="샘플 사진 선택 닫기">닫기</button></div>
|
||
<p id="sample-description">사진을 고른 뒤 아래 버튼을 눌러주세요</p>
|
||
<div class="sample-choices" role="group" aria-label="샘플 사진">
|
||
<button type="button" data-sample-choice="dog5" aria-pressed="true"><img src="/assets/samples/dog5.jpg" alt="" loading="lazy"><span>누렁이 · 정면 인사</span><small>가장 추천해요</small></button>
|
||
<button type="button" data-sample-choice="dog1" aria-pressed="false"><img src="/assets/samples/dog1.jpg" alt="" loading="lazy"><span>호두 · 저녁 얼굴</span><small>얼굴 사진 추천</small></button>
|
||
<button type="button" data-sample-choice="dog2" aria-pressed="false"><img src="/assets/samples/dog2.jpg" alt="" loading="lazy"><span>두부 · 나무 아래</span></button>
|
||
<button type="button" data-sample-choice="dog3" aria-pressed="false"><img src="/assets/samples/dog3.jpg" alt="" loading="lazy"><span>보리 · 낮잠</span></button>
|
||
<button type="button" data-sample-choice="dog4" aria-pressed="false"><img src="/assets/samples/dog4.jpg" alt="" loading="lazy"><span>콩이 · 저녁 산책</span></button>
|
||
</div>
|
||
<div class="sample-footer"><button id="sample-confirm" type="button">이 사진으로 둘러보기</button></div>
|
||
</dialog>
|
||
<dialog id="render-recovery" role="alertdialog" aria-labelledby="render-recovery-title" aria-describedby="render-recovery-detail">
|
||
<h2 id="render-recovery-title">화면을 잠시 그리지 못했어요</h2>
|
||
<p id="render-recovery-detail">그래픽 연결이 끊겼어요. 브라우저가 복구하면 같은 사진으로 돌아가요.<br>계속 이 화면이면 새로고침해 주세요.</p>
|
||
<button id="render-reload" type="button" autofocus>새로고침</button>
|
||
<p class="control-help">새로고침하면 사진을 다시 선택해야 해요</p>
|
||
</dialog>
|
||
<dialog id="mesh-render-error" role="alertdialog" aria-labelledby="mesh-error-title">
|
||
<h2 id="mesh-error-title">B 유리를 그리지 못했어요</h2>
|
||
<p>사진과 설정은 그대로예요. B를 다시 시도하거나 A 진단 모드를 직접 선택할 수 있어요.</p>
|
||
<button id="mesh-retry" type="button" autofocus>B 다시 시도</button>
|
||
<button id="mesh-use-a" type="button">A 진단 모드로 보기</button>
|
||
</dialog>
|
||
<dialog id="first-guide" aria-hidden="true"></dialog>
|
||
<dialog id="export-dialog" aria-labelledby="export-title">
|
||
<div class="export-heading"><h2 id="export-title">현재 구도 저장</h2><button id="export-close" type="button">닫기</button></div>
|
||
<div class="export-choices"><button id="export-photo" type="button">굴절사진만 저장</button><button id="export-scene" type="button">방까지 함께 저장</button></div>
|
||
<p id="export-status" role="status" aria-live="polite">마음에 드는 현재 구도를 저장해 보세요</p>
|
||
<img id="export-preview" alt="저장 미리보기" hidden>
|
||
<div class="export-actions"><a id="export-download" hidden download>PNG 내려받기</a><button id="export-share" type="button" hidden>공유하기</button></div>
|
||
</dialog>
|
||
<dialog id="credits-dialog" aria-labelledby="credits-title">
|
||
<div class="credits-heading"><h2 id="credits-title">만든 사람</h2><button id="credits-close" type="button" aria-label="만든 사람 닫기">닫기</button></div>
|
||
<dl class="credits-info"><dt>제작</dt><dd>horoli</dd><dt>버그 제보</dt><dd><a href="mailto:sunha321@gmail.com">sunha321@gmail.com</a></dd></dl>
|
||
<a class="credits-licenses" href="/THIRD-PARTY-NOTICES.txt" target="_blank" rel="noopener">리소스·라이선스 안내</a>
|
||
</dialog>
|
||
<dialog id="settings-dialog" aria-labelledby="settings-title">
|
||
<aside aria-label="사진 꾸미기">
|
||
<div class="editor-heading"><h2 id="settings-title">창가 설정</h2><button id="settings-close" type="button" aria-label="설정 닫기">닫기</button></div>
|
||
<p class="arrival-count" data-arrival-count hidden></p>
|
||
<button id="frame-retry" type="button" hidden>나무 창틀 다시 불러오기</button>
|
||
<button id="house-retry" type="button" hidden>집 주변 공간 다시 불러오기</button>
|
||
<div class="pet-name-field settings-name-field">
|
||
<label for="settings-pet-name">멍멍이 이름</label>
|
||
<input id="settings-pet-name" type="text" autocomplete="off" maxlength="40" spellcheck="false" placeholder="예시 : 장준" aria-describedby="settings-name-help">
|
||
<small id="settings-name-help">언제든 바꿀 수 있어요 · 최대 20자</small>
|
||
<small id="settings-name-notice" class="name-storage-notice" role="status" hidden>이름은 지금만 적용돼요. 이 브라우저에 저장하지 못했어요.</small>
|
||
</div>
|
||
<label class="theme-setting" for="theme-select">테마<select id="theme-select"><option value="wood">할머니집 창가</option><option value="booth">통통 포토부스</option></select></label>
|
||
<div id="tool-tabs" role="tablist" aria-label="사진 도구">
|
||
<button id="tab-glass" role="tab" aria-controls="panel-glass" aria-selected="true" data-tool="glass"><span aria-hidden="true">▦</span> 유리</button>
|
||
<button id="tab-photo" role="tab" aria-controls="panel-photo" aria-selected="false" tabindex="-1" data-tool="photo"><span aria-hidden="true">▧</span> 사진</button>
|
||
<button id="tab-view" role="tab" aria-controls="panel-view" aria-selected="false" tabindex="-1" data-tool="view">시점 조절</button>
|
||
</div>
|
||
<section id="panel-glass" class="tool-panel" role="tabpanel" aria-labelledby="tab-glass" data-panel="glass">
|
||
<div id="pattern-cards" role="group" aria-label="유리 무늬">
|
||
<button type="button" class="pattern-card" data-pattern="2" aria-pressed="true"><span class="pattern-swatch swatch-gobang" aria-hidden="true"></span><span class="pattern-name">고방유리</span><span class="pattern-detail">볼록한 조각</span></button>
|
||
<button type="button" class="pattern-card" data-pattern="0" aria-pressed="false"><span class="pattern-swatch swatch-color" aria-hidden="true"></span><span class="pattern-name">사각 색면</span><span class="pattern-detail">컬러 모자이크</span></button>
|
||
<button type="button" class="pattern-card" data-pattern="1" aria-pressed="false"><span class="pattern-swatch swatch-fine" aria-hidden="true"></span><span class="pattern-name">촘촘 유리</span><span class="pattern-detail">작은 조각들</span></button>
|
||
</div>
|
||
<label class="sr-only" aria-hidden="true" for="mosaic-style">유리 패턴<select id="mosaic-style" tabindex="-1"><option value="2" selected>고방유리 · 표면 굴절</option><option value="0">Ref1 옵션 2 · 사각 색면</option><option value="1">기존 촘촘 유리</option></select></label>
|
||
<p class="control-help">고방유리의 크기와 굴곡을 바꿔보세요.</p>
|
||
<label class="range-control" for="grid-size"><span>유리 무늬 크기 <output id="gridOut">20</output></span><input id="grid-size" type="range" min="10" max="24" step="1" value="20"><span class="range-ends" aria-hidden="true"><span>큼직하게</span><span>촘촘하게</span></span></label>
|
||
<div id="curvature-controls" class="core-optics"><label class="inline-control" for="curvature">유리 굴곡<select id="curvature"><option value="1" selected>볼록</option><option value="-1">오목</option></select></label></div>
|
||
<label class="range-control" for="photo-glass-distance"><span>유리와의 거리 <output id="photoGlassDistanceOut">16%</output></span><input id="photo-glass-distance" type="range" min="0" max="21" step="1" value="16"><span class="range-ends" aria-hidden="true"><span>가까이</span><span>조금 멀리</span></span></label>
|
||
</section>
|
||
<section id="panel-photo" class="tool-panel" role="tabpanel" aria-labelledby="tab-photo" data-panel="photo" hidden>
|
||
<div class="section-intro compact"><h2>사진 구도</h2><p>마음에 드는 위치로 직접 맞춰보세요</p></div>
|
||
<label class="range-control" for="photo-zoom"><span>사진 확대 <output id="photoZoomOut">1.00</output></span><input id="photo-zoom" type="range" min="1" max="3" step="0.05" value="1"></label>
|
||
<label class="range-control" for="photo-pan-x"><span>가로 위치 <output id="photoPanXOut">0.00</output></span><input id="photo-pan-x" type="range" min="-1" max="1" step="0.01" value="0"></label>
|
||
<label class="range-control" for="photo-pan-y"><span>세로 위치 <output id="photoPanYOut">0.00</output></span><input id="photo-pan-y" type="range" min="-1" max="1" step="0.01" value="0"></label>
|
||
<p class="control-help">사진을 확대한 뒤 가로·세로 위치를 움직일 수 있어요</p>
|
||
<button id="photo-reset" class="quiet-button">처음 구도로</button>
|
||
</section>
|
||
<section id="panel-view" class="tool-panel" role="tabpanel" aria-labelledby="tab-view" data-panel="view" hidden>
|
||
<button id="guide-replay" type="button">사용법 다시 보기</button>
|
||
<label class="range-control" for="camera-offset"><span>움직임 범위 <output id="cameraOut">0.75</output></span><input id="camera-offset" type="range" min="0.35" max="0.90" step="0.01" value="0.75"><span class="range-ends" aria-hidden="true"><span>살짝</span><span>넓게</span></span></label>
|
||
<button id="reset" type="button">정면으로</button>
|
||
<p id="scene-instructions" class="control-help">드래그·방향키로 둘러보고, 휠·두 손가락으로 확대해요. 손잡이를 왼쪽으로 밀면 열리고 오른쪽으로 밀면 닫혀요. 문 버튼으로도 조작할 수 있어요.</p>
|
||
<p id="gyro-privacy" class="control-help">버튼을 누르면 필요할 때 브라우저가 센서 접근을 물어요. 센서값은 이 기기 안에서만 사용해요</p>
|
||
</section>
|
||
</aside>
|
||
</dialog>
|
||
<!-- Internal control adapters preserve renderer diagnostics and retry state, not settings UI. -->
|
||
<div id="internal-controls" hidden inert aria-hidden="true">
|
||
<label class="range-control" for="camera-composition"><span>창가 구도</span><select id="camera-composition" aria-describedby="camera-composition-note"><option value="wall">벽 옆에서 보기 · 실험</option><option value="legacy">기존 구도 · 넓은 방 보기</option></select></label>
|
||
<p id="camera-composition-note" class="control-help" role="status">기존 구도로 바로 돌아갈 수 있어요. 선택은 이 브라우저에 저장돼요.</p>
|
||
<details id="sample-browser" class="sample-browser"><summary>샘플로 둘러보기 <span aria-hidden="true">+</span></summary><div class="details-content">
|
||
<div class="sample-strip" aria-hidden="true"><img src="/assets/samples/dog5.jpg" alt="" loading="lazy"><img src="/assets/samples/dog1.jpg" alt="" loading="lazy"><img src="/assets/samples/dog2.jpg" alt="" loading="lazy"><img src="/assets/samples/dog3.jpg" alt="" loading="lazy"><img src="/assets/samples/dog4.jpg" alt="" loading="lazy"></div>
|
||
<label for="test-photo">샘플 사진<select id="test-photo"><option value="dog5">누렁이 · 정면 인사</option><option value="dog1">호두 · 저녁 얼굴</option><option value="dog2">두부 · 나무 아래</option><option value="dog3">보리 · 낮잠</option><option value="dog4">콩이 · 저녁 산책</option></select></label>
|
||
<button id="sample" class="tactile-button">이 사진으로 해보기</button>
|
||
</div></details>
|
||
<details id="developer-tools"><summary>개발 도구 <span aria-hidden="true">+</span></summary><div id="developer-content" class="details-content">
|
||
<label for="glass-renderer">유리 렌더러<select id="glass-renderer"><option value="b" selected>B · 맞춤 메시 굴절 (기본)</option><option value="a">A · 기존 코드 (진단)</option></select></label>
|
||
<p id="door-notice" class="control-note">나무 문 손잡이를 왼쪽으로 밀어 열 수 있어요.</p>
|
||
<p id="renderer-notice" class="control-note" role="status">기본 B · 실제 고방 메시 + 맞춤 굴절</p>
|
||
<section class="comparison-control" aria-labelledby="comparison-title">
|
||
<h3 id="comparison-title">웹 유리 렌더러 비교</h3>
|
||
<p class="control-note">A 기존 코드 / 굴절 보정 B · 실제 고방 메시 + 맞춤 굴절. 같은 사진·깊이·시점·확대·거리로 동시에 표시해요. AI 분석은 한 번만 공유해요.</p>
|
||
<p class="control-note">화면을 두 번 그려 GPU·메모리 부담이 커져요. 특히 모바일에서는 느려지거나 그래픽 연결이 끊길 수 있어요. 기본 화면은 B만 그려요. A는 별도 진단 모드로 보존돼요.</p>
|
||
<button id="comparison-toggle" type="button" aria-pressed="false">비교 켜기 · 추가 렌더 비용</button>
|
||
<p id="comparison-state" class="control-note" role="status">고방유리의 Final 또는 No Glass에서 사용할 수 있어요.</p>
|
||
<details><summary>비교 조건과 차이</summary><p class="control-note">공통: 사진·128×128 깊이 메시, 격자·굴곡, IOR 1.5, 전면 요철·뒷면 위치, 거리, 카메라와 확대. B는 닫힌 실제 메시의 진입점·법선과 뒷면·옆면을 사용해요. 조명 위치와 방 배경 방향을 맞췄지만 반사 계산은 달라요.</p><p class="control-note">B: Three.js r169 MeshPhysicalMaterial의 맞춤 확장. 실제 유리 표면에서 굴절하고 유리를 나올 때 다시 굴절한 뒤 사진의 128×128 깊이 삼각형과 만나는 곳을 찾아요. 얇은 유리를 임의로 두껍게 만들지 않아요. 표준 재질만 사용한 결과가 아니며, 사진 색은 교차한 위치에서 직접 읽고 반사는 기존 물리 재질을 사용해요. 일부 GPU에서 직사각 조명을 지원하지 않으면 B에 환경광만 사용했다고 표시해요. 전체 장면의 다중 반사·산란을 추적하는 렌더러는 아니에요. A의 깊이 보정·가장자리 완화·광택 0.30과 동일한 결과를 만드는 방식도 아니에요. 다른 유리 무늬나 진단 화면으로 바꾸면 비교가 종료돼요.</p><p class="control-note">CPU 제출 시간은 GPU 시간이 아니에요. 화면 간격은 A+B를 함께 그린 이 브라우저의 값이고, 각 방식 단독 FPS가 아니에요. GPU 메모리 용량은 측정하지 않아요. 시각·속도 우열은 실제 기기에서 확인하세요.</p></details>
|
||
</section>
|
||
<p id="region-fixed-note" class="control-note">고방유리 · 표면 요철에 따른 굴절 · 색면 처리 없음</p>
|
||
<div id="reference-strip" class="reference-strip" hidden role="group" aria-label="레퍼런스 비교 이미지">
|
||
<figure aria-labelledby="reference-caption-1">
|
||
<img id="reference-image-1" alt="">
|
||
<figcaption id="reference-caption-1">Ref1 · 주 기준</figcaption>
|
||
</figure>
|
||
<figure aria-labelledby="reference-caption-2">
|
||
<img id="reference-image-2" alt="">
|
||
<figcaption id="reference-caption-2">Ref2 · 보조</figcaption>
|
||
</figure>
|
||
<p id="reference-status" role="status" aria-live="polite" hidden>레퍼런스 이미지를 불러오지 못했습니다.</p>
|
||
</div>
|
||
<p class="control-note">AI 깊이: Depth Anything V2 Small · 입력 770</p>
|
||
<details id="depth-inspection">
|
||
<summary>최대 깊이 영역 확인</summary>
|
||
<p class="control-note">모델 최대 1: 분홍 · 8비트 depth=1: 청록 · 모델 depth≥0.99: 노랑</p>
|
||
<canvas id="depth-inspection-canvas" aria-label="최대 깊이 영역 · 사진을 클릭해 값 확인"></canvas>
|
||
<pre id="depth-inspection-summary">사진과 깊이를 불러오세요.</pre>
|
||
<div class="depth-coordinates">
|
||
<label for="depth-inspection-x">픽셀 X<input id="depth-inspection-x" type="number" min="0" value="0"></label>
|
||
<label for="depth-inspection-y">픽셀 Y<input id="depth-inspection-y" type="number" min="0" value="0"></label>
|
||
</div>
|
||
<pre id="depth-inspection-point"></pre>
|
||
<small class="control-note">1은 사진별 정규화 최댓값이며 실제 거리나 코 검출 결과가 아닙니다. 간격은 깊이맵 기준 장면 단위입니다.</small>
|
||
</details>
|
||
<label class="upload" for="video">동영상 선택<input id="video" type="file" accept="video/mp4,video/webm"></label>
|
||
<label class="upload" for="depth-image">Depth 이미지 선택<input id="depth-image" type="file" accept="image/png,image/webp"></label>
|
||
<label for="view-mode">View Mode<select id="view-mode">
|
||
<option value="final">Final</option><option value="noglass">No Glass</option><option value="rgb">RGB</option><option value="depth">Depth</option><option value="glass">Glass Only</option>
|
||
</select></label>
|
||
<label for="effect">Glass<select id="effect">
|
||
<option value="mosaic">Mosaic</option><option value="reeded">Reeded</option><option value="frosted">Frosted</option>
|
||
</select></label>
|
||
<label for="depth-scale">Depth Scale <output id="depthOut">0.25</output>
|
||
<input id="depth-scale" type="range" min="0" max="0.4" step="0.01" value="0.25">
|
||
</label>
|
||
<label for="light-x">조명 가로 <output id="lightXOut">0.60</output>
|
||
<input id="light-x" type="range" min="-2" max="2" step="0.05" value="0.6">
|
||
</label>
|
||
<label for="light-y">조명 세로 <output id="lightYOut">0.80</output>
|
||
<input id="light-y" type="range" min="-2" max="2" step="0.05" value="0.8">
|
||
</label>
|
||
<label for="sensitivity">View sensitivity <output id="sensitivityOut">1.00</output>
|
||
<input id="sensitivity" type="range" min="0.2" max="2" step="0.05" value="1">
|
||
</label>
|
||
|
||
</div></details>
|
||
<section class="processing-card" aria-label="사진 처리 상태"><div class="processing-copy"><p id="status" role="status" aria-live="polite">사진을 선택하면 AI가 입체감을 준비해요</p><p id="depth-provenance" hidden></p></div><button id="ai-depth" data-testid="ai-depth" class="quiet-button" disabled>AI 입체감 만들기</button></section>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>
|