Skip to content

ckrb63/threeJs-react

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

9 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Three.js ๋ž€?

์›น์—์„œ 3d๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

Three.js ๋ฅผ ์‚ฌ์šฉํ•œ ํŽ˜์ด์ง€ https://github.com/home ๊นƒํ—ˆ๋ธŒ https://eyes.nasa.gov/apps/mars2020/#/home ๋‚˜์‚ฌ https://www.midwam.com/en midwam

์›น์—์„œ 3d๋ฅผ ๊ตฌํ˜„ํ–ˆ์œผ๋ฉด ๋ชจ๋‘ Three.js๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒƒ

https://threejs.org/ ์—์„œ ๋”๋งŽ์€ three.js๋ฅผ ์‚ฌ์šฉํ•œ ํŽ˜์ด์ง€๋“ค์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Settings

  1. ๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ create-react-app npx create-react-app 3d-app

  2. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ npm install three Three.js npm install @react-three/fiber React์—์„œ Three.js๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ Lib npm install use-cannon ๋ฌผ๋ฆฌ์—”์ง„ Lib

Scene ๋งŒ๋“ค๊ธฐ

1. ๊ธฐ๋ณธ Three.js๋กœ scene ๋งŒ๋“ค๊ธฐ

import * as THREE from "three";

3์š”์†Œ ์ƒ์„ฑ

three.js์—์„œ 3d ์˜ค๋ธŒ์ ํŠธ๋ฅผ ํ‘œ์‹œํ•˜๋ ค๋ฉด 3๊ฐ€์ง€ ์š”์†Œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  • scene
  • camera
  • renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
const renderer = new THREE.WebGLRenderer();

camera์˜ ์ƒ์„ฑ์ž์— ๋“ค์–ด๊ฐ€๋Š” 4๊ฐ€์ง€ ์†์„ฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • FOV(์‹œ์•ผ๊ฐ) : ํ•ด๋‹น ์‹œ์ ์˜ ํ™”๋ฉด์ด ๋ณด์—ฌ์ง€๋Š” ์ •๋„. ๊ฐ๋„๋กœ ๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • aspect ratio(์ข…ํšก๋น„) : ๋Œ€๋ถ€๋ถ„ ์š”์†Œ์˜ ๋†’์ด์™€ ๋„ˆ๋น„์— ๋งž์ถ”์–ด ํ‘œ์‹œํ•˜๊ณ  ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์™€์ด๋“œ ์Šคํฌ๋ฆฐ์— ์˜›๋‚  ์˜ํ™”๋ฅผ ํŠธ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ด๋ฏธ์ง€๊ฐ€ ํ‹€์–ด์ง‘๋‹ˆ๋‹ค.
  • near : ์ด ๊ฐ’๋ณด๋‹ค ๊ฐ€๊นŒ์ด ์žˆ๋Š” ์˜ค๋ธŒ์ ํŠธ๋Š” ๋ Œ๋”๋ง ๋˜์ง€ ์•Š์Œ
  • far : ์ด ๊ฐ’๋ณด๋‹ค ๋ฉ€๋ฆฌ ์žˆ๋Š” ์˜ค๋ธŒ์ ํŠธ๋Š” ๋ Œ๋”๋ง ๋˜์ง€ ์•Š์Œ

renderer ์ถ”๊ฐ€

renderer.setSize(window.innerWidth, window.innerHeight);
document.body.innerHTML = "";
document.body.appendChild(renderer.domElement);

๋‹ค์Œ์œผ๋กœ ๋ Œ๋”๋ง ํ•  ๊ณณ์˜ ํฌ๊ธฐ๋ฅผ ์„ค์ •ํ•ด์ฃผ๊ณ  HTML ๋ฌธ์„œ์— ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋‹จ ๋†’์ด์™€ ๋„ˆ๋น„๋ฅผ ๊ฐ๊ฐ ์œˆ๋„์šฐ์˜ ํฌ๊ธฐ๋กœ ์„ค์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.document.body.innerHTML = ""๋Š” renderer๋งŒ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค. renderer๋Š” <canvas> ์—˜๋ฆฌ๋จผํŠธ๋กœ HTML ๋ฌธ์„œ์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.

