개발 · 컴퓨터공학/three.js / / 2024. 6. 1. 09:20

Threejs Cloth Tailor 개발일지 - cloth object 옷감 오브젝트 vertex 움직임 확인, index에 해당하는 vertex 위치 표시, vscode 디버깅

728x90
반응형

 

이전에 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에 잘 멈춰서서 디버깅이 된다. 

728x90
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유