You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
earthquake_3d_viewer_front/three/manual/ko/custom-buffergeometry.html

431 lines
24 KiB
HTML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html><html lang="ko"><head>
<meta charset="utf-8">
<title>사용자 지정 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 사용자 지정 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="../resources/lesson.css">
<link rel="stylesheet" href="../resources/lang.css">
<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>사용자 지정 BufferGeometry</h1>
</div>
<div class="lesson">
<div class="lesson-main">
<p><a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>는 Three.js 내의 모든 <code class="notranslate" translate="no">geometry</code>를 나타냅니다(r125에서부터 <code class="notranslate" translate="no">Geometry</code>가 제거되었습니다). 좀 더 자세히 말하면 특정 <a href="/docs/#api/ko/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>라고 부르는 <strong>속성</strong>의 집합이죠.</p>
<p><a href="/docs/#api/ko/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>
위치(positions), 법선(normals), 색(colors), uv 데이터의 배열이고, 이들을 모으면 각 꼭지점에
대한 <em>평행 배열</em> 형식의 데이터가 됩니다.</p>
<div class="threejs_center"><img src="../resources/threejs-attributes.svg" style="width: 700px"></div>
<p>그림을 보면 총 4개의 속성(attribute), <code class="notranslate" translate="no">position</code>, <code class="notranslate" translate="no">normal</code>, <code class="notranslate" translate="no">color</code>, <code class="notranslate" translate="no">uv</code>가 있습니다.
이들은 평행 배열로 각 속성의 N 번째 데이터 묶음이 한 꼭지점의 데이터를 나타냅니다. 표시한
index = 4 위치의 꼭지점 데이터를 보세요. 이 묶음이 하나의 꼭지점을 정의합니다.</p>
<p>이해를 돕기 위해 정육면체의 한 모서리를 강조해보겠습니다.</p>
<div class="threejs_center"><img src="../resources/cube-faces-vertex.svg" style="width: 500px"></div>
<p>이 경우 맞닿은 면의 색이 다르기에, 이 모서리는 각 면에 다른 법선을 제공해야 합니다.
마찬가지로 UV도 달라야 하죠.
하나의 <em>꼭지점</em>은 위 4개 속성의 묶음이고, 때문에 속성이 달라진다면 그건 다른 꼭지점이
되는 것이죠.</p>
<p>이전과 마찬가지로 <a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>로 정육면체를 만들어보겠습니다. 굳이 정육면체를 쓰는
이유는 모서리의 꼭지점을 공유하는 듯해도 사실 그렇지 않기 때문이죠. 필요한 꼭지점을
전부 생성한 후, 꼭지점 데이터를 평행 배열로 변환해 <a href="/docs/#api/ko/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>를 만들고, 이를
<a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>에 추가해야 합니다.</p>
<p>먼저 정육면체를 만드는 데 필요한 데이터를 정렬합니다.
아까 말했듯 꼭지점의 속성 중 하나라도 다르다면 별도의 꼭지점으로 분리해야 합니다. 정육면체의
경우는 총 꼭지점 36개가 필요하죠. 면 6개, 면 하나당 삼각형 2개, 삼각형 하나 당 꼭지점 3개,
총 36개입니다.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const vertices = [
// 앞쪽
{ pos: [-1, -1, 1], norm: [ 0, 0, 1], uv: [0, 0], },
{ pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 0], },
{ pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 1], },
{ pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 1], },
{ pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 0], },
{ pos: [ 1, 1, 1], norm: [ 0, 0, 1], uv: [1, 1], },
// 오른쪽
{ pos: [ 1, -1, 1], norm: [ 1, 0, 0], uv: [0, 0], },
{ pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 0], },
{ pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 1], },
{ pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 1], },
{ pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 0], },
{ pos: [ 1, 1, -1], norm: [ 1, 0, 0], uv: [1, 1], },
// 뒤쪽
{ pos: [ 1, -1, -1], norm: [ 0, 0, -1], uv: [0, 0], },
{ pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 0], },
{ pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 1], },
{ pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 1], },
{ pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 0], },
{ pos: [-1, 1, -1], norm: [ 0, 0, -1], uv: [1, 1], },
// 왼쪽
{ pos: [-1, -1, -1], norm: [-1, 0, 0], uv: [0, 0], },
{ pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 0], },
{ pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 1], },
{ pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 1], },
{ pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 0], },
{ pos: [-1, 1, 1], norm: [-1, 0, 0], uv: [1, 1], },
// 상단
{ pos: [ 1, 1, -1], norm: [ 0, 1, 0], uv: [0, 0], },
{ pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 0], },
{ pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 1], },
{ pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 1], },
{ pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 0], },
{ pos: [-1, 1, 1], norm: [ 0, 1, 0], uv: [1, 1], },
// 하단
{ pos: [ 1, -1, 1], norm: [ 0, -1, 0], uv: [0, 0], },
{ pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 0], },
{ pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 1], },
{ pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 1], },
{ pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 0], },
{ pos: [-1, -1, -1], norm: [ 0, -1, 0], uv: [1, 1], },
];
</pre>
<p>다음으로 이 배열을 3개의 평행 배열로 변환합니다.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const positions = [];
const normals = [];
const uvs = [];
for (const vertex of vertices) {
positions.push(...vertex.pos);
normals.push(...vertex.norm);
uvs.push(...vertex.uv);
}
</pre>
<p>이제 <a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>를 만듭니다. 그리고 각 배열로 <a href="/docs/#api/ko/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a> 인스턴스를 생성한
<a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>에 추가합니다.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no"> const geometry = new THREE.BufferGeometry();
const positionNumComponents = 3;
const normalNumComponents = 3;
const uvNumComponents = 2;
geometry.setAttribute(
'position',
new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
geometry.setAttribute(
'normal',
new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
geometry.setAttribute(
'uv',
new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
</pre>
<p>이때 정확히 Three.js가 원하는 속성 이름을 써야 합니다(사용자 지정 쉐이더를 만들 때와는
달리). 이 경우에는 <code class="notranslate" translate="no">position</code>, <code class="notranslate" translate="no">normal</code>, <code class="notranslate" translate="no">uv</code>이죠. 꼭지점 색을 지정하려면 <code class="notranslate" translate="no">color</code> 속성을
지정해야 합니다.</p>
<p>아까 <code class="notranslate" translate="no">positions</code>, <code class="notranslate" translate="no">normals</code>, <code class="notranslate" translate="no">uvs</code>, 3개의 자바스크립트 순수 배열을 생성했습니다.
그리고 위에서 해당 배열을 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">형식화 배열</a>,
<code class="notranslate" translate="no">Float32Array</code>로 변환했죠. <a href="/docs/#api/ko/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>는 순수 배열이 아니라 형식화 배열을
인자로 받습니다. 또한 하나의 꼭지점에 몇 개의 요소를 사용할 지 지정해줘야 하죠.
위치(position)과 법선(normal)의 경우 x, y, z 총 3개이고, UV는 u, v 총 2개입니다.</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-buffergeometry-cube.html"></iframe></div>
<a class="threejs_center" href="/manual/examples/custom-buffergeometry-cube.html" target="_blank">새 탭에서 보기</a>
</div>
<p></p>
<p>데이터가 너무 많네요. 크게 구조를 바꾸긴 어렵지만, 꼭지점을 인덱스로 참조하게끔 바꾸면
조금 나을 듯합니다. 정육면체 데이터를 다시 봅시다. 각 면은 2개의 삼각형, 삼각형 하나에는
꼭지점이 3개 있으므로 면 하나에는 총 6개의 꼭지점이 있습니다. 하지만 이 중 꼭지점 2개는
완전히 같죠(같은 위치, 같은 법선, 같은 uv). 중복된 꼭지점을 제거하고 인덱스로 데이터를
불러오도록 바꿀 수 있겠네요. 먼저 중복된 꼭지점을 제거하겠습니다.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const vertices = [
// 앞쪽
{ pos: [-1, -1, 1], norm: [ 0, 0, 1], uv: [0, 0], }, // 0
{ pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 0], }, // 1
{ pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 1], }, // 2
-
- { pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 1], },
- { pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 0], },
{ pos: [ 1, 1, 1], norm: [ 0, 0, 1], uv: [1, 1], }, // 3
// 오른쪽
{ pos: [ 1, -1, 1], norm: [ 1, 0, 0], uv: [0, 0], }, // 4
{ pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 0], }, // 5
-
- { pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 1], },
- { pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 0], },
{ pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 1], }, // 6
{ pos: [ 1, 1, -1], norm: [ 1, 0, 0], uv: [1, 1], }, // 7
// 뒤쪽
{ pos: [ 1, -1, -1], norm: [ 0, 0, -1], uv: [0, 0], }, // 8
{ pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 0], }, // 9
-
- { pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 1], },
- { pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 0], },
{ pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 1], }, // 10
{ pos: [-1, 1, -1], norm: [ 0, 0, -1], uv: [1, 1], }, // 11
// 왼쪽
{ pos: [-1, -1, -1], norm: [-1, 0, 0], uv: [0, 0], }, // 12
{ pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 0], }, // 13
-
- { pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 1], },
- { pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 0], },
{ pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 1], }, // 14
{ pos: [-1, 1, 1], norm: [-1, 0, 0], uv: [1, 1], }, // 15
// 상단
{ pos: [ 1, 1, -1], norm: [ 0, 1, 0], uv: [0, 0], }, // 16
{ pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 0], }, // 17
-
- { pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 1], },
- { pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 0], },
{ pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 1], }, // 18
{ pos: [-1, 1, 1], norm: [ 0, 1, 0], uv: [1, 1], }, // 19
// 하단
{ pos: [ 1, -1, 1], norm: [ 0, -1, 0], uv: [0, 0], }, // 20
{ pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 0], }, // 21
-
- { pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 1], },
- { pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 0], },
{ pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 1], }, // 22
{ pos: [-1, -1, -1], norm: [ 0, -1, 0], uv: [1, 1], }, // 23
];
</pre>
<p>이제 꼭지점 24개만 남았습니다. 중복되는 꼭지점은 없죠. 이제 <a href="/docs/#api/ko/core/BufferGeometry.setIndex"><code class="notranslate" translate="no">BufferGeometry.setIndex</code></a>
메서드에 각 꼭지점 데이터의 인덱스값 36개-삼각형이 12개이므로-를 넘겨줍니다.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.setAttribute(
'position',
new THREE.BufferAttribute(positions, positionNumComponents));
geometry.setAttribute(
'normal',
new THREE.BufferAttribute(normals, normalNumComponents));
geometry.setAttribute(
'uv',
new THREE.BufferAttribute(uvs, uvNumComponents));
+geometry.setIndex([
+ 0, 1, 2, 2, 1, 3, // 앞쪽
+ 4, 5, 6, 6, 5, 7, // 오른쪽
+ 8, 9, 10, 10, 9, 11, // 뒤쪽
+ 12, 13, 14, 14, 13, 15, // 왼쪽
+ 16, 17, 18, 18, 17, 19, // 상단
+ 20, 21, 22, 22, 21, 23, // 하단
+]);
</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-buffergeometry-cube-indexed.html"></iframe></div>
<a class="threejs_center" href="/manual/examples/custom-buffergeometry-cube-indexed.html" target="_blank">새 탭에서 보기</a>
</div>
<p></p>
<p><a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>에 법선을 지정하지 않았다면 <a href="/docs/#api/ko/core/BufferGeometry#computeVertexNormals"><code class="notranslate" translate="no">computeVertexNormals</code></a>
메서드를 호출해 자동으로 법선을 지정할 수 있습니다. 다만 데이터가 조금이라도 다르다면
꼭지점을 공유할 수 없기에 구체나 원통 같은 물체를 만들려 하는 경우에는 이음새가 보일 수 있어요.</p>
<div class="spread">
<div>
<div data-diagram="bufferGeometryCylinder"></div>
<div class="code">BufferGeometry</div>
</div>
<div>
<div data-diagram="geometryCylinder"></div>
<div class="code">Geometry</div>
</div>
</div>
<p>위 두 원통은 <code class="notranslate" translate="no">computeVertexNormals</code> 메서드를 호출해 법선을 생성했습니다. 자세히 보면
왼쪽 원통에 이음매가 있는 게 보일 텐데, 이는 원통의 끝과 시작점의 UV가 달라 꼭지점을
공유할 수 없기 때문입니다. 그다지 큰 문제는 아니지만, 알아두어 나쁠 건 없죠. 이를 해결하려면
법선을 직접 지정해야 합니다.</p>
<p>아까는 처음에 순수 배열을 썼지만, 처음부터 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">형식화 배열</a>
사용할 수도 있습니다. 다만 형식화 배열은 초기화할 때 미리 크기를 지정해줘야 합니다.
그렇게 어려운 일은 아니지만, 순수 배열은 <code class="notranslate" translate="no">push</code> 메서드로 요소를 추가하고 바뀐
길이를 <code class="notranslate" translate="no">length</code> 속성으로 확인할 수 있습니다. 하지만 형식화 배열을 사용하면 어디서
요소를 추가했는지 직접 일일이 기록해야 하죠.</p>
<p>예제의 경우는 사전에 정해진 데이터를 사용하기에 배열 길이를 미리 구하는 게 어렵지
않습니다.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const positions = [];
-const normals = [];
-const uvs = [];
+const numVertices = vertices.length;
+const positionNumComponents = 3;
+const normalNumComponents = 3;
+const uvNumComponents = 2;
+const positions = new Float32Array(numVertices * positionNumComponents);
+const normals = new Float32Array(numVertices * normalNumComponents);
+const uvs = new Float32Array(numVertices * uvNumComponents);
+let posNdx = 0;
+let nrmNdx = 0;
+let uvNdx = 0;
for (const vertex of vertices) {
- positions.push(...vertex.pos);
- normals.push(...vertex.norm);
- uvs.push(...vertex.uv);
+ positions.set(vertex.pos, posNdx);
+ normals.set(vertex.norm, nrmNdx);
+ uvs.set(vertex.uv, uvNdx);
+ posNdx += positionNumComponents;
+ nrmNdx += normalNumComponents;
+ uvNdx += uvNumComponents;
}
geometry.setAttribute(
'position',
- new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
+ new THREE.BufferAttribute(positions, positionNumComponents));
geometry.setAttribute(
'normal',
- new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
+ new THREE.BufferAttribute(normals, normalNumComponents));
geometry.setAttribute(
'uv',
- new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
+ new THREE.BufferAttribute(uvs, uvNumComponents));
geometry.setIndex([
0, 1, 2, 2, 1, 3, // 앞쪽
4, 5, 6, 6, 5, 7, // 오른쪽
8, 9, 10, 10, 9, 11, // 뒤쪽
12, 13, 14, 14, 13, 15, // 왼쪽
16, 17, 18, 18, 17, 19, // 상단
20, 21, 22, 22, 21, 23, // 하단
]);
</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-buffergeometry-cube-typedarrays.html"></iframe></div>
<a class="threejs_center" href="/manual/examples/custom-buffergeometry-cube-typedarrays.html" target="_blank">새 탭에서 보기</a>
</div>
<p></p>
<p>꼭지점의 일부를 수정하고 싶다면 형식화 배열을 사용하는 게 좋습니다.</p>
<p>뭔가 꼭지점을 동적으로 수정하는 예제를 찾아보려고 노력했는데, 찾기가 어렵네요.
그냥 구체를 만들고 구체의 각 사분면(quad)을 중심으로부터 안팎으로 움직여보겠습니다.</p>
<p>아래는 구체의 위치값과 인덱스를 생성하는 코드입니다. 사분면 내의 꼭지점은 서로
공유할 수 있지만, 사분면은 각각 움직여야 하므로 사분면끼리는 공유하지 않도록
합니다.</p>
<p>구체의 정점을 구하기 위해 좀 더 복잡한 방법을 쓸 수도 있지만, 귀찮으니 그냥
<a href="/docs/#api/ko/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> 3개를 계층 구조로 배열해 쓰겠습니다. 이 방식에 대해서는 <a href="optimize-lots-of-objects.html">많은 물체를
최적화하는 방법</a>에서 더 자세히 다루겠습니다.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeSpherePositions(segmentsAround, segmentsDown) {
const numVertices = segmentsAround * segmentsDown * 6;
const numComponents = 3;
const positions = new Float32Array(numVertices * numComponents);
const indices = [];
const longHelper = new THREE.Object3D();
const latHelper = new THREE.Object3D();
const pointHelper = new THREE.Object3D();
longHelper.add(latHelper);
latHelper.add(pointHelper);
pointHelper.position.z = 1;
const temp = new THREE.Vector3();
function getPoint(lat, long) {
latHelper.rotation.x = lat;
longHelper.rotation.y = long;
longHelper.updateMatrixWorld(true);
return pointHelper.getWorldPosition(temp).toArray();
}
let posNdx = 0;
let ndx = 0;
for (let down = 0; down &lt; segmentsDown; ++down) {
const v0 = down / segmentsDown;
const v1 = (down + 1) / segmentsDown;
const lat0 = (v0 - 0.5) * Math.PI;
const lat1 = (v1 - 0.5) * Math.PI;
for (let across = 0; across &lt; segmentsAround; ++across) {
const u0 = across / segmentsAround;
const u1 = (across + 1) / segmentsAround;
const long0 = u0 * Math.PI * 2;
const long1 = u1 * Math.PI * 2;
positions.set(getPoint(lat0, long0), posNdx); posNdx += numComponents;
positions.set(getPoint(lat1, long0), posNdx); posNdx += numComponents;
positions.set(getPoint(lat0, long1), posNdx); posNdx += numComponents;
positions.set(getPoint(lat1, long1), posNdx); posNdx += numComponents;
indices.push(
ndx, ndx + 1, ndx + 2,
ndx + 2, ndx + 1, ndx + 3,
);
ndx += 4;
}
}
return { positions, indices };
}
</pre>
<p>만든 함수를 다음처럼 호출합니다.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const segmentsAround = 24;
const segmentsDown = 16;
const { positions, indices } = makeSpherePositions(segmentsAround, segmentsDown);
</pre>
<p>여기서 반환된 위치값(positions)은 구체의 위치값을 기반으로 합니다. 이는 법선의 값(normals)과
같으니 법선을 따로 구할 필요 없이 위치값을 복사해서 쓰면 됩니다.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const normals = positions.slice();
</pre>
<p>다음으로 각 속성을 지정합니다.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const geometry = new THREE.BufferGeometry();
const positionNumComponents = 3;
const normalNumComponents = 3;
+const positionAttribute = new THREE.BufferAttribute(positions, positionNumComponents);
+positionAttribute.setUsage(THREE.DynamicDrawUsage);
geometry.setAttribute(
'position',
+ positionAttribute);
geometry.setAttribute(
'normal',
new THREE.BufferAttribute(normals, normalNumComponents));
geometry.setIndex(indices);
</pre>
<p>예제에서 차이점이 있는 부분을 표시해 두었습니다. 위치 속성에 참조값을 넘겨주었고, 이
속성이 동적이라고 명시했습니다. 이는 Three.js에게 해당 속성을 자주 변경될 수 있음을
알려주는 역할이죠.</p>
<p>이제 <code class="notranslate" translate="no">render</code> 함수에서 매 프레임마다 법선을 기준으로 위치값을 변경해줍니다.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const temp = new THREE.Vector3();
...
for (let i = 0; i &lt; positions.length; i += 3) {
const quad = (i / 12 | 0);
const ringId = quad / segmentsAround | 0;
const ringQuadId = quad % segmentsAround;
const ringU = ringQuadId / segmentsAround;
const angle = ringU * Math.PI * 2;
temp.fromArray(normals, i);
temp.multiplyScalar(THREE.MathUtils.lerp(1, 1.4, Math.sin(time + ringId + angle) * .5 + .5));
temp.toArray(positions, i);
}
positionAttribute.needsUpdate = true;
</pre>
<p>마지막으로 <code class="notranslate" translate="no">positionAttribute.needsUpdate</code> 속성을 활성화해 변화를 감지하도록 합니다.</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-buffergeometry-dynamic.html"></iframe></div>
<a class="threejs_center" href="/manual/examples/custom-buffergeometry-dynamic.html" target="_blank">새 탭에서 보기</a>
</div>
<p></p>
<p>이 글이 <a href="/docs/#api/ko/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>로 사용자 지정 geometry를 만들고, <a href="/docs/#api/ko/core/BufferAttribute"><code class="notranslate" translate="no">BufferAttribute</code></a>를 다루는
데 도움이 되었으면 좋겠습니다.</p>
<p><canvas id="c"></canvas></p>
<script type="module" src="../resources/threejs-custom-buffergeometry.js"></script>
</div>
</div>
</div>
<script src="../resources/prettify.js"></script>
<script src="../resources/lesson.js"></script>
</body></html>