ํ™ˆ three.js geometry ์‚ดํŽด๋ณด๊ธฐ
ํฌ์ŠคํŠธ
์ทจ์†Œ

three.js geometry ์‚ดํŽด๋ณด๊ธฐ

๐Ÿ’ป Geometry ๋ž€?

three.js ์—์„œ geometry ๋Š” 3์ฐจ์› ๊ณต๊ฐ„์—์„œ์˜ ์ขŒํ‘œ๋“ค์„ ์˜๋ฏธํ•˜๋Š” ์ •์ ๋“ค(vertices)๊ณผ ๊ทธ ์ •์ ๋“ค์„ ์—ฐ๊ฒฐํ•ด ๋งŒ๋“  ์‚ผ๊ฐํ˜• ๋ชจ์–‘์˜ ๋ฉด(face)๋“ค๋กœ ์ด๋ฃจ์–ด์ง„ ๋ชจ์–‘(shape) ์ž…๋‹ˆ๋‹ค.
๋ณดํ†ต geometry ๋Š” mesh ๋ฅผ ๊ตฌ์„ฑํ•˜์ง€๋งŒ, ์ ์œผ๋กœ ๋œ ๋„ํ˜•์„ ๊ตฌ์„ฑํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ํ•˜๋‚˜์˜ ์ •์ (vertex)์€ ์ขŒํ‘œ ์ •๋ณด๋ฟ๋งŒ ์•„๋‹ˆ๋ผ normal, color, uv ์™€ ๊ฐ™์€ ์ •๋ณด๋„ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ป ์›์‹œ ๋ชจ๋ธ(Primitive Model)

three.js ์—๋Š” ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ์›์‹œ๋ชจ๋ธ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์›์‹œ๋ชจ๋ธ๋“ค์€ ๋Ÿฐํƒ€์ž„์— ๋‹ค์–‘ํ•œ ์ธ์ž๋ฅผ ๋ฐ›์•„ ๋ชจ์–‘์„ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ชจ๋“  ์›์‹œ ํƒ€์ž…๋“ค์€ BufferGeometry ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์Šต๋‹ˆ๋‹ค.

๐Ÿ‘จโ€๐Ÿ’ป ์ข…๋ฅ˜

๋ฌธ์„œ ์ฐธ์กฐ

๐Ÿ‘จโ€๐Ÿ’ป BoxGeometry

์›์‹œ ๋ชจ๋ธ์—๋Š” ๋‹ค์–‘ํ•œ ์ธ์ž๋“ค์ด ์žˆ๋Š”๋ฐ, ์œก๋ฉด์ฒด๋ฅผ ์˜ˆ์‹œ๋กœ ์ธ์ž๋“ค์˜ ์˜๋ฏธ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

BoxGeometry(width: Float, height: Float, depth: Float, widthSegments: Integer, heightSegments: Integer, depthSegments: Integer)

1
const geometry = new THREE.BoxGeometry(1, 1, 1, 1, 1, 1);

BoxGeometry ๋Š” ์œ„์™€ ๊ฐ™์ด 6๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›๊ณ ์žˆ๋Š”๋ฐ, ๊ฐ ์ธ์ž๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์˜๋ฏธ๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค.

  • width
    ์œก๋ฉด์ฒด์˜ ๋„“์ด, ์ฆ‰ x ์ถ•์— ํ‰ํ–‰ํ•œ ๋ชจ์„œ๋ฆฌ์˜ ๊ธธ์ด์ž…๋‹ˆ๋‹ค.

  • height
    ์œก๋ฉด์ฒด์˜ ๋†’์ด, ์ฆ‰ y ์ถ•์— ํ‰ํ–‰ํ•œ ๋ชจ์„œ๋ฆฌ์˜ ๊ธธ์ด์ž…๋‹ˆ๋‹ค.

  • depth
    ์œก๋ฉด์ฒด์˜ ๊นŠ์ด, ์ฆ‰ z ์ถ•๊ณผ ํ‰ํ–‰ํ•œ ๋ชจ์„œ๋ฆฌ์˜ ๊ธธ์ด์ž…๋‹ˆ๋‹ค.

  • widthSegments
    x ์ถ•์„ ๋”ฐ๋ผ ๋ถ„ํ• ๋œ ์ง์‚ฌ๊ฐํ˜• ๋ฉด์˜ ์ˆ˜ ์ž…๋‹ˆ๋‹ค.

  • heightSegments
    y ์ถ•์„ ๋”ฐ๋ผ ๋ถ„ํ• ๋œ ์ง์‚ฌ๊ฐํ˜•์˜ ๋ฉด์˜ ์ˆ˜ ์ž…๋‹ˆ๋‹ค.

  • depthSegments
    z ์ถ•์„ ๋”ฐ๋ผ ๋ถ„ํ• ๋œ ์ง์‚ฌ๊ฐํ˜•์˜ ๋ฉด์˜ ์ˆ˜ ์ž…๋‹ˆ๋‹ค.

