728x90
반응형
목표
자 이제 템플릿 프로젝트로 무엇을 해야 하느냐. 목표는 물리 리뮬레이션이 포함된 옷 재단 시뮬레이터.
우선 각각의 테스트 scene을 만드는 것이 첫 단계이다.
Test Scene
그렇다면 어떤 테스트 scene을 만들 것이냐. 우선은 cloth simulation을 구현하는 것이 가장 먼저가 될 것 같다.
orbit controler 환경도 이미 구현되어있기 때문에 carmen씨의 코드와 포스팅을 참고해보면서 threejs 기반 cloth simulation test 환경을 직접 개발하는 것이 그 첫번째 목표이다.
코드 모듈화
먼저 scene.ts에 모여있는 기능들을 하나씩 분리하여 모듈화시키자.
render-setting.ts
// ===== 🖼️ CANVAS, RENDERER, & SCENE =====
{
canvas = document.querySelector(`canvas#${CANVAS_ID}`)!
renderer = new WebGLRenderer({ canvas, antialias: true, alpha: true })
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
renderer.shadowMap.enabled = true
renderer.shadowMap.type = PCFSoftShadowMap
scene = new Scene()
}
canvas에 rendering initialize하는 기능을 모듈로 분리한다.
canvas의 id를 이용해서 원하는 canvas에 renderer를 할당하게 하는데, 현재는 canvas 하나만 단순히 사용할 예정이라. 간단히 처리한다.
import { PCFSoftShadowMap, Scene, WebGLRenderer } from "three"
export function InitScene(canvas_id : string): {scene:Scene, canvas:HTMLElement, renderer:WebGLRenderer}{
const canvas: HTMLElement = document.querySelector(`canvas#${canvas_id}`)!
const renderer: WebGLRenderer = new WebGLRenderer({ canvas, antialias: true, alpha: true })
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
renderer.shadowMap.enabled = true
renderer.shadowMap.type = PCFSoftShadowMap
const scene: Scene = new Scene()
return {scene, canvas, renderer};
}
이렇게 canvas와 renderer, scene을 분리해서 함수형으로 만들고 나면, scene.ts에서 InitScene 함수를 호출함으로써 초기화하도록 한다.
controls.ts
import { Camera } from "three"
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"
import { toggleFullScreen } from "./helpers/fullscreen"
export function SetCamera
(camera: Camera, canvas: HTMLElement): {
cameraControls: OrbitControls,
} {
const cameraControls = new OrbitControls(camera, canvas)
cameraControls.enableDamping = true
cameraControls.autoRotate = false
cameraControls.update()
return { cameraControls }
}
export function SetFullScreenEvent(canvas: HTMLElement){
window.addEventListener('dblclick', (event) => {
if (event.target === canvas) {
toggleFullScreen(canvas)
}
})
}
dragControl은 아직은 사용하지 않을 것이라서 제외시켰다.
camera controler와 full screen event 세팅을 해준다.
아참 함수 이름은 Camel식으로 하되 시작은 소문자로 변경했다.
debug-gui.ts
import GUI from 'lil-gui'
import { AmbientLight, AxesHelper, GridHelper, Mesh, PointLight, PointLightHelper, Scene } from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
let axesHelper: AxesHelper
let pointLightHelper: PointLightHelper
export function setHelper(scene: Scene, pointLight: PointLight) {
axesHelper = new AxesHelper(4)
axesHelper.visible = false
scene.add(axesHelper)
pointLightHelper = new PointLightHelper(pointLight, undefined, 'orange')
pointLightHelper.visible = false
scene.add(pointLightHelper)
const gridHelper = new GridHelper(20, 20, 'teal', 'darkgray')
gridHelper.position.y = -0.01
scene.add(gridHelper)
}
/**
* @desc must call after setHelper
* @desc-kr 반드시 setHelper 호출 후에 호출될 것
*/
export function setDebug
(target: Mesh, pointLight: PointLight, ambientLight: AmbientLight, cameraControls: OrbitControls) {
const gui = new GUI({ title: '🐞 Debug GUI', width: 300 })
const targetOneFolder = gui.addFolder('target one')
targetOneFolder.add(target.position, 'x').min(-5).max(5).step(0.5).name('pos x')
targetOneFolder.add(target.position, 'y').min(-5).max(5).step(0.5).name('pos y')
targetOneFolder.add(target.position, 'z').min(-5).max(5).step(0.5).name('pos z')
targetOneFolder.add(target.material, 'wireframe')
targetOneFolder.addColor(target.material, 'color')
targetOneFolder.add(target.material, 'metalness', 0, 1, 0.1)
targetOneFolder.add(target.material, 'roughness', 0, 1, 0.1)
targetOneFolder
.add(target.rotation, 'x', -Math.PI * 2, Math.PI * 2, Math.PI / 4)
.name('rotate x')
targetOneFolder
.add(target.rotation, 'y', -Math.PI * 2, Math.PI * 2, Math.PI / 4)
.name('rotate y')
targetOneFolder
.add(target.rotation, 'z', -Math.PI * 2, Math.PI * 2, Math.PI / 4)
.name('rotate z')
const lightsFolder = gui.addFolder('Lights')
lightsFolder.add(pointLight, 'visible').name('point light')
lightsFolder.add(ambientLight, 'visible').name('ambient light')
//chek helper is initialized
if (!axesHelper || !pointLightHelper) {
throw Error("helper is not initialized");
}
else {
const helpersFolder = gui.addFolder('Helpers')
helpersFolder.add(axesHelper, 'visible').name('axes')
helpersFolder.add(pointLightHelper, 'visible').name('pointLight')
}
const cameraFolder = gui.addFolder('Camera')
cameraFolder.add(cameraControls, 'autoRotate')
// persist GUI state in local storage on changes
gui.onFinishChange(() => {
const guiState = gui.save()
localStorage.setItem('guiState', JSON.stringify(guiState))
})
// load GUI state if available in local storage
const guiState = localStorage.getItem('guiState')
if (guiState) gui.load(JSON.parse(guiState))
// reset GUI state button
const resetGui = () => {
localStorage.removeItem('guiState')
gui.reset()
}
gui.add({ resetGui }, 'resetGui').name('RESET')
gui.close()
}
debug gui와 helper도 따로 모듈화 하였다.
다음에는 OBJ 파일을 load할 수 있도록 OBJLoader를 가져와서 기능을 추가하자.
728x90
반응형