728x90
반응형
파티클 큐브 생성
무작위 배열로 설정하였지만, 그래도 cube의 각 cell안에
들어있는 형태로 변경해보자.
function spawnParticlesInBox(
particleMesh: THREE.InstancedMesh,
numToSpawn: THREE.Vector3, // x, y, z로 몇 개씩 스폰할지 설정
spawnCenter: THREE.Vector3, // 스폰할 박스의 중심
particleRadius: number, // 입자의 반지름
spawnJitter: number // 입자가 얼마나 엇나가도록 할지 설정 (jitter)
) {
let index = 0;
for (let x = 0; x < numToSpawn.x; x++) {
for (let y = 0; y < numToSpawn.y; y++) {
for (let z = 0; z < numToSpawn.z; z++) {
// 입자의 위치 계산
const spawnPos = new THREE.Vector3(
spawnCenter.x + x * particleRadius * 2,
spawnCenter.y + y * particleRadius * 2,
spawnCenter.z + z * particleRadius * 2
);
// Jitter 적용 (무작위로 약간 엇나가게 설정)
const jitter = new THREE.Vector3(
(Math.random() - 0.5) * 2 * particleRadius * spawnJitter,
(Math.random() - 0.5) * 2 * particleRadius * spawnJitter,
(Math.random() - 0.5) * 2 * particleRadius * spawnJitter
);
spawnPos.add(jitter); // Jitter를 스폰 위치에 추가
// 각 인스턴스의 변환 행렬 설정
const matrix = new THREE.Matrix4();
matrix.setPosition(spawnPos);
// InstancedMesh에 행렬 적용
particleMesh.setMatrixAt(index, matrix);
index++;
}
}
}
// InstancedMesh를 업데이트하여 변경 사항을 반영
particleMesh.instanceMatrix.needsUpdate = true;
}
여기서 jitter를 0으로 하고 출력하면,
반듯하게 나온다.
다만 여백이 너무 넓다.
입자간 거리를 0.1로 설정하고,
material을 phong shading으로 바꿨다.
gizmo 그리기
unity의 gizmo.
즉 디버깅에서 영역을 보기 쉽게하는 선을 그린다.
three.js에서는 material을 line으로 하는 mesh를 생성하면 된다.
three.js lineSegments
import * as THREE from 'three'
// 직육면체의 8개 꼭짓점 정의 (x, y, z 좌표)
const vertices = [
-1, -1, -1, // 0번 점
1, -1, -1, // 1번 점
1, 1, -1, // 2번 점
-1, 1, -1, // 3번 점
-1, -1, 1, // 4번 점
1, -1, 1, // 5번 점
1, 1, 1, // 6번 점
-1, 1, 1 // 7번 점
];
// 각 점을 연결하는 선분 정의 (점의 인덱스)
const edges = [
0, 1, 1, 2, 2, 3, 3, 0, // 밑면
4, 5, 5, 6, 6, 7, 7, 4, // 윗면
0, 4, 1, 5, 2, 6, 3, 7 // 옆면
];
export function drawBoundary(scene: THREE.Scene){
const lineGeometry = new THREE.BufferGeometry()
lineGeometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices,3))
lineGeometry.setIndex(edges);
const wireCube = new THREE.LineSegments(lineGeometry,
new THREE.MeshBasicMaterial({color: 'purple'}));
scene.add(wireCube)
}
이렇게 큐브 모양의 gizmo를 대체하여 선을 그렸는데,
저거 파티클이 거슬린다.
gui로 키고 끌 수 있게 해보자.
lil-gui
import GUI from "lil-gui";
const gui = new GUI();
export function init(){
}
export function addBoolean(name: string, value: boolean){
const object = {[name]: value};
gui.add(object, name);
}
gui 스크립트를 추가해서 on off 기능을 넣었다.
참고로 object의 key를 변수의 string 값을 받아서 넣는 방법이 대괄호라는 것을 알았다.
이렇게 만들었는데.
boolean값을 다른 스크립트에서 변경하는게 문제다.
다양한 방법이 있는데. 그냥 SPH object를 class로 만들어서 전달하는 형식으로 리팩토링하는게 좋겠다.
SPH class
import * as THREE from 'three'
import { GPUComputationRenderer } from 'three/examples/jsm/misc/GPUComputationRenderer';
import { Particle } from './particle';
export class SPH{
//#region SPH variable - general
public showSpheres: boolean = true;
public numToSpawn: THREE.Vector3 = new THREE.Vector3(10, 10, 10);
public totalParticles: number = this.numToSpawn.x * this.numToSpawn.y * this.numToSpawn.z;
public spawnCenter: THREE.Vector3 = new THREE.Vector3(0, 0, 0);
public particleRadius: number = 0.1;
//#endregion
//#region SPH variable - particle rendering
public particleMesh: THREE.Mesh;
public particleRenderSize: number = 8;
public material: THREE.Material;
//#endregion
//#region SPH variable - compute
public gpuCompute: GPUComputationRenderer;
public dtPosition: THREE.DataTexture;
public positionVariable: any;
public particles: Particle[];
//#endregion
//#region SPH variable - compute
// const gpuCompute = new GPUComputationRenderer(canvas.clientWidth, canvas.clientHeight, renderer)
// const dtPosition = gpuCompute.createTexture();
// const positionVariable = gpuCompute.addVariable('uCurrentPosition', computefragment, dtPosition )
// gpuCompute.setVariableDependencies(positionVariable, [positionVariable])
// gpuCompute.init()
// let particles: Particle[];
//#endregion
constructor(private canvas: HTMLElement, private renderer: THREE.WebGLRenderer) {
this.particleMesh = new THREE.Mesh();
this.gpuCompute = new GPUComputationRenderer(this.canvas.clientWidth, this.canvas.clientHeight, this.renderer);
}
public setShowSpheres(value: boolean){
this.showSpheres = value
if(this.showSpheres)
this.particleMesh.visible = true;
else
this.particleMesh.visible = false;
console.log(this.showSpheres)
}
public setShader(computefragment: string){
this.dtPosition = this.gpuCompute.createTexture();
this.positionVariable = this.gpuCompute.addVariable('uCurrentPosition', computefragment, this.dtPosition);
this.gpuCompute.setVariableDependencies(this.positionVariable, [this.positionVariable]);
this.gpuCompute.init();
}
}
위와 같이 SPH class를 따로 준비하고
public setShowSpheres(value: boolean){
this.showSpheres = value
if(this.showSpheres)
this.particleMesh.visible = true;
else
this.particleMesh.visible = false;
console.log(this.showSpheres)
}
three.js에서는 매 프레임마다 파티클을 그려내는게 아니므로,
particle mesh의 visible로 조정하기로 하였다.
debugGui.init();
debugGui.setShowSphere('showSpheres', SPH.showSpheres, SPH);
gui에 SPH객체를 전달하고
export function setShowSphere(name: string, value: boolean, SPH: _SPH){
const object = {[name]: value};
gui.add(object, name).onChange(value =>{
SPH.setShowSpheres(value);
})
}
gui 스크립트에서 SPH 객체를 전달받아 show sphere 함수를 실행한다.
이제 gui를 이용해서 on off 를 할 수 있다.
728x90
반응형
'개발 · 컴퓨터공학 > Physical Simulation' 카테고리의 다른 글
three.js GPGPU SPH - three.js GLSL shader version (6) | 2024.10.21 |
---|---|
three.js GPGPU SPH - Compute Shader Texture (3) | 2024.10.20 |
three.js GPGPU SPH 파티클 instanced mesh 생성하기 (3) | 2024.10.18 |
three.js GPGPU로 SPH 유체 시뮬레이션 개발 계획 (13) | 2024.10.06 |
Threejs Cloth Tailor 개발일지 - 물리 시뮬레이션 PhysicsObject class 디버깅, PBD cloth simulation 동작 (0) | 2024.06.02 |