width-height-depth

์œ„ ์ด๋ฏธ์ง€๋Š” width, height, depth ์†์„ฑ์„ ๊ฐ๊ฐ 2๋กœ ์ฆ๊ฐ€์‹œํ‚จ ํ›„ ๋ Œ๋”๋ง๋œ BoxGeometry ์ž…๋‹ˆ๋‹ค.

segment

์œ„ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚จ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
// ...์ƒ๋žต...
const boxGeometry = new THREE.BoxGeometry(1, 1, 1, 1, 1, 1);
const material = new THREE.MeshBasicMaterial({
  color: 0xff0000,
  wireframe: true,
});
const mesh = new THREE.Mesh(boxGeometry, material);
// ...์ƒ๋žต...

๋ถ„ํ• ๋œ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด material ์— wireframe ์˜ต์…˜์„ true ์„ค์ •ํ•œ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

widthSegments ๋ฅผ 2 ๋กœ ์„ค์ •ํ•˜๊ฒŒ๋˜๋ฉด, x ์ถ• ๋ฐฉํ–ฅ(๋„“์ด)์œผ๋กœ ์‚ฌ๊ฐํ˜•์„ ์ด๋“ฑ๋ถ„ํ–ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. heightSegments ์™€ depthSegments ๋„ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

segment ๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฉด, ๊ณก์„  ํ‘œํ˜„์— ์œ ๋ฆฌํ•ด์ง‘๋‹ˆ๋‹ค. ํ‰๋ฉด์€ segment ์˜ ๊ฐฏ์ˆ˜์™€ ๋ฌด๊ณผํ•˜์ง€๋งŒ, ๊ตฌ์˜ ๊ฒฝ์šฐ segment ์˜ ๊ฐฏ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•  ์ˆ˜๋ก ์™„๋ฒฝํ•œ ๊ตฌ์— ๊ฐ€๊นŒ์›Œ ์ง‘๋‹ˆ๋‹ค.

๐Ÿ‘จโ€๐Ÿ’ป TextGeometry

TextGeometry ๋Š” ํ…์ŠคํŠธ๋ฅผ ํ•˜๋‚˜์˜ geometry ๋กœ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
TextGeometry ์˜ ๋ถ€๋ชจ ํด๋ž˜์Šค๋Š” BufferGeometry ๋ฅผ ์ƒ์†๋ฐ›๋Š” ExtrudeGeometry ์ด๋ฉฐ, ์ƒ์„ฑ์ž์— ํ‘œํ˜„ํ•  ํ…์ŠคํŠธ์™€ ์˜ต์…˜์„ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค.

๐Ÿ–Š ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ ํฐํŠธ

TextGeometry ๋Š” typeface.json ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
typeface.json ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Facetype.js ์™€ ๊ฐ™์€ ๋ณ€ํ™˜ ์‚ฌ์ดํŠธ๋ฅผ ์ด์šฉํ•ด ์ผ๋ฐ˜ ๊ธ€๊ผด์„ typeface.json ์œผ๋กœ ๋ณ€ํ™˜ ์‹œํ‚ค๊ฑฐ๋‚˜ three.js ์—์„œ ์ œ๊ณตํ•˜๋Š” built-in typeface.json ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

three.js ์˜ built-in typeface.json ๋“ค์€ /examples/fonts/ ํ•˜์œ„์— ์œ„์น˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ–Š ํฐํŠธ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

์‚ฌ์šฉํ•  typeface.json ์„ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์œ„ํ•ด์„œ๋Š” FontLoader ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { FontLoader } from "three/addons/loaders/FontLoader";

