์์ฑ์๊ฐ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์จ์์ ํ์ ์คํฌ๋ฆฝํธ์ ๋ํด ๋ฏธ์ํ์ฌ ํ์ ์คํฌ๋ฆฝํธ์ ๋ฏธํกํ ๋ถ๋ถ์ด ํฌํจ๋์ด ์์ต๋๋ค.
๐ fiber / drei
๋ฆฌ์กํธ์์ three.js๋ฅผ ์ข ๋ ํธํ๊ฒ ์ฌ์ฉํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ๋๋ค.
์์ฑ์๋ ์ด ๋๊ฐ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒ์๊ธ์ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค. ๐
https://docs.pmnd.rs/react-three-fiber/getting-started/introduction
https://github.com/pmndrs/drei
โฌ๏ธ install
๋ฆฌ์กํธ(ํ์ ์คํฌ๋ฆฝํธ) ํ๋ก์ ํธ๋ฅผ ๋ง๋ญ๋๋ค.
$ npx create-react-app <FolderName> --template typescript
์ดํ, three.js๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํฉ๋๋ค.
# three.js ์ค์น
$ npm install --save three
# fiber ์ค์น
$ npm install three @types/three @react-three/fiber
# drei ์ค์น
$ npm install @react-three/drei
๐ Scene ์์ฑ
3D ๊ณต๊ฐ์ ํธ์งํ๊ธฐ ์ํ Scene์ ๋ง๋ค์ด์ค๋๋ค.
์์ฑ์๋ ํ๋ก์ ํธ์ ๐Editor๋ฅผ ์์ฑํ์ฌ ํด๋น ํด๋ ์์ ๐ EditorScene.tsx๋ฅผ ์์ฑํ๊ฒ ์ต๋๋ค.
๐ Scene
๐ Editor/ ๐ EditorScene.tsx
import { useState } from "react";
import { Canvas } from "@react-three/fiber";
import styles from "./CSS/EditorScene.module.css";
export const EditorScene = () => {
return (
<div>
<Canvas>
</Canvas>
</div>
);
};
ํด๋น ์ปดํฌ๋ํธ๋ฅผ ๐ App.js์ ๋ถ๋ฌ์จ๋ค.
$ npm run start
๊ทธ๋ฆฌ๊ณ ํ๋ก์ ํธ๋ฅผ ์คํํ๋ฉด
ํ๋ฉด์ด ๋ํ๋๋๋ฐ ์๋ฌด๊ฒ๋ ๋ณด์ด์ง ์๋๋ค.
์ข ๋ 3D ๊ณต๊ฐ์ ๋๋์ ๋ผ ์ ์๋๋ก ์ฝ๋๋ฅผ ์ถ๊ฐํด์ฃผ์.
๋ฐฐ๊ฒฝ์์ ๋ณ๊ฒฝํ๊ณ ๊ทธ๋ฆฌ๋๋ฅผ ์ถ๊ฐํด๋ณด์.
์ด๋, Scene์ ๋ถ๋ชจ ์ปจํ ์ด๋์ ๋์ด๋ฅผ ๊ทธ๋๋ก ์์ ๋ฐ๋๋ค.
๐ Editor / ๐ CSS / ๐ EditorScene.module.css
.editorFrame {
width: 100vw;
height: 100vh;
background-color: #2e2e2e;
}
โฐ Grid
๐ Editor / ๐ EditorScene.tsx
import { useState } from "react";
import { Grid } from "@react-three/drei";
import { Canvas } from "@react-three/fiber";
import styles from "./CSS/EditorScene.module.css";
export const EditorScene = () => {
return (
<div className={styles.editorFrame}>
<Canvas>
<Grid/>
</Canvas>
</div>
);
};
๋ค์ ํ๋ก์ ํธ๋ฅผ ์๋ก๊ณ ์นจ ํ๋ค๋ฉด,
๐์ง์~
์๋ฌด๊ฒ๋ ์ ๋ณด์ธ๋ค.
์ฌ์ค์ ์ถ๊ฐ๋์ด ์๋ ์ํ์ด๋ฉฐ, ํ์ฌ ๊ธฐ๋ณธ ๊ฐ๋๊ฐ ์ ๋ฉด์ด๋ผ์ ํด๋น ๊ทธ๋ฆฌ๋ ๋ผ์ธ์ด ์ ๋ณด์ด๋ ๊ฒ์ด๋ค.
Scene์์ ์นด๋ฉ๋ผ์ ๊ธฐ๋ณธ ์์น๊ฐ์ ๋ณ๊ฒฝํด์ฃผ์.
๐ฅ Camera
๐ Editor / ๐ EditorScene.tsx
import { useState } from "react";
import { Grid } from "@react-three/drei";
import { Canvas } from "@react-three/fiber";
import styles from "./CSS/EditorScene.module.css";
export const EditorScene = () => {
return (
<div className={styles.editorFrame}>
<Canvas
camera={{
aspect: 1280 / 720,
fov: 50,
near: 0.1,
far: 1000,
position: [2.5, 1.2, 3.5],
}}
>
<Grid />
</Canvas>
</div>
);
};
๊ทธ๋ฆฌ๋๊ฐ ๋ํ๋ฌ์ง๋ง ๋ง์ด ์์ฝ๋ค.
three-drei์ ๋ค์ด๊ฐ๋ฉด ๊ทธ๋ฆฌ๋์ ๊ธฐ๋ณธ ์ค์ ๊ฐ์ด ๋์จ๋ค.
์ค์ ํด์ฃผ์.
๐ Editor / ๐ EditorScene.tsx
import { useState } from "react";
import { Grid } from "@react-three/drei";
import { Canvas } from "@react-three/fiber";
import styles from "./CSS/EditorScene.module.css";
export const EditorScene = () => {
const [gridConfig, setGridConfig] = useState({
cellSize: 0.25,
/** Cell thickness, default: 0.5 */
// cellThickness: "white",
/** Cell color, default: black */
cellColor: "#afafaf",
/** Section size, default: 1 */
sectionSize: 0.5,
/** Section thickness, default: 1 */
// sectionThickness?: number
/** Section color, default: #2080ff */
sectionColor: "#afafaf",
/** Follow camera, default: false */
// followCamera: true,
/** Display the grid infinitely, default: false */
infiniteGrid: true,
/** Fade distance, default: 100 */
fadeDistance: 30,
/** Fade strength, default: 1 */
fadeStrength: 3,
});
return (
<div className={styles.editorFrame}>
<Canvas
camera={{
aspect: 1280 / 720,
fov: 50,
near: 0.1,
far: 1000,
position: [2.5, 1.2, 3.5],
}}
>
<Grid {...gridConfig} />
</Canvas>
</div>
);
};
๋ค์ ์ฝ๋๋ฅผ ์คํํ๋ฉด,
์ด์ ๋ญ๊ฐ 3D ๊ณต๊ฐ ์ฒ๋ผ ๋ณด์ธ๋ค. ๐๐ป๐๐ป๐๐ป๐๐ป๐๐ป
๋ง์ฐ์ค๋ฅผ ๋๋๊ทธํด๋ ํด๋น ๊ณต๊ฐ์ ๋๋ฌ๋ณผ ์๊ฐ ์๋ค.
Controller๋ฅผ ์ถ๊ฐํ์ฌ ๊ณต๊ฐ์ ์ปจํธ๋กคํด๋ณด์.
๐น๏ธ OrbitControls
๐ Editor / ๐ EditorScene.tsx
import { useState } from "react";
import { Grid, OrbitControls } from "@react-three/drei";
import { Canvas } from "@react-three/fiber";
import styles from "./CSS/EditorScene.module.css";
export const EditorScene = () => {
const [gridConfig, setGridConfig] = useState({
cellSize: 0.25,
/** Cell thickness, default: 0.5 */
// cellThickness: "white",
/** Cell color, default: black */
cellColor: "#afafaf",
/** Section size, default: 1 */
sectionSize: 0.5,
/** Section thickness, default: 1 */
// sectionThickness?: number
/** Section color, default: #2080ff */
sectionColor: "#afafaf",
/** Follow camera, default: false */
// followCamera: true,
/** Display the grid infinitely, default: false */
infiniteGrid: true,
/** Fade distance, default: 100 */
fadeDistance: 30,
/** Fade strength, default: 1 */
fadeStrength: 3,
});
return (
<div className={styles.editorFrame}>
<Canvas
camera={{
aspect: 1280 / 720,
fov: 50,
near: 0.1,
far: 1000,
position: [2.5, 1, 3.5],
}}
>
<OrbitControls/>
<Grid {...gridConfig} />
</Canvas>
</div>
);
};
๊ทธ๋ผ ์ด์ ๊ธฐ๋ณธ ์นด๋ฉ๋ผ ๊ฐ๋๋ฅผ ๋ง์ฐ์ค๋ก ์กฐ์ข ํ ์ ์๋ค.