물리 시뮬레이션 코드 디버깅하기
cloth 객체에서 constraint를 생성하는 부분을 디버깅하면서 어디서부터 잘못되었는지 보자
보니까 cloth 객체의 물리 시뮬레이션 필요 요소들이 제대로 초기화가 되지 않은 것 같다
physics object의 초기화 부분을 찾다보니 indices가 0개인 것부터 numTris가 0으로 초기화 되는 것을 확인했다
이 부분은 threejs의 mesh type으로 바꾸는 과정에서 임의로 설정한 부분인데
역시 여기서 문제가 생겼다
공교롭게도 index가 null이라서 indices에 0 배열이 들어간 것이다
즉 threejs의 Mesh형으로 변환하는 것과, 기존 코드에서 object를 parsing할 때 indices 로직이 달라서 생긴 문제다
threejs의 OBJLoader의 indices문제
위 링크를 참고해보면 threejs의 OBJLoader는 non-indexed geometry를 parsing해온다고한다
즉 loader에서 file을 parsing하는 방식 자체가 추구하는 방식과 다른 것으로 OBJLoader를 사용하는 방법은 좋지 않은 것이다.
그럼 결국 threejs의 OBJLoader를 사용하지 않고 carmen씨가 custom으로 제작한 Loader를 사용해서 Model을 load해야할 것 같다.
custom ObjLoader 로 모델 로드하기
carmen씨의 objloader를 이용하였을 때 반환되는 것은 위와 같다
이 값들을 이용해서 threejs geometry를 생성하면 모델이 잘 생성되는지 확인해보자
이렇게 custom objloader에서 return할 때 attribute를 담아서 mesh형태로 반환하도록 한다
set attribute함수를 이용해서 넣으면 attribute count가 1이 된다
count는 read-only라서 따로 넣는 방법은 없는 것 같다
따라서 particles number에 count를 사용하지 않고 itemSize로 변경해준다
...근데 scene에 mesh를 추가하니 에러가 뜬다
뭐가 문제일까
이제보니 attribute의 두 번째 인자가 itemSize인데, position은 3, uv는 2 이런 식으로 한 쌍을 몇 개가 구성하는지를 명시해야한다 이 부분에 length를 넣은 것이 실수였다
이러면 numParticles를 itemSize로 변경하지 않고 count로 정상 진행할 수 있다
고치니까 렌더링이 잘 된다
다시 물리 시뮬레이션 디버깅
그럼 이제 다시 어디서부터 시뮬레이션에 문제가 생기는지 알아보자
검토 결과 내부적으로 값들은 잘 주고 받아지는 것 같다
PBD 물리 시뮬레이션 결과 position 반영하기
그렇다면 update vertex normals 이후 변경된 position을 반영하도록 해보자
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()
// apply vertex position
clothMesh.geometry.setAttribute('position', new BufferAttribute(new Float32Array(cloth.positions), 3))
clothMesh.geometry.setAttribute('normal', new BufferAttribute(new Float32Array(cloth.normals), 3))
}
이렇게 set attribute position과 normal을 cloth mesh에 적용하도록 해봤다
그랬더니 시뮬레이션이 동작한다!
드디어 webGPU의 코드를 가지고 threejs로 포팅하는데 성공했다
의문점
// apply vertex position
clothMesh.geometry.setAttribute('position', new BufferAttribute(new Float32Array(cloth.positions), 3))
clothMesh.geometry.setAttribute('normal', new BufferAttribute(new Float32Array(cloth.normals), 3))
근데 position만 움직이면 되지 normal이 필요한가? 하는 생각이 문득 들었다
wireframe을 material로 채우고 나서도 normal의 차이는 없어보이는데
lighting을 적용 해봐야 알려나?
directional light를 적용해보니 확연히 normal의 중요성이 느껴진다
왼쪽이 normal을 잘 적용해준 버전, 오른쪽은 normal을 주석처리한 버전이다
근데 기존 carmen씨의 webGPU 코드에서도 있었던 문제인데,
normal을 적용하는 코드도 윗단 일부의 normal이 제대로 적용되지 않는다
이건 왜 그럴까
다음에는 이 반만 제대로 적용되는 normal 문제를 해결하자