const fontLoader = new FontLoader();
fontLoader.load(
  "font-path",
  (font) => {
    console.log("font is loaded");
  },
  (xmlHttpRequest) => {
    console.log("font is loading");
  },
  () => {
    console.log("font cannot be loaded");
  }
);

์œ„ ์˜ˆ์‹œ๋Š” FontLoader ๋ฅผ ์ด์šฉํ•˜์—ฌ typeface.json ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.
์œ„ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด FontLoader ๊ฐ€ three/addons ๊ฒฝ๋กœ(three/examples/jsm)์— ์œ„์น˜ํ•˜๊ณ  ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ load ๋ฉ”์†Œ๋“œ๋Š” typeface.json ์˜ ๊ฒฝ๋กœ์™ธ์—๋„, onLoad, onProgress, onError ๋ฅผ ์ฝœ๋ฐฑํ•จ์ˆ˜๋กœ ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ถ”๊ฐ€๋กœ texture ์—์„œ ์‚ดํŽด๋ณด๊ฒ ์ง€๋งŒ, FontLoader ๋„ Loader ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›๋Š” ์ž์‹ ํด๋ž˜์Šค์ด๊ธฐ ๋•Œ๋ฌธ์— LoadingManager ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ–Š TextGeometry ์ƒ์„ฑํ•˜๊ธฐ

์•„๋ž˜๋Š” TextGeometry ์˜ ์ƒ์„ฑ์ž์˜ ์ธ์ž๋“ค๊ณผ, ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ TextGeometry ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

