개발 · 컴퓨터공학/three.js / / 2024. 5. 31. 09:07

Threejs Cloth Tailor 개발일지 - Mesh buffer type을 TypsScript threejs에 맞게 수정하기

728x90
반응형

 

 

현재 상황

그냥 cloth object를 보여주는게 전부이다..

이제 여기다가 분석한 carmen씨의 코드를 바탕으로 cloth simulation을 구현하도록 한다

 

아무렇게나 스크립트를 가져오면 안되니 종속되지 않은 스크립트부터 하나씩 가져오자

필요한 스크립트 가져오기

math관련 함수들, constraints, collision, physics object 등 

지금까지 물리 시뮬레이션을 위해 필요했던 스크립트들을 가져오자

 

matrix 연산자가 필요하기 때문에 gl-matrix도 설치해준다

gl-matrix 설치

npm i gl-matrix

gl-matrix는 빌드에서도 처리되어야하므로 dependencies에 설치하자

mesh type이 필요하다

physics object 스크립트에서는 직접 정의한 Mesh type을 사용한다

 

하지만 threejs를 사용하는 상황에서 mesh에 대한 type 정의없이 threejs에서 정의된 mesh를 사용해야한다

기존 carment씨의 코드는 webGPU로 작성한 코드이므로 이걸 threejs mesh type으로 변경해서 사용할 방법에 대해서 탐구해보자

custom mesh type을 threejs mesh로 바꾸기

export interface Mesh {
  positions: Float32Array;
  uvs: Float32Array;
  normals: Float32Array;
  indices: Uint16Array;
}

custom으로 선언되어있는 mesh type은 이렇다

 

threejs mesh의 경우 아래 docs에서 볼 수 있는데

 

three.js docs

 

threejs.org

 

const geometry = new THREE.BufferGeometry();

const vertices = new Float32Array( [
	-1.0, -1.0,  1.0, // v0
	 1.0, -1.0,  1.0, // v1
	 1.0,  1.0,  1.0, // v2
	-1.0,  1.0,  1.0, // v3
] );

const indices = [
	0, 1, 2,
	2, 3, 0,
];

geometry.setIndex( indices );
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );

const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
const mesh = new THREE.Mesh( geometry, material );

threejs의 geometry는 위와 같은 property들로 구성되어있다

보면 다른 건 몰라도 normal에 대한 정의는 없다

 

docs에는 compute vertex normal이라고 해서 vertex 정보를 가지고 normal을 계산하는게 있는데

그렇다면 시계 혹은 반시계 방향으로 자동으로 계산해주다는 뜻이다

 

일단 normal에 대해서는 신경쓰지 않고 진행해보자

physics object의 mesh type들 수정하기

export default abstract class PhysicsObject {
  ...

  constructor(mesh: Mesh) {
    this.numParticles = mesh.positions.length / 3;
    this.positions = new Float32Array(mesh.positions);
    this.normals = new Float32Array(mesh.normals);
    this.prevPositions = new Float32Array(mesh.positions);
    this.vels = new Float32Array(3 * this.numParticles);
    this.invMass = new Float32Array(this.numParticles);
    this.indices = new Uint16Array(mesh.indices);
    this.constraints = [];
    this.collisions = [];

생성자의 mesh의 타입은 Mesh인데

이 type은 types/three 라이브러리에서 가져올 수 있지 않을까 싶다

 

Mesh라는 type이 types/three에 있지는 않고 geometry 중 하나를 type으로 사용해야할 것 같다

 

custom하게 사용하기 위해서는 threejs에서는 일반적으로 BufferGeometry를 사용하는데

BufferAttribute에 position, uvs, indices가 있을텐데 docs에 제대로 설명이 없다

 

그래서 BoxGeometry의 attributes를 출력해본 결과인데

normal, position, uv가 모두 정의되어있다

 

이러면 Mesh를 그대로 attributes로 대체해도 충분한데

indices를 사용하는 방법이 threejs에도 있기 때문에 이것까지만 알아보자

geometry attributes의 indices 속성은 어디에

geometry의 attributes 안에서 indices 속성을 사용하지 않아

오브젝트의 geometry를 꺼내서 indices를 확인해보자

 

여기 보면 BoxGeometry에 index 속성이 있다

이게 indices에 해당하는 값이다

 

custom type에서 정의한 Mesh의 각각 요소들을 이렇게 대응하도록 코드를 수정해보자

physics object 코드 수정하기

threejs에도 Mesh type은 있길래

Mesh를 threejs의 type으로 가져와보기로 했다

 

position, normal은 geometry.attribute에서 되는데

indices의 경우 에러가 뜨네

 

에러를 읽어보면 type 변한 에러인데

ArrayBufferLike 타입이 아니라서 생긴 문제라고 한다

TypedArray에서 변환은 어렵지 않을거라고 생각하는데 undefined일 수 있어서 그런가?

 

  constructor(mesh: Mesh) {
    this.numParticles = mesh.geometry.attributes.position.count;
    this.positions = new Float32Array(mesh.geometry.attributes.position.array);
    this.normals = new Float32Array(mesh.geometry.attributes.normal.array);
    this.prevPositions = new Float32Array(mesh.geometry.attributes.position.array);
    this.vels = new Float32Array(3 * this.numParticles);
    this.invMass = new Float32Array(this.numParticles);
    this.indices = new Uint16Array(mesh.geometry.index?.array ?? new Array(0));

이렇게 undefined인 경우 empty array를 주도록 coalescing 연산자를 사용하니 잘 된다

 

cloth class에도 이렇게 적용해보자

cloth class는 mesh를 외부에서 가져와서 super 함수로 넘겨주기만 한다

외부에서 가져오는 mesh를 확인해보자

 

// Create Buffers and Bind Groups
const meshBuffers = gpuCanvas.createMeshBuffers(mesh);

...

// thickness and spacing in hash table needed adjusting.
const thickness = VERTEX_SPACING;
const cloth = new Cloth(mesh, thickness);

본래 cloth 객체에 들어가는 mesh는 webGPUCanvas class의 MeshBuffer type을 담는다

  createMeshBuffers(mesh: Mesh): VertexBuffers {
    const { indices, positions, normals, uvs } = mesh;

    const positionBuffer = this.createMeshBuffer(
      positions,
      GPUBufferUsage.VERTEX
    );
    const indexBuffer = this.createMeshBuffer(indices, GPUBufferUsage.INDEX);
    const normalBuffer = this.createMeshBuffer(normals, GPUBufferUsage.VERTEX);
    const uvBuffer = this.createMeshBuffer(uvs, GPUBufferUsage.VERTEX);

    return {
      indices: indexBuffer,
      normals: normalBuffer,
      position: positionBuffer,
      uvs: uvBuffer,
    };
  }

이는 위 코드처럼 device buffer를 직접적으로 접근하지 않는 threejs에서는

단순히 empty Mesh type을 의미한다

 

이로써 webGPU의 custom Mesh type을 threejs의 Mesh로 수정하였다

 

이제 PBD cloth simulation 을 적용해보자

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