Cube ์ƒ์„ฑ

const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({
  color: "blue"
});

camera.position.z = 5;
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

Three.js ์—์„œ 3d ์˜ค๋ธŒ์ ํŠธ๋Š” mesh๋ผ ๋ถ€๋ฆ…๋‹ˆ๋‹ค. mesh๋Š” 2๊ฐ€์ง€ ์š”์†Œ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

  • geometry : ๊ธฐํ•˜ํ•™์  ํ˜•ํƒœ, ๋ผˆ๋Œ€๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๋ถ€๋ถ„
  • material : ์งˆ๊ฐ, ์ƒ‰, ๋ฐ˜์‚ฌ์œจ ๋“ฑ ๋ฌผ์ฒด์˜ ํ‘œ๋ฉด

2๊ฐ€์ง€ ์†์„ฑ์œผ๋กœ cube๋ผ๋Š” mesh๋ฅผ ๋งŒ๋“ค๊ณ  scene์— ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ธฐ๋ณธ ์„ค์ •์ƒ scene.add()๋กœ ๋ฌผ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด (0,0,0)์˜ ์œ„์น˜๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด camera์™€ cube๊ฐ€ ๋™์ผํ•œ ์œ„์น˜์— ๊ฒน์ณ์„œ ์ œ๋Œ€๋กœ ๋ณด์ด์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ camera์˜ ์œ„์น˜๋ฅผ ์•ฝ๊ฐ„ ๋ณ€๊ฒฝํ•˜์˜€์Šต๋‹ˆ๋‹ค.

scene rendering

์•„์ง ํ™”๋ฉด์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋‚˜์˜ค์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์•„๋ฌด๊ฒƒ๋„ ๋ Œ๋”๋งํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ํ™”๋ฉด์— cube๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  ํšŒ์ „์‹œํ‚ค๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
  }
  animate();

1์ดˆ์— 60๋ฒˆ ๋ Œ๋”๋ง(60fps) ํ•  ๊ฒƒ์ด๊ณ  ๊ทธ๋Ÿด ๋•Œ๋งˆ๋‹ค x ๋ฐฉํ–ฅ์œผ๋กœ 0.01, y ๋ฐฉํ–ฅ์œผ๋กœ 0.01๋งŒํผ ํšŒ์ „ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ฐฝ ํฌ๊ธฐ ๋ณ€๊ฒฝ ์ด์Šˆ

์ด๋ฏธ ๋ฉ‹์ง€๊ฒŒ cube๊ฐ€ ๋ Œ๋”๋ง๋˜๊ณ  ํšŒ์ „ํ•˜๊ณ  ์žˆ๊ฒ ์ง€๋งŒ ์ฐฝ ํฌ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ–ˆ์„ ๋•Œ ๋น„์œจ์ด ๋ญ‰๊ฒŒ์ง€๋Š”๊ฒŒ ๋ณด์ž…๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด renderer์™€ camera์˜ ์„ค์ •์ด ์ฒ˜์Œ ์ฐฝ์„ ์ผฐ์„ ๋•Œ์˜ ์œˆ๋„์šฐ ์‚ฌ์ด์ฆˆ๋กœ ๊ณ ์ •๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. addEventListener๋ฅผ ํ†ตํ•ด ์ฐฝ์ด resize๋  ๋•Œ๋งˆ๋‹ค renderer์™€ camera์˜ ์„ค์ •์„ ๋ฐ”๊ฟ”์ค์‹œ๋‹ค.

window.addEventListener("resize", () => {
  renderer.setSize(window.innerWidth, window.innerHeight);
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
});

์ „์ฒด์ฝ”๋“œ

import * as THREE from "three";
import "./App.css";

function App() {
  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  const renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.innerHTML = "";
  document.body.appendChild(renderer.domElement);

  const geometry = new THREE.BoxGeometry();
  const material = new THREE.MeshBasicMaterial({
    color: "blue"
  });

  camera.position.z = 5;
  const cube = new THREE.Mesh(geometry, material);
  scene.add(cube);

  function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
  }

  animate();

  window.addEventListener("resize", () => {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
  });

  return null;
}

export default App;