TextGeometry(text: String, { 
  font: THREE.Font, 
  size: Float, 
  height: Float, 
  curveSegments: Integer, 
  bevelEnabled: Boolean, 
  bevelThickness: Float,
  bevelSize: Float, 
  bevelOffset: Float, 
  bevelSegments: Integer
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { TextGeometry } from "three/addons/geometry/TextGeometry";

// ...์ƒ๋žต...
fontLoader.load("font-path", (font) => {
  const textGeometry = new THREE.Text("Hello", {
    font,
    size: 0.5,
    height: 0.2,
    curveSegments: 12,
    bevelEnabled: true,
    bevelThickness: 0.03,
    bevelSize: 0.02,
    bevelOffset: 0,
    bevelSegments: 5,
  });

  const material = new THREE.MeshBasicMaterial();
  const mesh = new THREE.Mesh(textGeometry, material);

  scene.add(mesh);
});
// ...์ƒ๋žต...

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚จ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. (์ฝ”๋“œ์—๋Š” ์ƒ๋žต๋˜์—ˆ์ง€๋งŒ AxesHelper ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.)

text-geometry-1

์ด์ œ TextGeometry ์˜ ์ƒ์„ฑ์ž ์ธ์ž์ค‘, ๋‘๋ฒˆ์งธ ๊ฐ์ฒด ์ธ์ž์˜ ์†์„ฑ๋“ค์˜ ์˜๋ฏธ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  • font
    TextGeometry ์—์„œ ์‚ฌ์šฉํ•  font ๋ฅผ ์„ค์ •ํ•˜๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค.

  • size
    font ์˜ ํฌ๊ธฐ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ 100 ์ž…๋‹ˆ๋‹ค.

  • height
    TextGeometry ์˜ ๋‘๊ป˜๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ 50 ์ž…๋‹ˆ๋‹ค. text-geometry-height-1

    ์œ„ ์ด๋ฏธ์ง€๋Š” material ์˜ wireframe ์˜ต์…˜์„ true ์„ค์ •ํ•œ ๋‹ค์Œ height ์˜ ํฌ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•œ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค.

  • curveSegments
    ํ•˜๋‚˜์˜ ๊ณก์„ ์„ ๊ตฌ์„ฑํ•˜๋Š” ์ ์˜ ์ˆ˜๋ฅผ ์„ค์ •ํ•˜๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ 12 ์ž…๋‹ˆ๋‹ค.
    ๊ฐ’์ด ํด ์ˆ˜๋ก ๊ณก์„ ์ƒ์˜ segment ์ˆ˜๊ฐ€ ๋งŽ์•„์ง€๊ณ , ๋ถ€๋“œ๋Ÿฌ์›Œ ์ง‘๋‹ˆ๋‹ค.
    text-geometry-curve-segment-1

  • bevelEnabled
    beveled edge ์‚ฌ์šฉ ์—ฌ๋ถ€๋ฅผ ์„ค์ •ํ•˜๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ false ์ž…๋‹ˆ๋‹ค.

    ๐Ÿ–Š beveled edge ๋ž€? beveled edge ๋ž€ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฌผ์ฒด์˜ ๊ฐ€์žฅ์ž๋ฆฌ๋‚˜ ๋ชจ์„œ๋ฆฌ๋ฅผ ํ‰๋ฉด์œผ๋กœ ๋‹ค๋“ฌ์€ ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
    beveled-edge

    text-geometry-bevel-enabled-1

    bevelEnabled ๋ฅผ true ๋กœ ์„ค์ •ํ•  ์‹œ ํ…์ŠคํŠธ์˜ ๋ชจ์„œ๋ฆฌ ๋ถ€๋ถ„์ด ๋‘ฅ๊ธ€๊ฒŒ ์ฒ˜๋ฆฌ๋จ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • bevelThickness
    bevel ์˜ ๋‘๊ป˜๋ฅผ ์„ค์ •ํ•˜๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ 10 ์ž…๋‹ˆ๋‹ค.
    text-geometry-bevel-thickness-1

  • bevelSize
    ํ…์ŠคํŠธ์˜ ์œค๊ณฝ(outline)์œผ๋กœ ๋ถ€ํ„ฐ bevel ์ด ์–ผ๋งˆ๋‚˜ ๋–จ์–ด์ ธ ์žˆ๋Š”์ง€๋ฅผ ์„ค์ •ํ•˜๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ 8 ์ž…๋‹ˆ๋‹ค.

  • bevelOffset
    ํ…์ŠคํŠธ์˜ ์œค๊ณฝ์ด ์‹œ์ž‘ํ•˜๋Š” ์œ„์น˜๋ฅผ ์„ค์ •ํ•˜๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ 0 ์ž…๋‹ˆ๋‹ค.
    text-geometry-bevel-offset-1
    bevelOffset ์„ 0.01 ์„ค์ •ํ–ˆ์„ ๋•Œ, ํ…์ŠคํŠธ์˜ ์œค๊ณฝ์„ ์ด ์›์ ์—์„œ ๋–จ์–ด์ง์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • bevelSegments
    bevel edge ์˜ segment ์ˆ˜๋ฅผ ์„ค์ •ํ•˜๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ 3 ์ด๋ฉฐ, ๋†’์„ ์ˆ˜๋ก ๋ถ€๋“œ๋Ÿฌ์šด bevel edge ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ–Š ๊ฐ€์šด๋ฐ ์ •๋ ฌํ•˜๊ธฐ

BufferGeometry ๋ฅผ ์ƒ์†๋ฐ›๋Š” ๊ฐ์ฒด๋“ค์€ center ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ€์šด๋ฐ๋กœ ์œ„์น˜๋ฅผ ์˜ฎ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
center ๋ฉ”์†Œ๋“œ๋Š” bounding box ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ geometry ๋ฅผ ๊ฐ€์šด๋ฐ๋กœ ์ด๋™์‹œํ‚ค๋Š”๋ฐ, ์ด๋™์‹œํ‚ค๋Š” ๊ณผ์ •์„ ์ง์ ‘ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

bounding ์ด๋ž€?

bounding ์€ Geometry ๋ฅผ ๋‘˜๋Ÿฌ์‹ธ๊ณ  ์žˆ๋Š” ๊ฒฝ๊ณ„๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ํฌ๊ธฐ ์ธก์ •, ์ถฉ๋Œ๊ฐ์ง€ ๋“ฑ ๋‹ค์–‘ํ•œ ์šฉ๋„๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘˜๋Ÿฌ ์‹ธ๊ณ  ์žˆ๋Š” ๊ฒฝ๊ณ„์˜ ๋ชจ์–‘์˜ ๋”ฐ๋ผ, bounding box, bounding sphere ๋“ฑ์œผ๋กœ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. computeBoundingBox ๋กœ bounding box ๊ตฌํ•˜๊ธฐ
    Bounding box ๋Š” ๊ธฐ๋ณธ์œผ๋กœ ๊ณ„์‚ฐ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ณ„์‚ฐํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” bounding sphere ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€ ์ž…๋‹ˆ๋‹ค. (computeBoundingSphere)

  2. boundingBox ์†์„ฑ ์ด์šฉํ•˜๊ธฐ

1
2
textGeometry.computeBoundBox();
console.log(textGeometry.boundingBox);

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

bound-box-1

max ์™€ min ๋‘ ๊ฐ€์ง€ ์†์„ฑ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ํ•ด๋‹น ์ขŒํ‘œ๋Š” bounding box ์˜ ์ตœ๋Œ€, ์ตœ์†Œ ์ขŒํ‘œ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
๋˜ํ•œ min ์†์„ฑ์˜ Vector3 ๊ฐ€ 0 ์ด ์•„๋‹˜์„ ์•Œ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋Š” bevelSize ์™€ bevelThickness ๊ฐ’ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์œ„์˜ ๋‚ด์šฉ๋“ค์„ ๋ฐ”ํƒ•์œผ๋กœ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
// ...์ƒ๋žต...
textGeometry.translate(
  -(textGeometry.boundingBox.max.x - bevelSize) * 0.5,
  -(textGeometry.boundingBox.max.y - bevelSize) * 0.5,
  -(textGeometry.boundingBox.max.z - bevelThickness) * 0.5
);
// ...์ƒ๋žต...

๐Ÿ’ป ์‚ฌ์šฉ์ž ์ง€์ • BufferGeometry

BufferGeometry๋Š” three.js ๋‚ด์˜ ๋ชจ๋“  geometry๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค (์›์‹œ ๋ชจ๋ธ์˜ ๋ถ€๋ชจ ํด๋ž˜์Šค). ๋˜ํ•œ BufferAttribute ์†์„ฑ๋“ค์˜ ์ง‘ํ•ฉ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. BufferGeometry ๋กœ ์ง์ ‘ geometry ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ •์ ๋“ค์„ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ •์ ๋“ค์„ ์ถ”๊ฐ€ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐฐ์—ด์„ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š”๋ฐ, ์ด๋•Œ Float32Array ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

BufferGeometry ๋ฅผ ์ด์šฉํ•ด์„œ ์œก๋ฉด์ฒด๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ...์ƒ๋žต...
const vertices = [
  -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1,
  -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1,
  1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1,
  -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1,
  1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1,
];

const myBoxPositionAttribute = new THREE.BufferAttribute(
  new Float32Array(vertices),
  3
);
const boxGeometry = new THREE.BufferGeometry();
boxGeometry.setAttribute("position", myBoxPositionAttribute);

const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });

