three.js, WebGPU에서 지오메트리를 구성하는 좌표에 파티클 배치하기

위의 결과를 위해서 3가지 방식이 있습니다. 첫번째는 …
private setupModel() {
const knotGeometry = new THREE.TorusKnotGeometry(1, 0.3, 256, 64);
const positionAttribute = knotGeometry.attributes.position;
const count = positionAttribute.count;
const instancePositions = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
instancePositions[i * 3] = positionAttribute.getX(i);
instancePositions[i * 3 + 1] = positionAttribute.getY(i);
instancePositions[i * 3 + 2] = positionAttribute.getZ(i);
}
const spriteMaterial = new THREE.SpriteNodeMaterial({
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending
});
const dist = uv().distance(vec2(0.5, 0.5));
const alphaNode = dist.smoothstep(0.5, 0.0);
spriteMaterial.colorNode = color(0xff0000);
spriteMaterial.opacityNode = alphaNode;
const instancePositionNode = instancedBufferAttribute(
new THREE.InstancedBufferAttribute(instancePositions, 3)
);
spriteMaterial.positionNode = instancePositionNode;
spriteMaterial.scaleNode = vec2(.05);
// const spriteGeometry = new THREE.PlaneGeometry(0.08, 0.08);
// const instancedSprites = new THREE.Mesh(spriteGeometry, spriteMaterial);
const instancedSprites = new THREE.Sprite(spriteMaterial); // 상기 코드와 동일
instancedSprites.count = count;
this.scene.add(instancedSprites);
}
두번째 방식은 ...
private setupModel() {
const knotGeometry = new THREE.TorusKnotGeometry(1, 0.3, 256, 64);
const positionAttribute = knotGeometry.attributes.position;
const count = positionAttribute.count; // 정점 개수
const spriteMaterial = new THREE.SpriteNodeMaterial({
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending
});
const dist = uv().distance(vec2(0.5, 0.5));
const alphaNode = dist.smoothstep(0.5, 0.0);
spriteMaterial.colorNode = color(0xff0000);
spriteMaterial.opacityNode = alphaNode;
spriteMaterial.vertexNode = billboarding({
horizontal: true,
vertical: true,
horizontalRotation: false
});
const spriteGeometry = new THREE.PlaneGeometry(0.05, 0.05);
const instancedSprites = new THREE.InstancedMesh(spriteGeometry, spriteMaterial, count);
const dummy = new THREE.Object3D();
const vertex = new THREE.Vector3();
for (let i = 0; i < count; i++) {
vertex.fromBufferAttribute(positionAttribute, i);
dummy.position.copy(vertex);
dummy.updateMatrix();
instancedSprites.setMatrixAt(i, dummy.matrix);
}
instancedSprites.instanceMatrix.needsUpdate = true;
this.scene.add(instancedSprites);
}
세번째 방식은 다음과 같습니다.
private setupModel() {
const knotGeometry = new THREE.TorusKnotGeometry(1, 0.3, 256, 64);
const positionAttr = knotGeometry.attributes.position;
const count = positionAttr.count;
const positionArray = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
positionArray[i * 3] = positionAttr.getX(i);
positionArray[i * 3 + 1] = positionAttr.getY(i);
positionArray[i * 3 + 2] = positionAttr.getZ(i);
}
const positionStorageBuffer = new THREE.StorageBufferAttribute(positionArray, 3);
const positionStorageNode = storage(positionStorageBuffer, 'vec3', count);
const spriteMaterial = new THREE.SpriteNodeMaterial({
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending
});
const dist = uv().distance(vec2(0.5, 0.5));
const alphaNode = dist.smoothstep(0.5, 0.0);
spriteMaterial.colorNode = color(0xff0000);
spriteMaterial.opacityNode = alphaNode;
spriteMaterial.positionNode = positionStorageNode.element(THREE.TSL.instanceIndex);
const spriteGeometry = new THREE.PlaneGeometry(0.08, 0.08);
const instancedSprites = new THREE.Mesh(spriteGeometry, spriteMaterial);
instancedSprites.count = count;
this.scene.add(instancedSprites);
}
three.js, TSL에서 compute 쉐이더를 위한 버퍼 생성 코드
컴퓨트 쉐이더를 위한 버퍼를 생성하기 위한 일반적인 코드는 다음과 같습니다.
const positionBuffer = new THREE.StorageInstancedBufferAttribute(count, 3, Float32Array) const positionStorage = storage(positionBuffer, 'vec3', count)
위의 코드는 다음 한줄로 줄일 수 있습니다.
const positionStorage = instancedArray(count, 'vec3')
three.js, 이동, 회전, 스케일에 대한 행렬 만들기
2가지 방식이 있습니다. 먼저 Object3D의 position, scale, rotation을 지정해 행렬을 뽑아내는 다음의 방식입니다.
const matrixArray = new Float32Array(count * 16);
const matrixBuffer = new THREE.InstancedBufferAttribute(matrixArray, 16);
const dummy = new THREE.Object3D();
for (let i = 0; i < count; i++) {
dummy.position.set(i * 2, 0, 0);
dummy.rotation.y = Math.PI * 0.25 * i;
dummy.updateMatrix();
// Matrix4 데이터를 Float32Array의 i * 16 위치에 복사
dummy.matrix.toArray(matrixArray, i * 16);
}
2번째는 직접 행렬을 통해서 얻는 아래의 방식입니다.
const matricesArray = new Float32Array(count * 16)
const matricesBuffer = new THREE.InstancedBufferAttribute(matricesArray, 16)
for (let i = 0; i < count; i++) {
const progress = i / (count - 1)
const position = new THREE.Vector3((progress - 0.5) * 4, 0, 0)
const scale = new THREE.Vector3(1, 1, 1)
const rotation = new THREE.Quaternion()
.setFromEuler(new THREE.Euler(0, progress * 3, 0))
const matrix = new THREE.Matrix4().compose(position, rotation, scale)
matrix.toArray(matricesArray, i * 16)
}
three.js, TSL에서 Fresnel을 위한 내적값 연산
import { dot, positionViewDirection, normalView } from 'three/tsl'
dot(positionViewDirection, normalView)