2. react-three/fiber๋กœ shene ๋งŒ๋“ค๊ธฐ

์‚ฌ์‹ค @react-three/fiber๋ฅผ ํ™œ์šฉํ•˜๋ฉด ํ›จ์”ฌ ์‰ฝ๊ฒŒ ๊ฐ„๋‹จํ•œ scene์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Import

import { Canvas, useFrame } from "@react-three/fiber";

Canvas ์ƒ์„ฑ

return (
<div style={{ width: "100vw", height: "100vh" }}>
      <Canvas style={{ background: "black" }}>
        <Box />
      </Canvas>
</div>
);

Canvas๋ฅผ ๋งŒ๋“ค๋ฉด scene, camera, renderer๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“คํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. Canvas์˜ ์Šคํƒ€์ผ์€ ๋‹ค๋ฅธ HTML ํƒœ๊ทธ๋“ค ์ฒ˜๋Ÿผ style์†์„ฑ์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Canvas์•ˆ์— ๋ Œ๋”๋ง๋  Box๋Š” ๋”ฐ๋กœ ์ปดํฌ๋„ŒํŠธ๋กœ ๋นผ์„œ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

Box ์ƒ์„ฑ

const Box = () => {
  const ref = useRef();
  useFrame((state) => {
    ref.current.rotation.x += 0.01;
    ref.current.rotation.y += 0.01;
  });
  return (
    <mesh ref={ref}>
      <boxBufferGeometry />
      <meshBasicMaterial color="blue" />
    </mesh>
  );
};

Canvas์•ˆ์—๋Š” mesh๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. mesh๋Š” ์•ˆ์— geometry, material ์†์„ฑ์„ ๊ฐ€์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค. boxBufferGeometry, meshBasicMaterial๋Š” ๊ฐ๊ฐ presetting๋œ ๊ธฐ๋ณธ ์ƒ์ž geometry, ๊ธฐ๋ณธ material ์ž…๋‹ˆ๋‹ค. mesh์— useRef๋ฅผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ๋™์ž‘ํ•˜๋Š” useFrame ํ›…์—์„œ ref๋ฅผ ์กฐ์ž‘ํ•ด mesh๋ฅผ ํšŒ์ „์‹œํ‚ต๋‹ˆ๋‹ค.

์ „์ฒด ์ฝ”๋“œ

import { Canvas, useFrame } from "@react-three/fiber";
import { useRef } from "react";

const Box = () => {
  const ref = useRef();
  useFrame((state) => {
    ref.current.rotation.x += 0.01;
    ref.current.rotation.y += 0.01;
  });
  return (
    <mesh ref={ref}>
      <boxBufferGeometry />
      <meshBasicMaterial color="blue" />
    </mesh>
  );
};

function App() {
  return (
    <div style={{ width: "100vw", height: "100vh" }}>
      <Canvas style={{ background: "black" }}>
        <Box />
      </Canvas>
    </div>
  );
}

export default App;

๊ธฐ๋ณธ three.js๋งŒ ์‚ฌ์šฉํ•ด์„œ scene์„ ๋งŒ๋“ค ๋•Œ๋ณด๋‹ค ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ์งง๊ณ  ๊ฐ„๋‹จํ•ด์ง„ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ ๋‚ด์šฉ๋“ค์€ ์ „๋ถ€ @react-three/fiber๋ฅผ ํ™œ์šฉํ•œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

Axes Helper, Orbit Controller

Axes Helper

Axex Helper๋Š” three.js์—์„œ x, y, z์ถ•์„ ๋‚˜ํƒ€๋‚ด ์ฃผ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. canvas์•ˆ์— axesHelper๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

<Canvas style={{ background: "black" }} camera={{ position: [3, 3, 3] }}>
        <Box />
        <axesHelper args={[5]} />
</Canvas>

camera={{ position: [3, 3, 3] }} ๊ธฐ์กด ์นด๋ฉ”๋ผ ์œ„์น˜๊ฐ€ [0,0,5]์—ฌ์„œ x, y์ถ• ๋ฐ–์— ์•ˆ๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์— ์นด๋ฉ”๋ผ์˜ ์œ„์น˜๋ฅผ ๋ฐ”๊ฟ”์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค axesHelper์˜ args๋Š” ์‚ฌ์ด์ฆˆ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