const mesh = new THREE.Mesh(boxGeometry, material);
// ...์ƒ๋žต...

์ •์œก๋ฉด์ฒด๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด vertices ๋ฐฐ์—ด์— 108๊ฐœ์˜ ์›์†Œ๊ฐ€ ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค. ์ •์œก๋ฉด์ฒด์˜ ๋ฉด์€ ๋ชจ๋‘ 6๊ฐœ์ด๊ณ , ํ•œ ๋ฉด์ด 2๊ฐœ์˜ ์‚ผ๊ฐํ˜•์œผ๋กœ ๋˜์–ด์žˆ์œผ๋‹ˆ ์ด 12๊ฐœ์˜ ์‚ผ๊ฐํ˜•์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. 12๊ฐœ์˜ ์‚ผ๊ฐํ˜• ๊ผญ์ง€์ ์€ ๋ชจ๋‘ 36๊ฐœ์ด๊ณ  ๊ฐ ์ ์€ (x, y, z) ์ขŒํ‘œ์˜ ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ์–ด 108๊ฐœ(6 X 2 X 3 X 3)์˜ ์›์†Œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

์ขŒํ‘œ์˜ ์ •๋ณด๋ฅผ ๋‹ด์€ vertices ๋ฐฐ์—ด์„ Float32Array ๋กœ ๋ณ€๊ฒฝ์‹œํ‚จ ํ›„, ์ขŒํ‘œ์™€ ๊ด€๋ จ๋œ BufferAttribute ์„ ์ƒ์„ฑํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋•Œ ๋‘๋ฒˆ์งธ ์ธ์ž๋กœ, 3 ์„ ์ „๋‹ฌํ–ˆ๋Š”๋ฐ, ์ด๋Š” ๋ฐฑํ„ฐ์˜ ๊ฐฏ์ˆ˜๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์œ„์˜ vertices ์˜ ๊ฒฝ์šฐ ์ขŒํ‘œ์— 3๊ฐœ์˜ ๋ฐฑํ„ฐ (x, y, z) ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ๋•Œ๋ฌธ์— 3 ์„ ์ „๋‹ฌํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

