수강생 모집 : “웹표준 기술 기반의 3D 가상환경 인터렉티브 웹 개발”

안녕하세요. 오는 10월 24일 ~ 26일에 대전(ICT이노베이션스퀘어)에서 “웹표준 기술을 이용한 3D 가상환경 인터렉티브 웹 개발”에 대한 교육을 진행합니다. 이번 교육은 3D 웹 표준 기술을 보다 쉽게 활용할 수 있는 three.js를 React에서 사용할 수 있는 R3F를 주제로 합니다. 실습 위주의 교육으로 재미있게 진행할 수 있도록 했고 최종 실습에서는 가상공간을 만들고 가상공간과 케릭터의 상호작용에 대한 기능을 수강생과 함께 만들어 나갑니다. 아울러 실습 진행을 위해 필요한 수학적인 내용을 최대한 간결하고 쉽게 설명해 다른 프로젝트에서도 유용하게 적용할 수 있습니다.

수강신청은 아래의 이미지를 클릭 하시면 해당 링크로 이동합니다.

Drei 컴포넌트 : Grid

Drei 컴포넌트는 R3F에서 사용할 수 있는 매우 유용한 컴포넌트의 집합입니다. 그 중 Grid 컴포넌트에 대한 사용인데 Grid 이외의 Drei 컴포넌트도 사용되고 있습니다. 예를 들어 StatsGl, Center, Grid, GizmoHelper, GizmoViewcube, GizmoViewport 입니다.

먼저 결과는 다음과 같습니다.

위의 결과에 대한 코드를 하나씩 살펴보면, 먼저 App.jsx 입니다.

import './App.css'
import { Canvas } from '@react-three/fiber'
import MyElement3D from './MyElement3D_Grid'

function App() {
  return (
    <>
      <Canvas shadows camera={{ position: [10, 12, 12], fov: 25 }}>
        <color args={["#303035"]} attach="background" />
        <MyElement3D />
      </Canvas>
    </>
  )
}

export default App

다음은 App 컴포넌트에서 사용하고 있는 MyElement3D_Grid 파일에 대한 코드입니다. 이 파일의 코드를 기능 별로 언급합니다.

먼저 모델 컴포넌트입니다.

import { AccumulativeShadows, Center, Environment, GizmoHelper, GizmoViewcube, GizmoViewport, Grid, OrbitControls, RandomizedLight, Stats, StatsGl, useGLTF } from "@react-three/drei"
import { useControls } from "leva"
import { memo } from "react"

function Robot(props) {
  const { nodes } = useGLTF('./models/model.glb')

  nodes.Scene.traverse(obj => {
    obj.castShadow = true
    obj.receiveShadow = true
  })

  console.log(nodes)
  return (
    <primitive object={nodes.Scene} {...props} />
  )
}

다음은 그림자 컴포넌트입니다. memo 고차 컴포넌트를 사용했는데 그림자일 경우 이렇게 처리하면 장점이 있다고 하네요.

const Shadows = memo(() => (
  <AccumulativeShadows temporal frames={100} color="#000000" colorBlend={0.5} alphaTest={0.9} scale={20}>
    <RandomizedLight amount={8} radius={4} position={[5, 5, -10]} />
  </AccumulativeShadows>
))

이 글의 주인공인 Grid 컴포넌트입니다. 이 컴포넌트의 속성을 UI로 설정하기 위해서 useControls를 사용했습니다.

function MyGrid() {
  const { gridSize, ...gridConfig } = useControls({
    gridSize: [10.5, 10.5],
    cellSize: { value: 0.6, min: 0, max: 10, step: 0.1 },
    cellThickness: { value: 1, min: 0, max: 5, step: 0.1 },
    cellColor: '#6f6f6f',
    sectionSize: { value: 3.3, min: 0, max: 10, step: 0.1 },
    sectionThickness: { value: 1.5, min: 0, max: 5, step: 0.1 },
    sectionColor: '#9d4b4b',
    fadeDistance: { value: 25, min: 0, max: 100, step: 1 },
    fadeStrength: { value: 1, min: 0, max: 1, step: 0.1 },
    followCamera: false,
    infiniteGrid: true
  })

  return (
    <Grid args={gridSize} position={[0, -0.01, 0]} {...gridConfig} />
  )
}

위에서 정의된 컴포넌트들을 사용한 실제 MyElement3D입니다. 리엑트의 좋은 코딩 습관은 말단 컴포넌트는 최대한 JSX 코드만을 반환하는 것으로 작성하는 것입니다. 그러면 코드 가독성이 높아지고 컴포넌트 분리가 효과적으로 됩니다.

function MyElement3D() {
  return (
    <>
      {/* <Stats /> */}
      <StatsGl />

      <OrbitControls makeDefault />
      <Environment preset="city" />
      
      {/* <Center top>
        <Suzi rotation={[-0.63, 0, 0]} scale={2} />
      </Center> */}
      
      <Center>
        <Robot scale={2} />
      </Center>

      <MyGrid />

      <GizmoHelper alignment="bottom-right" margin={[80, 80]}>
        <GizmoViewcube />
        {/* <GizmoViewport axisColors={['#9d4b4b', '#2f7f4f', '#3b5b9d']} labelColor="white" /> */}
      </GizmoHelper>
      
      <Shadows />
    </>
  )
}

export default MyElement3D

이 코드는 추후 제 유튜브 채널을 통해 영상으로 소개될 예정입니다.