Orbit Controls

Orbit Controls๋Š” ์นด๋ฉ”๋ผ(ํ™”๋ฉด)์˜ ๊ฐ๋„๋ฅผ ๋Œ๋ฆฌ๊ฑฐ๋‚˜ ํ™•๋Œ€ํ•˜๊ฑฐ๋‚˜ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋Š” ์ปจํŠธ๋กค๋Ÿฌ ์ž…๋‹ˆ๋‹ค.

  • ํšŒ์ „ : ์ขŒํด๋ฆญ ํ›„ ๋“œ๋ž˜๊ทธ
  • ํ™•๋Œ€/์ถ•์†Œ : ์Šคํฌ๋กค
  • ์ด๋™ : ์šฐํฌ๋ฆญ ํ›„ ๋“œ๋ž˜๊ทธ

import, extend

import { Canvas, useFrame, extend, useThree } from "@react-three/fiber";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
extend({ OrbitControls });

Orbit Controls๋ฅผ jsx ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด three.js์—์„œ import ํ•œ ๋’ค react-three/fiber๋กœ extendํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Orbit jsx ์ƒ์„ฑ

const Orbit = () => {
  const { camera, gl } = useThree();
  return <orbitControls args={[camera, gl.domElement]} />;
};

orbitControls ์ƒ์„ฑ์ž๋Š” camera๊ฐ์ฒด์™€ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์— ์‚ฌ์šฉ๋˜๋Š” HTML ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” react-three/fiber์—์„œ ์ œ๊ณตํ•˜๋Š” useThree()๋ฅผ ๊ฐ์ฒด ๊ตฌ์กฐ ๋ถ„ํ•ดํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

Canvas์— ๋„ฃ๊ธฐ

<Canvas style={{ background: "black" }} camera={{ position: [3, 3, 3] }}>
  <Box />
  <axesHelper args={[5]} />
  <Orbit />
</Canvas>

Light, Shadow

์ง€๊ธˆ์€ ๋น›์ด ์—†๋Š” ์ƒํƒœ์ง€๋งŒ ํ๋ทฐ๊ฐ€ ์ž˜ ๋ณด์ž…๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” meshBasicMaterial์€ ๋น›์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š” material์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ ์ž์ฒด์˜ ์ƒ‰์„ ๋‚ด๊ณ  ์žˆ๋˜ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋น›์˜ ์˜ํ–ฅ์„ ๋ฐ›๋Š” material์„ ์ ์šฉ์‹œ์ผœ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

<mesh ref={ref} {...props}>
  <boxBufferGeometry />
  <meshPhysicalMaterial color="blue" />
</mesh>

meshBasicMaterial๋Œ€์‹  meshPhysicalMaterial์„ ์‚ฌ์šฉํ•˜๋‹ˆ ๋”์ด์ƒ ํ๋ธŒ๊ฐ€ ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด์ œ ๋น›์„ ์ถ”๊ฐ€ํ•ด๋ด…์‹œ๋‹ค.

Light

ambientLight

ambientLight๋Š” ๋ชจ๋“  mesh๊ฐ€ ๋ชจ๋“  ๊ฐ๋„์—์„œ ๋™์ผํ•œ ์–‘์„ ๋ฐ›๊ฒŒํ•˜๋Š” ๋น›์ž…๋‹ˆ๋‹ค. ๋‹น์—ฐํžˆ ๊ทธ๋ฆผ์ž๋„ ์ƒ๊ธฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

<ambientLight intensity={0.2} />

canvas์•ˆ์— ์ถ”๊ฐ€ํ•ด ์ค์‹œ๋‹ค. intensity๋กœ ๋น›์˜ ์„ธ๊ธฐ๋ฅผ ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

pointLight