position-attribute-1

์ขŒํ‘œ์˜ BufferAttribute ๋ฅผ ์ƒ์„ฑํ•œ ํ›„, setAttribute ๋ฅผ ์ด์šฉํ•ด BufferGeometry ์— ์ถ”๊ฐ€๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โ—๏ธ BufferGeometry ์˜ attribute ์— ์ ‘๊ทผํ• ๋•Œ๋Š” ์ง์ ‘ ์ ‘๊ทผ๋ณด๋‹ค๋Š” getter ์™€ setter ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋น ๋ฆ…๋‹ˆ๋‹ค.

๐Ÿ‘จโ€๐Ÿ’ป Vertex์— ๋‹ค๋ฅธ ์ •๋ณด ์ €์žฅํ•˜๊ธฐ

์œ„์—์„œ ์–ธ๊ธ‰ํ•œ๋Œ€๋กœ, ์ •์ ์—๋Š” uv, normal, color ์™€ ๊ฐ™์€ ์ •๋ณด๋„ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// ...์ƒ๋žต...
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] },
];

const positions = [];
const normals = [];
const uvs = [];

for (const vertex of vertices) {
  positions.push(...vertex.pos);
  normals.push(...vertex.norm);
  uvs.push(...vertex.uv);
}

const geometry = new THREE.BufferGeometry();

geometry.setAttribute(
  "position",
  new BufferAttribute(new Float32Array(positions), 3)
);
geometry.setAttribute(
  "normal",
  new BufferAttribute(new Float32Array(normals), 3)
);
geometry.setAttribute("uv", new BufferAttribute(new Float32Array(uvs), 2));
// ...์ƒ๋žต...

threejs-attributes-1

์œ„ ์ฝ”๋“œ๋Š”, ์ขŒํ‘œ์˜ ์œ„์น˜ ์™ธ์˜ ์ •๋ณด๋ฅผ BufferAttribute ๋กœ ์ถ”๊ฐ€ํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

๐Ÿ’ป index

BufferGeometry ๋กœ geometry ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ vertices ๋ฐฐ์—ด์—๋Š” ์ค‘๋ณต๋˜๋Š” ์ •์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
์œ„์—์„œ ๋งŒ๋“  ์ •์œก๋ฉด์ฒด๋ฅผ ์‚ดํŽด๋ณด๋ฉด, ํ•œ ๋ฉด์€ ๋‘๊ฐœ์˜ ์‚ผ๊ฐํ˜•์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ๊ณ  ์ด 6๊ฐœ์˜ ์ •์ ์ด ์ƒ๊น๋‹ˆ๋‹ค. ์ด์ค‘์—์„œ 2๊ฐœ์˜ ์ •์ ์ด ์ค‘๋ณต๋ฉ๋‹ˆ๋‹ค. ๊ณ„์‚ฐ์˜ ํšจ์œจ์„ฑ์„ ์œ„ํ•ด ์ค‘๋ณต๋˜๋Š” ์ •์ ๋“ค์„ ์ œ๊ฑฐํ•˜๊ณ  ๊ฐ, ์ขŒํ‘œ์˜ ์ธ๋ฑ์Šค๋ฅผ ํ™•์šฉํ•ด geometry ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// ...์ƒ๋žต...
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: [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] }, // 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] }, // 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] }, // 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] }, // 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] }, // 22
  { pos: [-1, -1, -1], norm: [0, -1, 0], uv: [1, 1] }, // 23
];

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, // ํ•˜๋‹จ
]);

// ...์ƒ๋žต...

๐Ÿ“” ์ฐธ๊ณ ์ž๋ฃŒ

Primitives
Custom BufferGeometry
Three.js: Geometries and materials
Bevel

์ด ๊ธฐ์‚ฌ๋Š” ์ €์ž‘๊ถŒ์ž์˜ CC BY 4.0 ๋ผ์ด์„ผ์Šค๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

three.js ๋ฐ˜์‘ํ˜• ๋””์ž์ธ

three.js texture ์‚ดํŽด๋ณด๊ธฐ