이전에 mesh type을 webGPU의 custom type에서 threejs의 Mesh type으로 수정하였다
이제 cloth simulation scene에다가 physics simulation을 한 단계씩 적용해보자
시뮬레이션 코드를 어디다가 적용하지
물리 시뮬레이션이 적용되는 것은 매 프레임을 draw할 때
즉 drawcall 단계에서 적용한다
function animate() {
requestAnimationFrame(animate)
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement
camera.aspect = canvas.clientWidth / canvas.clientHeight
camera.updateProjectionMatrix()
}
cameraControls.update()
renderer.render(scene, camera)
}
threejs에서는 animate 함수에
Mesh에 물리 시뮬레이션을 적용하면 될 것이다
if(clothMesh.children[0] instanceof Mesh)
cloth = new Cloth(clothMesh.children[0], thickness)
typescript에서는 번거롭지만 instance의 type check로 예외처리를 항상 해주어야한다
기본적으로 object 형태로 인식되기 때문에 Mesh type이라고 명시가 되어있어도 TS에서는 object로 인식한다
// global variable
const { scene, canvas, renderer } = initScene(CANVAS_ID);
const camera = new PerspectiveCamera(50, canvas.clientWidth / canvas.clientHeight, 0.1, 1000)
camera.position.set(2, 2, 5)
const { cameraControls } = controls.setCameraControl(camera, canvas)
let clothMesh: Group
let cloth:Cloth
const thickness: number = 0.05
const dt = 1.0 / 60.0;
const steps = 10;
const sdt = dt / steps;
const gravity = new Float32Array([-1.1, -9.8, 2.5]);
await init()
animate()
async function init() {
// ===== 💡 LIGHTS =====
{
ambientLight = new AmbientLight('white', 0.4)
scene.add(ambientLight)
}
const planeGeometry = new PlaneGeometry(3, 3)
const planeMaterial = new MeshLambertMaterial({
color: 'gray',
emissive: 'teal',
emissiveIntensity: 0.2,
side: 2,
transparent: true,
opacity: 0.4,
})
const plane = new Mesh(planeGeometry, planeMaterial)
plane.rotateX(Math.PI / 2)
plane.receiveShadow = true
scene.add(plane)
// model load
const objPath = 'cloth.obj'
clothMesh = await loader.loadOBJ(objPath)
clothMesh.position.set(0, 1, 0)
clothMesh.traverse((ele) => {
if (ele instanceof Mesh) {
ele.material = new MeshStandardMaterial({ color: 'red', side: 2 })
}
})
scene.add(clothMesh)
if(clothMesh.children[0] instanceof Mesh){
if(clothMesh.children[0].geometry instanceof BufferGeometry){
clothMesh.children[0].geometry.getAttribute('position').needsUpdate = true
clothMesh.children[0].geometry.getAttribute('normal').needsUpdate = true
clothMesh.children[0].geometry.getAttribute('uv').needsUpdate = true
}
}
// physics object
if(clothMesh.children[0] instanceof Mesh)
cloth = new Cloth(clothMesh.children[0], thickness)
cloth.registerDistanceConstraint(0.0);
cloth.registerPerformantBendingConstraint(1.0);
cloth.registerSelfCollision();
// cloth.registerIsometricBendingConstraint(10.0)
// console.log(cloth)
}
function physicsSimulation(){
gravity[2] = Math.cos(Date.now() / 2000) * 15.5;
cloth.preIntegration(sdt);
for (let i = 0; i < steps; i++) {
cloth.preSolve(sdt, gravity);
cloth.solve(sdt);
cloth.postSolve(sdt);
}
cloth.updateVertexNormals();
}
function animate() {
requestAnimationFrame(animate)
//#region simulation
physicsSimulation()
//#endregion
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement
camera.aspect = canvas.clientWidth / canvas.clientHeight
camera.updateProjectionMatrix()
}
cameraControls.update()
renderer.render(scene, camera)
}
무작정 집어넣으니까 반응이 없다..
어떻게 중력부터 구현을 해보아야하나?
webGPU - app.ts
cloth.updateVertexNormals();
gpuCanvas.device.queue.writeBuffer(
meshBuffers.position.data,
0,
cloth.positions,
0,
meshBuffers.position.length
);
gpuCanvas.device.queue.writeBuffer(
meshBuffers.normals.data,
0,
cloth.normals,
0,
meshBuffers.normals.length
);
program
.activate(renderPassAPI)
.updateCameraUniforms(perspectiveCamera)
.updateModelUniforms(
modelTransformation.modelMatrix,
modelTransformation.getNormalMatrix(perspectiveCamera.viewMatrix),
0
)
.updateLightModelPositionUniform(lightModel.position)
.render(renderPassAPI, meshBuffers);
기존 main 함수를 보면, cloth 객체의 vertex update 이후에
cloth 객체의 position과 normal을 버퍼에 담고 렌더링 파이프라인에 넘긴다
즉 mesh에 반영하는 작업은 별도로 해주어야한다
실제로 vertex 계산은 되었는데 반영이 안되었을 가능성이 있다
...일단 vertex를 수정하는 것부터 테스트를 진행하자.
geometry vertex 수정하기
// test Cube
const cubeMesh = new Mesh(new BoxGeometry(), new MeshStandardMaterial({ color: 'red', side: 1 }))
scene.add(cubeMesh)
cubeMesh.position.set(0,1,0)
cubeMesh.geometry.getAttribute('position').needsUpdate = true
cubeMesh.geometry.getAttribute('position').setX(0, cubeMesh.geometry.getAttribute('position').getX(0) + 1);
일단 위 코드처럼 getAttribute로 position을 가져와서 특정 인덱스의 값을 set해주는 방법이 있다.
위와 같은 방법을 이용하였을 때 position array의 index를 잘 파악해야한다는 점이 중요하다
테스트로 BoxGeometry를 사용하였지만 해당 geometry는 index 정보를 포함하지 않고 vertex에 face 정보들이 포함되어있어서 다른 오브젝트가 필요하다
vertex point 표시하기
앞으로 index 마다 vertex가 어디에 위치해있는지 볼 필요성이 있어서 임시로 point로 vertex를 표시하는 기능을 만들었다
function viewPoint(mesh: Mesh, index: number, scene: Scene){
const pos = mesh.localToWorld(new Vector3(
mesh.geometry.getAttribute('position').getX(index),
mesh.geometry.getAttribute('position').getY(index),
mesh.geometry.getAttribute('position').getZ(index)
))
const point = new Mesh(new SphereGeometry(0.02), new MeshBasicMaterial({color: 'green', transparent: false}))
point.position.set(pos.x,pos.y,pos.z)
console.log(pos)
scene.add(point)
}
const childMesh = clothMesh.children[0]
if(childMesh instanceof Mesh)
childMesh.geometry.getAttribute('position').setX(2, childMesh.geometry.getAttribute('position').getX(2) + 2.5);
viewPoint(childMesh as Mesh, 0, scene)
viewPoint(childMesh as Mesh, 1, scene)
viewPoint(childMesh as Mesh, 2, scene)
이 기능으로 하나씩 attribute position의 get 함수가 가져오는 index를 잘 이용할 수 있을지 확인해보았다
cloth object의 vertex 확인해보기
위에서 만든 기능을 이용하면 cloth 오브젝트에서도 현재 어느 vertex가 어디에있는지 구분하고
vertex를 잘 이동시켰는지 알 수 있다
조금 비스듬히 보이지만 해당 point가 표시하는 지점이 index 0인 지점이다
index 순서대로 늘려보면서 위치를 확인해보면
index는 이런 식으로 배치되어있다
이렇게 해당 index의 vertex 위치를 바꾸는 것도 가능한 것을 확인했으니
이제 물리 시뮬레이션을 적용하는 부분에서 어느쪽이 원인인지 파악해보자
vscode 디버깅 하는법
threejs + Typescript를 단계별로 디버깅하는 방법을 알아야 시뮬레이션 코드의 문제 원인을 찾기 한결 쉬워질 것이다
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"name": "http://localhost:5173/",
"request": "launch",
"url": "http://localhost:5173/",
}
]
}
위와 같이 launch.json이라는 파일을 구성하고 디버깅 모드에서 F5를 누르면 디버깅이 실행되어야 하는데
자꾸 이런 메시지가 떠서 뭐가 문제인지 보다가
브라우저를 naver whale로 사용하고 있는 바람에 chrome으로 인지를 못하는 건가 싶어서 크롬을 설치했다
순간 디버깅 실행이 되더니 갑자기 또 에러가 뜬다
vite를 먼저 실행하고 나서 debugging을 해보니
지정한 breakpoint에 잘 멈춰서서 디버깅이 된다.
'개발 · 컴퓨터공학 > three.js' 카테고리의 다른 글
threejs 의류 관련 프로젝트 - 의상 color customize (0) | 2024.06.16 |
---|---|
threejs 가상 피팅 사이트 virtual try on 레퍼런스 (0) | 2024.06.15 |
Threejs Cloth Tailor 개발일지 - Mesh buffer type을 TypsScript threejs에 맞게 수정하기 (1) | 2024.05.31 |
Threejs Cloth Tailor 개발일지 - 모델 OBJ 파일 load하기 OBJLoader, load model material 입히기 (0) | 2024.05.11 |
Threejs Cloth Tailor 개발일지 - TypeScripts 코드 모듈화 (0) | 2024.05.10 |