pointLight๋Š” ํ•œ ์ ์—์„œ ๋‚˜์˜ค๋Š” ๋น›์ž…๋‹ˆ๋‹ค. ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ผ ๋น›์˜ ์–‘์ด ์ค„์–ด๋“ค๊ณ  ๊ฐ๋„์— ๋”ฐ๋ผ ๋น›์„ ๋ฐ›์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ๋Š” ํ”ํžˆ ์šฐ๋ฆฌ๊ฐ€ ์•„๋Š” ๋น›์ž…๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๋น› ์ž์ฒด๋Š” ๊ด€์ธก๋˜์ง€ ์•Š๊ณ  ๋น›์„ ๋ฐ›๋Š” mesh๋“ค์—๊ฒŒ ์˜ํ–ฅ์„ ์ค„ ๋ฟ์ž…๋‹ˆ๋‹ค.

<pointLight />

canvas์•ˆ์— ์ถ”๊ฐ€ํ•ด ์ค์‹œ๋‹ค. default ์œ„์น˜ [0,0,0]์„ ๋ฐ”๋ผ๋ณด๋Š” ํ๋ธŒ์˜ ๋ฉด๋งŒ ๋ฐ์•„์ง„ ๊ฒƒ์ด ๋ณด์ž…๋‹ˆ๋‹ค.

๋น›์„ ๋‚ด๋Š” ๊ตฌ์ฒด

์ง€๊ธˆ๊นŒ์ง€๋Š” ๋น› ์ž์ฒด๋Š” ๊ด€์ธกํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ ์„ธ์ƒ์—์„œ๋Š” ํƒœ์–‘๊ณผ ๊ฐ™์ด ๋น›์„ ๋ถ„์ถœํ•˜๋Š” ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” meshPhongMaterial์•ˆ์— pointLight๋ฅผ ๋„ฃ์Œ์œผ๋กœ์จ ํƒœ์–‘ ๋น„์Šทํ•œ mesh๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. meshPhongMaterial์€ ๋น›์„ ๋ฐ˜์‚ฌ์‹œ์ผœ ํ‘œ๋ฉด์ด ๋น›๋‚˜๋Š” material์ž…๋‹ˆ๋‹ค.

const Sun = (props) => {
  return (
    <mesh {...props}>
      <pointLight castShadow />
      <sphereBufferGeometry args={[0.3]} />
      <meshPhongMaterial emissive="yellow" />
    </mesh>
  );
};

๊ทธ๋™์•ˆ์€ ๊ณ„์† boxBufferGeometry๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ ๊ตฌ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ sphereBufferGeometry๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. args๋Š” ๋ฐ˜์ง€๋ฆ„์˜ ๊ธธ์ด ์ž…๋‹ˆ๋‹ค. meshPhongMaterial์—์„œ emissive๋กœ ๋น›์ด ๋ฐ˜์‚ฌ๋˜๋Š” ์ƒ‰์„ ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<Sun position={[0, 3, 0]} />

Shadow

๋ถ„๋ช… ๋ฐฉํ–ฅ์™€ ๊ฑฐ๋ฆฌ๊ฐ€ ์œ ํšจํ•œ ๋น›์„ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ ๊ทธ๋ฆผ์ž๊ฐ€ ์ƒ๊ธฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆผ์ž๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ์ถ”๊ฐ€์ ์ธ ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

Canvas ์†์„ฑ ์ถ”๊ฐ€

Canvas์— shadows์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๊ทธ๋ฆผ์ž๋ฅผ ๊ทธ๋ฆด ์ˆ˜ ์žˆ๋Š” Canvas๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

<Canvas
	shadows
    style={{ background: "black" }}
    camera={{ position: [3, 3, 3] }}
>

๊ทธ๋ฆผ์ž๋ฅผ ๊ทธ๋ฆฌ๋Š” ์ž, ๊ทธ๋ ค์ง€๋Š” ์ž

๊ทธ๋ฆผ์ž๋ฅผ ๋งŒ๋“œ๋Š” ์˜ค๋ธŒ์ ํŠธ, ๋ฐ›์•„์„œ ๊ทธ๋ฆฌ๋Š” ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์„ค์ •ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•„๊นŒ ๋งŒ๋“  Sun์€ ๊ทธ๋ฆผ์ž๋ฅผ ๊ทธ๋ฆฌ๋Š” ์˜ค๋ธŒ์ ํŠธ์ด๊ณ  ๋ฐ”๋‹ฅ์ธ Floor๋Š” ๊ทธ๋ฆผ์ž๊ฐ€ ๊ทธ๋ ค์ง€๋Š” ์˜ค๋ธŒ์ ํŠธ ์ž…๋‹ˆ๋‹ค. ๊ฐ๊ฐ castShadow ์™€ receiveShadow๋ฅผ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

