삼각형 504개1.40 × 1.49 × 1.40 m
코드에서 사용하기
에디터에서 리믹스crystal-cluster.js · 195줄
독립형 ES 모듈이에요 — three에만 의존해요. 팩토리 함수를 호출하고 그룹을 씬에 추가하세요.
/**
* Crystal Cluster — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
* Triangles: 504 · Size (W × H × D): 1.40 × 1.49 × 1.40 m
*
* Usage:
* import { createCrystalCluster } from './crystal-cluster.js';
*
* const model = createCrystalCluster();
* model.position.set(0, 0, 0);
* scene.add(model);
*/
import * as THREE from 'three';
const deg = THREE.MathUtils.degToRad;
// Deterministic vertex displacement (organic low-poly look).
function jitter(geometry, amount, seed) {
const pos = geometry.attributes.position;
const h = (a, b, c, s) => {
let x = Math.imul(a, 73856093) ^ Math.imul(b, 19349663) ^ Math.imul(c, 83492791) ^ Math.imul(s, 2654435761);
x = Math.imul(x ^ (x >>> 16), 2246822507);
x = Math.imul(x ^ (x >>> 13), 3266489909);
return ((x ^ (x >>> 16)) >>> 0) / 4294967295 - 0.5;
};
for (let i = 0; i < pos.count; i++) {
const x = pos.getX(i), y = pos.getY(i), z = pos.getZ(i);
const qx = Math.round(x * 1000), qy = Math.round(y * 1000), qz = Math.round(z * 1000);
pos.setXYZ(i, x + h(qx, qy, qz, seed) * 2 * amount, y + h(qx, qy, qz, seed + 1) * 2 * amount, z + h(qx, qy, qz, seed + 2) * 2 * amount);
}
pos.needsUpdate = true;
geometry.computeVertexNormals();
return geometry;
}
export function createCrystalCluster() {
const root = new THREE.Group();
root.name = 'CrystalCluster';
// Materials
const crystalMat = new THREE.MeshStandardMaterial({ name: 'crystal', color: 0x7b4fff, roughness: 0.2, metalness: 0.1, emissive: 0x7b4fff, emissiveIntensity: 0.45, flatShading: true });
const crystalLightMat = new THREE.MeshStandardMaterial({ name: 'crystalLight', color: 0xa98dff, roughness: 0.15, metalness: 0.1, emissive: 0x7b4fff, emissiveIntensity: 0.55, flatShading: true });
const rockMat = new THREE.MeshStandardMaterial({ name: 'rock', color: 0x4a4652, roughness: 0.95, flatShading: true });
// Parts
const cluster = new THREE.Group();
cluster.name = 'cluster';
root.add(cluster);
const base = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(1, 1), 0.14, 3), rockMat);
base.name = 'base';
base.position.set(0, 0.2092, 0);
base.scale.set(0.5, 0.2, 0.44);
cluster.add(base);
const crystal1 = new THREE.Group();
crystal1.name = 'crystal1';
crystal1.position.set(0, 0.2514, 0);
crystal1.rotation.set(deg(0.987), deg(6.923), deg(-2.9225));
cluster.add(crystal1);
const prism = new THREE.Mesh(new THREE.CylinderGeometry(0.13, 0.1196, 0.95, 6), crystalMat);
prism.name = 'prism';
prism.position.set(0, 0.475, 0);
crystal1.add(prism);
const tip = new THREE.Mesh(new THREE.ConeGeometry(0.13, 0.286, 6), crystalMat);
tip.name = 'tip';
tip.position.set(0, 1.093, 0);
crystal1.add(tip);
const crystal2 = new THREE.Group();
crystal2.name = 'crystal2';
crystal2.position.set(-0.1138, 0.2442, 0.125);
crystal2.rotation.set(deg(9.0983), deg(33.2754), deg(17.4994));
cluster.add(crystal2);
const prism2 = new THREE.Mesh(new THREE.CylinderGeometry(0.1001, 0.0921, 0.6109, 6), crystalLightMat);
prism2.name = 'prism';
prism2.position.set(0, 0.3055, 0);
crystal2.add(prism2);
const tip2 = new THREE.Mesh(new THREE.ConeGeometry(0.1001, 0.2203, 6), crystalLightMat);
tip2.name = 'tip';
tip2.position.set(0, 0.7211, 0);
crystal2.add(tip2);
const crystal3 = new THREE.Group();
crystal3.name = 'crystal3';
crystal3.position.set(0.0467, 0.2403, -0.1931);
crystal3.rotation.set(deg(-22.345), deg(57.0069), deg(-12.0242));
cluster.add(crystal3);
const prism3 = new THREE.Mesh(new THREE.CylinderGeometry(0.097, 0.0892, 0.5228, 6), crystalMat);
prism3.name = 'prism';
prism3.position.set(0, 0.2614, 0);
crystal3.add(prism3);
const tip3 = new THREE.Mesh(new THREE.ConeGeometry(0.097, 0.2133, 6), crystalMat);
tip3.name = 'tip';
tip3.position.set(0, 0.6294, 0);
crystal3.add(tip3);
const crystal4 = new THREE.Group();
crystal4.name = 'crystal4';
crystal4.position.set(0.1639, 0.2365, 0.174);
crystal4.rotation.set(deg(25.7801), deg(6.4064), deg(-18.505));
cluster.add(crystal4);
const prism4 = new THREE.Mesh(new THREE.CylinderGeometry(0.0671, 0.0617, 0.4778, 6), crystalMat);
prism4.name = 'prism';
prism4.position.set(0, 0.2389, 0);
crystal4.add(prism4);
const tip4 = new THREE.Mesh(new THREE.ConeGeometry(0.0671, 0.1476, 6), crystalMat);
tip4.name = 'tip';
tip4.position.set(0, 0.5516, 0);
crystal4.add(tip4);
const crystal5 = new THREE.Group();
crystal5.name = 'crystal5';
crystal5.position.set(-0.2707, 0.2328, -0.076);
crystal5.rotation.set(deg(-18.1232), deg(5.1112), deg(37.694));
cluster.add(crystal5);
const prism5 = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.0736, 0.4863, 6), crystalLightMat);
prism5.name = 'prism';
prism5.position.set(0, 0.2432, 0);
crystal5.add(prism5);
const tip5 = new THREE.Mesh(new THREE.ConeGeometry(0.08, 0.1761, 6), crystalLightMat);
tip5.name = 'tip';
tip5.position.set(0, 0.5744, 0);
crystal5.add(tip5);
const crystal6 = new THREE.Group();
crystal6.name = 'crystal6';
crystal6.position.set(0.2725, 0.2291, -0.1268);
crystal6.rotation.set(deg(-0.5359), deg(27.4831), deg(-51.3398));
cluster.add(crystal6);
const prism6 = new THREE.Mesh(new THREE.CylinderGeometry(0.071, 0.0653, 0.3969, 6), crystalMat);
prism6.name = 'prism';
prism6.position.set(0, 0.1984, 0);
crystal6.add(prism6);
const tip6 = new THREE.Mesh(new THREE.ConeGeometry(0.071, 0.1562, 6), crystalMat);
tip6.name = 'tip';
tip6.position.set(0, 0.475, 0);
crystal6.add(tip6);
const crystal7 = new THREE.Group();
crystal7.name = 'crystal7';
crystal7.position.set(-0.132, 0.2253, 0.2661);
crystal7.rotation.set(deg(35.6281), deg(36.8381), deg(23.4026));
cluster.add(crystal7);
const prism7 = new THREE.Mesh(new THREE.CylinderGeometry(0.0694, 0.0639, 0.3539, 6), crystalMat);
prism7.name = 'prism';
prism7.position.set(0, 0.177, 0);
crystal7.add(prism7);
const tip7 = new THREE.Mesh(new THREE.ConeGeometry(0.0694, 0.1527, 6), crystalMat);
tip7.name = 'tip';
tip7.position.set(0, 0.4302, 0);
crystal7.add(tip7);
const shard1 = new THREE.Group();
shard1.name = 'shard1';
shard1.position.set(0.5844, 0.0321, -0.3957);
shard1.rotation.set(deg(10.2125), deg(38.9111), deg(-62.7794));
cluster.add(shard1);
const prism8 = new THREE.Mesh(new THREE.CylinderGeometry(0.0446, 0.041, 0.1114, 6), crystalMat);
prism8.name = 'prism';
prism8.position.set(0, 0.0557, 0);
shard1.add(prism8);
const tip8 = new THREE.Mesh(new THREE.ConeGeometry(0.0446, 0.0981, 6), crystalMat);
tip8.name = 'tip';
tip8.position.set(0, 0.1604, 0);
shard1.add(tip8);
const shard2 = new THREE.Group();
shard2.name = 'shard2';
shard2.position.set(-0.2601, 0.03, 0.544);
shard2.rotation.set(deg(25.6908), deg(49.7994), deg(33.2307));
cluster.add(shard2);
const prism9 = new THREE.Mesh(new THREE.CylinderGeometry(0.0404, 0.0372, 0.1795, 6), crystalLightMat);
prism9.name = 'prism';
prism9.position.set(0, 0.0898, 0);
shard2.add(prism9);
const tip9 = new THREE.Mesh(new THREE.ConeGeometry(0.0404, 0.089, 6), crystalLightMat);
tip9.name = 'tip';
tip9.position.set(0, 0.224, 0);
shard2.add(tip9);
const shard3 = new THREE.Group();
shard3.name = 'shard3';
shard3.position.set(-0.3582, 0.0289, -0.5171);
shard3.rotation.set(deg(-78.5459), deg(29.2065), deg(35.8379));
cluster.add(shard3);
const prism10 = new THREE.Mesh(new THREE.CylinderGeometry(0.0368, 0.0338, 0.1077, 6), crystalMat);
prism10.name = 'prism';
prism10.position.set(0, 0.0539, 0);
shard3.add(prism10);
const tip10 = new THREE.Mesh(new THREE.ConeGeometry(0.0368, 0.0809, 6), crystalMat);
tip10.name = 'tip';
tip10.position.set(0, 0.1482, 0);
shard3.add(tip10);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createCrystalCluster;
평점 및 댓글
댓글 0개
아직 댓글이 없어요. 이 모델을 어떻게 사용했는지, 어떤 점이 더 나아질 수 있을지 공유해 주세요.
관련 모델
같은 카테고리 또는 비슷한 태그









