https://brianzinn.github.io/react-babylonjs/
3D, XR, 게임 등에 사용하는 바빌론 라이브러리 리액트 Wrapper
바빌론js 의 대부분 선언적 코드 생성으로 인해 그림자, 물리, 3D 모델 등을 props 로 추가하여 작업할 수 있다고 한다. 바빌론js를 사용하기 앞서 리액트를 사용하는 개발자로써 다뤄보기로 하였다. 공식문서 순서에 따라 정리해보았으며 자세한 사항을 알고자한다면... 직접 해석하길 바란다. (나도 안되는 영어로 읽어본 것이니)
시작하기
npm i react-babylonjs @babylonjs/core @babylonjs/gui
yarn add react-babylonjs @babylonjs/core @babylonjs/gui
설치는 기본적으로 babylonjs/core 가 필요하고, react-babylonjs 로 사용해줘야한다.
예시를 보며 분석하기
import {
Engine,
FreeCamera,
HemisphericLight,
MeshBuilder,
Scene,
Vector3,
} from '@babylonjs/core'
import React, { useEffect, useRef } from 'react'
const SceneComponent = (props) => {
const reactCanvas = useRef(null)
const {
canvasId,
antialias,
engineOptions,
adaptToDeviceRatio,
sceneOptions,
onRender,
onSceneReady,
...rest
} = props
useEffect(() => {
if (!reactCanvas.current) return
const engine = new Engine(
reactCanvas.current,
antialias,
engineOptions,
adaptToDeviceRatio
)
const scene = new Scene(engine, sceneOptions)
if (scene.isReady()) {
onSceneReady(scene)
} else {
scene.onReadyObservable.addOnce(onSceneReady)
}
engine.runRenderLoop(() => {
onRender(scene)
scene.render()
})
const resize = () => {
scene.getEngine().resize()
}
if (window) {
window.addEventListener('resize', resize)
}
return () => {
scene.getEngine().dispose()
if (window) {
window.removeEventListener('resize', resize)
}
}
}, [
antialias,
engineOptions,
adaptToDeviceRatio,
sceneOptions,
onRender,
onSceneReady,
])
return <canvas id={canvasId} ref={reactCanvas} {...rest} />
}
let box
const onSceneReady = (scene) => {
// This creates and positions a free camera (non-mesh)
var camera = new FreeCamera('camera1', new Vector3(0, 5, -10), scene)
// This targets the camera to scene origin
camera.setTarget(Vector3.Zero())
const canvas = scene.getEngine().getRenderingCanvas()
// This attaches the camera to the canvas
camera.attachControl(canvas, true)
// This creates a light, aiming 0,1,0 - to the sky (non-mesh)
var light = new HemisphericLight('light', new Vector3(0, 1, 0), scene)
// Default intensity is 1. Let's dim the light a small amount
light.intensity = 0.7
// Our built-in 'box' shape.
box = MeshBuilder.CreateBox('box', { size: 2 }, scene)
// Move the box upward 1/2 its height
box.position.y = 1
// Our built-in 'ground' shape.
MeshBuilder.CreateGround('ground', { width: 6, height: 6 }, scene)
}
/**
* Will run on every frame render. We are spinning the box on y-axis.
*/
const onRender = (scene) => {
if (box !== undefined) {
var deltaTimeInMillis = scene.getEngine().getDeltaTime()
const rpm = 10
box.rotation.y += (rpm / 60) * Math.PI * 2 * (deltaTimeInMillis / 1000)
}
}
const App = () => (
<div>
<SceneComponent
canvasId="babylon-canvas"
antialias
onSceneReady={onSceneReady}
onRender={onRender}
/>
</div>
)
export default App
기본적으로 사용되는 Engine, FreeCamera, HemisphericLight, MeshBuilder, Scene, Vector3 는 core 에서 가져오고, 씬 컴포넌트에 canvasId 와 onSceneReady, onRender props 를 지정해서 씬을 직접 커스텀 해줄 수 있다.
onSceneReady 의 경우 씬과 관련된 카메라, 조명 등의 설정과 보여질 컴포넌트에 대한 렌더링을 설정해준다.
onRender 는 씬 렌더가 완료되면 보여질 컴포넌트의 행위 등을 설정한다. 위의 예시의 경우 box 오브젝트의 rotation.y 를 사용하여 회전처리를 해준다.
이를 babylonjs-hook 라이브러리를 추가하여 더 간단하게 처리가 가능하다.
import {
FreeCamera,
HemisphericLight,
MeshBuilder,
Vector3,
} from '@babylonjs/core'
import SceneComponent from 'babylonjs-hook'
let box
const onSceneReady = (scene) => {
// This creates and positions a free camera (non-mesh)
var camera = new FreeCamera('camera1', new Vector3(0, 5, -10), scene)
// This targets the camera to scene origin
camera.setTarget(Vector3.Zero())
const canvas = scene.getEngine().getRenderingCanvas()
// This attaches the camera to the canvas
camera.attachControl(canvas, true)
// This creates a light, aiming 0,1,0 - to the sky (non-mesh)
var light = new HemisphericLight('light', new Vector3(0, 1, 0), scene)
// Default intensity is 1. Let's dim the light a small amount
light.intensity = 0.7
// Our built-in 'box' shape.
box = MeshBuilder.CreateBox('box', { size: 2 }, scene)
// Move the box upward 1/2 its height
box.position.y = 1
// Our built-in 'ground' shape.
MeshBuilder.CreateGround('ground', { width: 6, height: 6 }, scene)
}
/**
* Will run on every frame render. We are spinning the box on y-axis.
*/
const onRender = (scene) => {
if (box !== undefined) {
var deltaTimeInMillis = scene.getEngine().getDeltaTime()
const rpm = 10
box.rotation.y += (rpm / 60) * Math.PI * 2 * (deltaTimeInMillis / 1000)
}
}
const App = () => (
<div>
<SceneComponent antialias onSceneReady={onSceneReady} onRender={onRender} />
</div>
)
export default App
위의 예시와 동일한 코드지만 그 길이가 줄어들었다. babylonjs-hook 라이브러리의 SceneComponent 를 사용하여 씬 컴포넌트를 직접 만들어주는 과정을 생략하게 된 것이다. 나머지는 동일하다.
기본 씬부터 차근히 만들어보기
react-babylonjs에서 기본적으로 씬은 Engine 과 Scene 컴포넌트를 사용하여 만들어줄 수 있다.
import { Engine, Scene } from 'react-babylonjs'
const BabylonApp = ({ children }) => (
<div style={{ flex: 1, display: 'flex' }}>
<Engine antialias adaptToDeviceRatio canvasId="babylon-canvas">
<Scene>{children}</Scene>
</Engine>
</div>
)
빛, 카메라, 표면 추가하기
위의 예제로는 씬 안에 아무것도 추가된 게 없으므로 보이지 않는 게 당연하다. 무언가 보이게 만들기 위해선 빛과 카메라 그리고 무언가의 컴포넌트를 넣어줘야 한다.
빛의 경우 hemispericLight
카메라는 freeCamera
땅의 경우 ground 컴포넌트를 넣어서 기본 씬을 구성해주자.
import { Engine, Scene } from "react-babylonjs";
import { Vector3 } from "@babylonjs/core";
export default ({ children }) => (
<div style={{ flex: 1, display: "flex" }}>
<Engine
antialias
adaptToDeviceRatio
canvasId="babylon-js"
renderOptions={{
whenVisibleOnly: true,
}}
>
<Scene>
<freeCamera name="camera1" position={new Vector3(0, 5, -10)} setTarget={[Vector3.Zero()]} />
<hemisphericLight name="light1" intensity={0.7} direction={new Vector3(0, 1, 0)} />
<ground name="ground" width={6} height={6} />
</Scene>
</Engine>
</div>
);
땅 위에 박스를 추가해준다. 박스의 위치를 지정해줘야한다.
<div style={{ flex: 1, display: "flex" }}>
<Engine
antialias
adaptToDeviceRatio
canvasId="babylon-js"
renderOptions={{
whenVisibleOnly: true,
}}
>
<Scene>
<freeCamera name="camera1" position={new Vector3(0, 5, -10)} setTarget={[Vector3.Zero()]} />
<hemisphericLight name="light1" intensity={0.7} direction={new Vector3(0, 1, 0)} />
<ground name="ground" width={6} height={6} />
<box name="box" size={2} position={new Vector3(0, 1, 0)} rotation={Vector3.Zero()} />
</Scene>
</Engine>
</div>
이런 식으로 씬 안에 컴포넌트를 넣어줘서 처리한다. 만약 박스 컴포넌트를 분리하고자 한다면
import { MeshBuilder } from '@babylonjs/core'
const box = MeshBuilder.CreateBox('box', { size: 2 }, scene);
box.position = new Vector3(0, 1, 0);
box.rotation = Vector3.Zero();
이런 식으로 지정해줄 수도 있다.
다른 컴포넌트들도 컴포넌트화 되어 있는 것을 위처럼 분리해서 사용해줄 수 있다.
import { FreeCamera, Vector3 } from '@babylonjs/core'
var camera = new FreeCamera('camera1', new Vector3(0, 5, -10), scene)
camera.setTarget(Vector3.Zero())
기본적인 내용은 이러하고 이제 애니메이션 같은건 차차 다뤄볼 예정이다.
'Ect. > Library' 카테고리의 다른 글
pynetdicom 의 storescu에 대해 (1) | 2023.10.08 |
---|---|
pynetdicom 을 사용하여 DICOM Networking 시도 (1) | 2023.10.07 |
Ag-grid react 사용기 - 정렬(Sorting) (0) | 2023.09.06 |
React Chart 라이브러리 - recharts (0) | 2023.08.31 |
리액트 Chart 라이브러리 (0) | 2023.08.26 |