const Sun = (props) => {
  return (
    <mesh {...props}>
      <pointLight castShadow />
		...
const Floor = (props) => {
  return (
    <mesh {...props} receiveShadow>
      ...

Box๋Š” ์–ด๋–จ๊นŒ์š”? Box๋Š” ๊ทธ๋ฆผ์ž๋ฅผ Floor์— ๊ทธ๋ฆฌ๊ธฐ๋„ ํ•˜์ง€๋งŒ Sun์ด ๋งŒ๋“œ๋Š” ๊ทธ๋ฆผ์ž๋ฅผ ์ž์‹ ์—๊ฒŒ ๊ทธ๋ฆฌ๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. castShadow ์™€ receiveShadow ๋‘˜๋‹ค ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

const Box = (props) => {
  ...
  return (
    <mesh ref={ref} {...props} castShadow receiveShadow>
      ...

Material

meshMaterial ๊ธฐ๋ณธ ์†์„ฑ

meshMaterial์—์„œ ๋ช‡๊ฐ€์ง€ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์†์„ฑ๋“ค์„ ํ›‘์–ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

opacity, transparent

opacity๋Š” ๋ถˆํˆฌ๋ช…๋„์ž…๋‹ˆ๋‹ค. 1์ด๋ฉด ์™„์ „ํ•œ ๋ถˆํˆฌ๋ช…์ด๊ณ  0์ด๋ฉด ์™„์ „ํ•œ ํˆฌ๋ช…์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ opacity๋งŒ์œผ๋กœ๋Š” mesh๊ฐ€ ์‹ค์ œ๋กœ ํˆฌ๋ช…ํ•ด์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. transparent ์†์„ฑ์ด true์ผ ๋•Œ๋งŒ opacity์†์„ฑ์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. material ์†์„ฑ์„ ์ž˜ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ๋ธŒ์˜ ์œ„์น˜๋ฅผ ๋ณ€๊ฒฝํ•˜์˜€์Šต๋‹ˆ๋‹ค.

<meshPhysicalMaterial
    color="blue"
    opacity={0.3}
    transparent
/>

wireframe

mesh์˜ ํ”„๋ ˆ์ž„๋งŒ ๋‚˜์˜ค๊ฒŒ ํ•˜๋Š” ์†์„ฑ์ž…๋‹ˆ๋‹ค. ํ”„๋ ˆ์ž„์˜ ์ƒ‰์€ color์†์„ฑ์œผ๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

<meshPhysicalMaterial
    color="blue"
    opacity={0.3}
    transparent
    wireframe
/>

metalness, roughness

metalness๋Š” ๊ธˆ์† ์žฌ์งˆ์„ ๋งŒ๋“ค์–ด ์ฃผ๊ณ  1์ด๋ฉด ์™„์ „ํ•œ ๊ธˆ์†์ž…๋‹ˆ๋‹ค. roughness๋Š” ํ‘œ๋ฉด์˜ ๊ฑฐ์น ๊ธฐ๋ฅผ ๋œปํ•˜๊ณ  0์ด๋ฉด ๋น›์„ ๊ทธ๋Œ€๋กœ ๋ฐ˜์‚ฌํ•ฉ๋‹ˆ๋‹ค. metalness์˜ default ๋Š” 0, roughness๋Š” 1์ž…๋‹ˆ๋‹ค.

<meshPhysicalMaterial
    color="blue"
    metalness={1}
	roughness={0}
/>

roughness๊ฐ€ 0์ด๊ธฐ ๋•Œ๋ฌธ์— ํ‘œ๋ฉด์—์„œ ํ•œ์ ์œผ๋กœ ๋น›์„ ๊ทธ๋Œ€๋กœ ๋ฐ˜์‚ฌํ•˜๋Š” ๊ฒƒ์ด ๋ณด์ž…๋‹ˆ๋‹ค.

๋” ๋งŽ์€ ์†์„ฑ๋“ค์€ https://threejs.org/docs/index.html?q=mater#api/en/materials/Material ์—์„œ ์ถ”๊ฐ€๋กœ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Texture

ํ…์Šค์ณ๋ฅผ ์ ์šฉ์‹œํ‚ค๋Š” ๋ฒ•๋„ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ € ํ…์Šค์ณ ์‚ฌ์ง„์„ ์ค€๋น„ํ•ด์„œ ์ž‘์—…ํด๋”์— ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

const Box = (props) => {
  ...
  const texture = useLoader(THREE.TextureLoader, "/assets/wood.jpg");
  ...
  return (
    <mesh ref={ref} {...props} castShadow>
      <boxBufferGeometry />
      <meshPhysicalMaterial map={texture} />
    </mesh>
  );
};

@react-three/fiber์˜ ํ›… useLoader๋กœ ์ด๋ฏธ์ง€๋ฅผ ํ™œ์šฉํ•ด ํ…์Šค์ณ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. meshMaterial์˜ map ์†์„ฑ์œผ๋กœ ํ…์Šค์ณ๋ฅผ ์ ์šฉ์‹œํ‚ต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด ์ƒํƒœ๋ฉด texture๊ฐ€ ๋กœ๋”ฉ๋˜๊ธฐ ์ „์— ํ™”๋ฉด์ด ๋ Œ๋”๋ง๋˜๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. <Suspense>์•ˆ์— Box๋ฅผ ๋„ฃ์–ด texture ๋กœ๋”ฉ์ด ์™„๋ฃŒ๋˜๋ฉด ๋ณด์—ฌ์ฃผ๊ธฐ๋กœ ํ•ฉ์‹œ๋‹ค. Suspense๋Š” ๋ฆฌ์•กํŠธ์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

<Canvas
  shadows
  style={{ background: "black" }}
  camera={{ position: [3, 3, 3] }}>
  <Suspense fallback={null}>
    <Box position={[0, 1, 0]} />
  </Suspense>
  ...

Texture - background

์ด๋ฒˆ์—๋Š” ๋ฐฐ๊ฒฝ์— ํ…์Šค์ณ๋ฅผ ๋„ฃ์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ 3d ๋ฐฐ๊ฒฝ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด 360๋„ ์ด๋ฏธ์ง€(ํŒŒ๋‚˜๋กœ๋งˆ)๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

https://cdn.pixabay.com/photo/2018/07/11/16/34/landscape-3531355_960_720.jpg ์‚ฌ์šฉํ•œ ์ด๋ฏธ์ง€

const BackGround = (props) => {
  const texture = useLoader(THREE.TextureLoader, "/assets/back.jpg");

  const { gl } = useThree();

  const formatted = new THREE.WebGLCubeRenderTarget(
    texture.image.height
  ).fromEquirectangularTexture(gl, texture);

  return <primitive attach="background" object={formatted.texture} />;
};

ํ…์Šค์ณ๋ฅผ ๋งŒ๋“œ๋Š”๊ฑด ๊ธฐ์กด๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ์ง€๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๊ธฐ ์ „, ํ›„์— ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” WebGlRenderTarget์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. WebGLCubeRenderTarget๋Š” ํ๋ธŒ ์นด๋ฉ”๋ผ๋กœ ์ดฌ์˜ํ•œ 360๋„ ์ด๋ฏธ์ง€ ์šฉ WebGlRenderTarget์ž…๋‹ˆ๋‹ค. ๊ทธ ์ค‘์—์„œ fromEquirectangularTexture ๋ฉ”์†Œ๋“œ๋Š” ํŒŒ๋‚˜๋กœ๋งˆ ์ด๋ฏธ์ง€๋ฅผ 360๋„ ํ๋ธŒ๋งต(3d ๋ฐฐ๊ฒฝ)์œผ๋กœ ์ปจ๋ฒ„ํŒ…ํ•ด์ค๋‹ˆ๋‹ค.

<Suspense fallback={null}>
    <BackGround />
</Suspense>

BackGround๋„ ํ…์Šค์ณ ๋กœ๋”ฉ์ด ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— Suspense ์‚ฌ์ด์— ๊ปด์„œ Canvas์— ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors