123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- <!DOCTYPE html><html lang="ko"><head>
- <meta charset="utf-8">
- <title>사용자 지정 Geometry(Custom BufferGeometry)</title>
- <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
- <meta name="twitter:card" content="summary_large_image">
- <meta name="twitter:site" content="@threejs">
- <meta name="twitter:title" content="Three.js – 사용자 지정 Geometry(Custom BufferGeometry)">
- <meta property="og:image" content="https://threejs.org/files/share.png">
- <link rel="shortcut icon" href="/files/favicon_white.ico" media="(prefers-color-scheme: dark)">
- <link rel="shortcut icon" href="/files/favicon.ico" media="(prefers-color-scheme: light)">
- <link rel="stylesheet" href="/manual/resources/lesson.css">
- <link rel="stylesheet" href="/manual/resources/lang.css">
- <!-- Import maps polyfill -->
- <!-- Remove this when import maps will be widely supported -->
- <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
- <script type="importmap">
- {
- "imports": {
- "three": "../../build/three.module.js"
- }
- }
- </script>
- <link rel="stylesheet" href="/manual/ko/lang.css">
- </head>
- <body>
- <div class="container">
- <div class="lesson-title">
- <h1>사용자 지정 Geometry(Custom BufferGeometry)</h1>
- </div>
- <div class="lesson">
- <div class="lesson-main">
- <div class="warning">
- <strong>NOTE!</strong> This article is deprecated. Three.js r125
- removed support for <code class="notranslate" translate="no">Geometry</code>. Please refer to
- the article on <a href="custom-buffergeometry.html">custom BufferGeometry</a>.
- </div>
- <p><a href="primitives.html">이전 글</a>에서는 Three.js의 내장 원시 모델에
- 대해 살펴보았죠. 이 글에서는 이런 모델, geometry를 직접 만들어 볼 것입니다.</p>
- <p>거듭 이야기하지만, 정말 진지하게 3D 컨텐츠를 만들 생각이라면
- <a href="https://blender.org">블렌더(Blender)</a>,
- <a href="https://www.autodesk.com/products/maya/overview">마야(Maya)</a>,
- <a href="https://www.autodesk.com/products/3ds-max/overview">3D Studio Max</a>,
- <a href="https://www.maxon.net/en-us/">시네마4D(Cinema4D)</a> 등의 3D 모델링
- 프로그램을 사용하는 것이 좋습니다. 모델을 만들고 <a href="load-gltf.html">gLTF</a>나
- <a href="load-obj.html">.obj</a> 포멧으로 저장하여 프로젝트에서 불러오는
- 것이죠. 어떤 프로그램을 선택하든 튜토리얼에는 유용한 내용이 많으니, 2주에서
- 3주 정도는 해당 프로그램의 튜토리얼을 익히는 데 투자하기 바랍니다.</p>
- <p>하지만 때로는 모델링 프로그램을 쓰는 것보다 직접 3D geometry를
- 만드는 게 유리할 수 있을 겁니다.</p>
- <p>먼저 정육면체를 하나 만들어보겠습니다. 이미 원시 모델에 <a href="/docs/#api/ko/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a>와
- <a href="/docs/#api/ko/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a>가 있긴 하지만, 기본 개념을 이해하는 데는 간단한 게
- 훨씬 효과적일 테니까요.</p>
- <p>Three.js에서 사용자 지정 geometry는 <code class="notranslate" translate="no">Geometry</code> 또는 <a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>,
- 2가지 클래스를 이용해 만들 수 있습니다. <code class="notranslate" translate="no">Geometry</code>는 분명 사용하기에는
- 편하지만 느리고 메모리도 많이 차지합니다. 삼각형 천 개 정도까지야 그냥
- 써도 나쁠 것이 없지만, 만 개가 넘어간다면 <a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>를 고려하는
- 것이 좋죠.</p>
- <p>당연하게도 <a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>는 훨씬 쓰기 어렵지만, 비교적 메모리도 덜 차지하고
- 훨씬 빠릅니다. 대략 구상하기에 삼각형을 10000개 이상 쓰겠다 싶으면 <a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>를
- 고려하기 바랍니다.</p>
- <p>아까 <code class="notranslate" translate="no">Geometry</code>가 느리다고 했는데, 이는 처음 로드할 때와 수정할 때 느리다는
- 것이지, 렌더링 속도가 느리다는 말이 아닙니다. geometry를 수정하지 않고 geometry가
- 무지막지하게 크지 않은 한, <code class="notranslate" translate="no">Geometry</code>는 <a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>에 비해 프로그램 초기화
- 단계에서만 약간 더 느릴 뿐입니다. 결국에는 둘 다 살펴보겠지만, 일단-제 생각에-훨씬
- 간단하고 이해하기 쉬운 <code class="notranslate" translate="no">Geometry</code>를 먼저 써봅시다.</p>
- <p>먼저 정육면체를 만들겠습니다. <a href="responsive.html">반응형 디자인에 관한 글</a>에서
- 썼던 예제를 일부 가져오도록 하죠.</p>
- <p>먼저 <a href="/docs/#api/ko/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a>를 <code class="notranslate" translate="no">Geometry</code>로 교체합니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const boxWidth = 1;
- -const boxHeight = 1;
- -const boxDepth = 1;
- -const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
- +const geometry = new THREE.Geometry();
- </pre>
- <p>이제 정육면체의 꼭지점를 추가합니다. 정육면체의 모서리는 총 8개이죠.</p>
- <div class="threejs_center"><img src="../resources/cube-vertex-positions.svg" style="width: 500px"></div>
- <p>중점을 중심으로 각 꼭지점을 추가합니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const geometry = new THREE.Geometry();
- +geometry.vertices.push(
- + new THREE.Vector3(-1, -1, 1), // 0
- + new THREE.Vector3( 1, -1, 1), // 1
- + new THREE.Vector3(-1, 1, 1), // 2
- + new THREE.Vector3( 1, 1, 1), // 3
- + new THREE.Vector3(-1, -1, -1), // 4
- + new THREE.Vector3( 1, -1, -1), // 5
- + new THREE.Vector3(-1, 1, -1), // 6
- + new THREE.Vector3( 1, 1, -1), // 7
- +);
- </pre>
- <p>다음으로 정육면체 한 면에 2개씩, 총 삼각형 12개를 추가해야 합니다.</p>
- <div class="threejs_center"><img src="../resources/cube-triangles.svg" style="width: 500px"></div>
- <p><a href="/docs/#api/ko/core/Face3"><code class="notranslate" translate="no">Face3</code></a> 인스턴스를 만들어 3 꼭지점의 인덱스(index)를 넘겨주면 삼각형
- 면(face)을 만들 수 있습니다.</p>
- <p>꼭지점의 인덱스를 넘겨줄 때는 순서에 유의해야 합니다. 삼각형이 카메라를
- 바라볼 때, 면이 정육면체의 바깥쪽을 향하려면 시계 반대 방향 순으로 인덱스를
- 넘겨줘야 합니다.</p>
- <div class="threejs_center"><img src="../resources/cube-vertex-winding-order.svg" style="width: 500px"></div>
- <p>이 패턴대로 정육면체를 구성할 삼각형 12개를 만들어봅시다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces.push(
- // 앞쪽
- new THREE.Face3(0, 3, 2),
- new THREE.Face3(0, 1, 3),
- // 오른쪽
- new THREE.Face3(1, 7, 3),
- new THREE.Face3(1, 5, 7),
- // 뒷쪽
- new THREE.Face3(5, 6, 7),
- new THREE.Face3(5, 4, 6),
- // 왼쪽
- new THREE.Face3(4, 2, 6),
- new THREE.Face3(4, 0, 2),
- // 상단
- new THREE.Face3(2, 7, 6),
- new THREE.Face3(2, 3, 7),
- // 하단
- new THREE.Face3(4, 1, 0),
- new THREE.Face3(4, 5, 1),
- );
- </pre>
- <p>이제 몇 가지만 더 고치면 됩니다.</p>
- <p>이 정육면체들은 <a href="/docs/#api/ko/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a>보다 두 배 더 크므로, 카메라를 약간
- 더 뒤로 옮겨줍니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
- const aspect = 2; // canvas 기본값
- const near = 0.1;
- -const far = 5;
- +const far = 100;
- const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
- -camera.position.z = 2;
- +camera.position.z = 5;
- </pre>
- <p>커진 만큼 간격을 띄우고, 바꾸는 김에 색상도 바꿔주겠습니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cubes = [
- - makeInstance(geometry, 0x44aa88, 0),
- - makeInstance(geometry, 0x8844aa, -2),
- - makeInstance(geometry, 0xaa8844, 2),
- + makeInstance(geometry, 0x44FF44, 0),
- + makeInstance(geometry, 0x4444FF, -4),
- + makeInstance(geometry, 0xFF4444, 4),
- ];
- </pre>
- <p>아직 법선(normal)을 넣지 않았는데, 법선이 없으면 빛은 소용이 없으니 마지막으로
- 재질(material)을 <a href="/docs/#api/ko/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>로 바꿔줍니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, x) {
- - const material = new THREE.MeshPhongMaterial({ color });
- + const material = new THREE.MeshBasicMaterial({ color });
- const cube = new THREE.Mesh(geometry, material);
- scene.add(cube);
- ...
- </pre>
- <p>자, 이제 실행해보죠.</p>
- <p></p><div translate="no" class="threejs_example_container notranslate">
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube.html"></iframe></div>
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube.html" target="_blank">새 탭에서 보기</a>
- </div>
- <p></p>
- <p>각 삼각형 면의 <code class="notranslate" translate="no">color</code> 속성을 바꿔 색을 따로 지정할 수 있습니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces[ 0].color = geometry.faces[ 1].color = new THREE.Color('red');
- geometry.faces[ 2].color = geometry.faces[ 3].color = new THREE.Color('yellow');
- geometry.faces[ 4].color = geometry.faces[ 5].color = new THREE.Color('green');
- geometry.faces[ 6].color = geometry.faces[ 7].color = new THREE.Color('cyan');
- geometry.faces[ 8].color = geometry.faces[ 9].color = new THREE.Color('blue');
- geometry.faces[10].color = geometry.faces[11].color = new THREE.Color('magenta');
- </pre>
- <p>추가로 재질을 생성할 때 <code class="notranslate" translate="no">vertexColors</code>를 사용한다고 명시해야 하죠.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const material = new THREE.MeshBasicMaterial({ color });
- +const material = new THREE.MeshBasicMaterial({ vertexColors: true });
- </pre>
- <p></p><div translate="no" class="threejs_example_container notranslate">
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-face-colors.html"></iframe></div>
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube-face-colors.html" target="_blank">새 탭에서 보기</a>
- </div>
- <p></p>
- <p>또는 삼각형 면의 <code class="notranslate" translate="no">vertexColors</code> 속성에 각 꼭지점의 색상을 지정할 수도 있습니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces.forEach((face, ndx) => {
- face.vertexColors = [
- (new THREE.Color()).setHSL(ndx / 12 , 1, 0.5),
- (new THREE.Color()).setHSL(ndx / 12 + 0.1, 1, 0.5),
- (new THREE.Color()).setHSL(ndx / 12 + 0.2, 1, 0.5),
- ];
- });
- </pre>
- <p></p><div translate="no" class="threejs_example_container notranslate">
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-vertex-colors.html"></iframe></div>
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube-vertex-colors.html" target="_blank">새 탭에서 보기</a>
- </div>
- <p></p>
- <p>빛을 사용하려면 법선을 추가해야 합니다. 법선이란 특정 방향을 나타내는 벡터값으로,
- 색과 마찬가지로 법선도 각 삼각형 면의 <code class="notranslate" translate="no">normal</code> 속성을 지정해 추가할 수 있습니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">face.normal = new THREE.Vector3(...)
- </pre>
- <p>또는 <code class="notranslate" translate="no">vertexNormals</code> 속성에 각 꼭지점의 법선을 배열로 지정할 수도 있죠.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">face.vertexNormals = [
- new THREE.Vector3(...),
- new THREE.Vector3(...),
- new THREE.Vector3(...),
- ]
- </pre>
- <p>하지만 대게 우리가 지정한 좌표에 따라 알아서 법선을 계산해달라고 하는 게
- 훨씬 편합니다.</p>
- <p>삼각형 면 법선의 경우 <code class="notranslate" translate="no">Geometry.computeFaceNormals</code>를 호출하면 되죠.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.computeFaceNormals();
- </pre>
- <p>꼭지점 색을 제거하고 다시 재질을 <a href="/docs/#api/ko/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a>로 바꾸겠습니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const material = new THREE.MeshBasicMaterial({ vertexColors: true });
- +const material = new THREE.MeshPhongMaterial({ color });
- </pre>
- <p>이제 정육면체들이 빛의 영향을 받습니다.</p>
- <p></p><div translate="no" class="threejs_example_container notranslate">
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-face-normals.html"></iframe></div>
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube-face-normals.html" target="_blank">새 탭에서 보기</a>
- </div>
- <p></p>
- <p>삼각형 면 법선을 사용하면 물체가 각진 느낌을 줍니다. 꼭지점 법선을 사용하면
- 훨씬 부드러워 보일 수 있죠. 꼭지점 법선은 <code class="notranslate" translate="no">Geometry.computeVertexNormals</code>를
- 호출해 사용합니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-geometry.computeFaceNormals();
- +geometry.computeVertexNormals();
- </pre>
- <p>아쉽게도 정육면체는 꼭지점 법선의 예제로 적당하지 않습니다. 각 꼭지점이 같은
- 꼭지점을 쓰는 모든 삼각형 면에서 법선을 가져오기 때문이죠.</p>
- <p></p><div translate="no" class="threejs_example_container notranslate">
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-vertex-normals.html"></iframe></div>
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube-vertex-normals.html" target="_blank">새 탭에서 보기</a>
- </div>
- <p></p>
- <p>UV라고도 불리는, 텍스처 좌표는 <code class="notranslate" translate="no">Geometry.faceVertexUvs</code> 속성에 삼각형 면들의
- 층(layer)을 배열로 지정해 추가할 수 있습니다. 정육면체의 경우 다음처럼 지정할
- 수 있죠.</p>
- <p>(※ 참고: <a href="https://ko.wikipedia.org/wiki/UV_%EB%A7%A4%ED%95%91">UV 매핑</a>. 역주)</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faceVertexUvs[0].push(
- // 앞쪽
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
- // 오른쪽
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
- // 뒤쪽
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
- // 왼쪽
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
- // 상단
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
- // 하단
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
- );
- </pre>
- <p>중요한 건 <code class="notranslate" translate="no">faceVertexUvs</code>는 층의 배열이라는 점입니다. 하나의 층은 별도의 UV 좌표이죠.
- 기본적으로 하나의 UV 층, 층 0이 있어, 예제에서는 그냥 그 층에 UV를 추가했습니다.</p>
- <p>다시 삼각형 면 법선을 계산하도록 코드를 바꾸고, 이번에는 재질에 <a href="textures.html">텍스처를 추가</a>하겠습니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-geometry.computeVertexNormals();
- +geometry.computeFaceNormals();
- +const loader = new THREE.TextureLoader();
- +const texture = loader.load('resources/images/star.png');
- function makeInstance(geometry, color, x) {
- - const material = new THREE.MeshPhongMaterial({color});
- + const material = new THREE.MeshPhongMaterial({color, map: texture});
- const cube = new THREE.Mesh(geometry, material);
- scene.add(cube);
- ...
- </pre>
- <p></p><div translate="no" class="threejs_example_container notranslate">
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-texcoords.html"></iframe></div>
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube-texcoords.html" target="_blank">새 탭에서 보기</a>
- </div>
- <p></p>
- <p>다음으로는 이 글에서 배운 것을 총 동원해 지형 mesh를 기반으로 높이 맵(heightmap)을
- 만들어보겠습니다.</p>
- <p>높이 맵을 기반으로 한 지형이란, 이차원 높이 배열을 격자 형태로 만든 것을
- 말합니다. 이차원 높이 배열을 만드는 가장 쉬운 방법은 이미지 편집 프로그램을
- 사용하는 것이죠. 아래는 제가 만든 96x64 픽셀의 이미지입니다.</p>
- <div class="threejs_center"><img src="../examples/resources/images/heightmap-96x64.png" style="width: 512px; image-rendering: pixelated;"></div>
- <p>이 이미지의 데이터를 불러와 높이 맵 mesh를 만들겠습니다. 이미지 데이터를
- 불러올 때는 <a href="/docs/#api/ko/loaders/ImageLoader"><code class="notranslate" translate="no">ImageLoader</code></a>를 활용합니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const imgLoader = new THREE.ImageLoader();
- imgLoader.load('resources/images/heightmap-96x64.png', createHeightmap);
- function createHeightmap(image) {
- // canvas에 이미지를 렌더링한 후, getImageData 메서드를 호출해 픽셀 데이터를 추출합니다
- const ctx = document.createElement('canvas').getContext('2d');
- const { width, height } = image;
- ctx.canvas.width = width;
- ctx.canvas.height = height;
- ctx.drawImage(image, 0, 0);
- const { data } = ctx.getImageData(0, 0, width, height);
- const geometry = new THREE.Geometry();
- </pre>
- <p>이미지에서 데이터를 추출했으니, 이제 격자를 만들어야 합니다. 이미지의 픽셀
- 하나당 정사각형 격자 한 칸을 만듭니다.</p>
- <div class="threejs_center"><img src="../resources/heightmap-points.svg" style="width: 500px"></div>
- <p>격자 한 칸당 꼭지점 5개를 만듭니다. 정사각형의 각 꼭지점 당 하나씩 총 4개를 두고,
- 네 꼭지점의 높이를 평균내 중앙에 하나를 둡니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cellsAcross = width - 1;
- const cellsDeep = height - 1;
- for (let z = 0; z < cellsDeep; ++z) {
- for (let x = 0; x < cellsAcross; ++x) {
- /**
- * 열의 위치를 높이 데이터로 계산합니다
- * 데이터가 RGBA이므로 4를 곱하지만, R 값만 사용합니다
- **/
- const base0 = (z * width + x) * 4;
- const base1 = base0 + (width * 4);
- // 격자 칸 각 꼭지점의 높이를 참조합니다
- const h00 = data[base0] / 32;
- const h01 = data[base0 + 4] / 32;
- const h10 = data[base1] / 32;
- const h11 = data[base1 + 4] / 32;
- // 높이의 평균값을 구합니다
- const hm = (h00 + h01 + h10 + h11) / 4;
- // 꼭지점의 위치
- const x0 = x;
- const x1 = x + 1;
- const z0 = z;
- const z1 = z + 1;
- // 각 꼭지점의 첫 번째 인덱스를 기록합니다
- const ndx = geometry.vertices.length;
- // 격자에 모퉁이 꼭지점 4개와 중앙 꼭지점 하나를 배치합니다
- geometry.vertices.push(
- new THREE.Vector3(x0, h00, z0),
- new THREE.Vector3(x1, h01, z0),
- new THREE.Vector3(x0, h10, z1),
- new THREE.Vector3(x1, h11, z1),
- new THREE.Vector3((x0 + x1) / 2, hm, (z0 + z1) / 2),
- );
- </pre>
- <p>다음으로 방금 만든 5개의 정점을 모아 4개의 삼각형을 만들어야 합니다.</p>
- <div class="threejs_center"><img src="../resources/heightmap-triangles.svg" style="width: 500px"></div>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> // 삼각형 4개를 만듭니다
- geometry.faces.push(
- new THREE.Face3(ndx + 0, ndx + 4, ndx + 1),
- new THREE.Face3(ndx + 1, ndx + 4, ndx + 3),
- new THREE.Face3(ndx + 3, ndx + 4, ndx + 2),
- new THREE.Face3(ndx + 2, ndx + 4, ndx + 0),
- );
- // 각 삼각형 면의 각 꼭지점에 텍스처 좌표를 추가합니다
- const u0 = x / cellsAcross;
- const v0 = z / cellsDeep;
- const u1 = (x + 1) / cellsAcross;
- const v1 = (z + 1) / cellsDeep;
- const um = (u0 + u1) / 2;
- const vm = (v0 + v1) / 2;
- geometry.faceVertexUvs[0].push(
- [ new THREE.Vector2(u0, v0), new THREE.Vector2(um, vm), new THREE.Vector2(u1, v0) ],
- [ new THREE.Vector2(u1, v0), new THREE.Vector2(um, vm), new THREE.Vector2(u1, v1) ],
- [ new THREE.Vector2(u1, v1), new THREE.Vector2(um, vm), new THREE.Vector2(u0, v1) ],
- [ new THREE.Vector2(u0, v1), new THREE.Vector2(um, vm), new THREE.Vector2(u0, v0) ],
- );
- }
- }
- </pre>
- <p>이제 마무리 지어보죠.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> geometry.computeFaceNormals();
- // geometry를 중점에 배치
- geometry.translate(width / -2, 0, height / -2);
- const loader = new THREE.TextureLoader();
- const texture = loader.load('resources/images/star.png');
- const material = new THREE.MeshPhongMaterial({ color: 'green', map: texture });
- const cube = new THREE.Mesh(geometry, material);
- scene.add(cube);
- }
- </pre>
- <p>장면을 보기 쉽도록 몇 가지 요소를 추가하겠습니다.</p>
- <p><a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>를 추가하고,</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from '/build/three.module.js';
- +import { OrbitControls } from '/examples/jsm/controls/OrbitControls.js';
- </pre>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
- const aspect = 2; // canvas 기본 비율
- const near = 0.1;
- -const far = 100;
- +const far = 200;
- const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
- -camera.position.z = 5;
- +camera.position.set(20, 20, 20);
- +const controls = new OrbitControls(camera, canvas);
- +controls.target.set(0, 0, 0);
- +controls.update();
- </pre>
- <p>조명도 두 개 추가합니다.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-{
- +function addLight(...pos) {
- const color = 0xFFFFFF;
- const intensity = 1;
- const light = new THREE.DirectionalLight(color, intensity);
- - light.position.set(-1, 2, 4\);
- + light.position.set(...pos);
- scene.add(light);
- }
- +addLight(-1, 2, 4);
- +addLight(1, 2, -2);
- </pre>
- <p>정육면체를 회전시키는 코드는 필요없으니 삭제하도록 하죠.</p>
- <p></p><div translate="no" class="threejs_example_container notranslate">
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-heightmap.html"></iframe></div>
- <a class="threejs_center" href="/manual/examples/custom-geometry-heightmap.html" target="_blank">새 탭에서 보기</a>
- </div>
- <p></p>
- <p>이 글이 <code class="notranslate" translate="no">Geometry</code>를 활용하는 데 도움이 되었으면 합니다.</p>
- <p>글이 길어졌으니 <a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>는 <a href="custom-buffergeometry.html">다음 글</a>에서
- 살펴보도록 하겠습니다.</p>
- </div>
- </div>
- </div>
-
- <script src="/manual/resources/prettify.js"></script>
- <script src="/manual/resources/lesson.js"></script>
- </body></html>
|