삼각형 1.1천개1.53 × 8.00 × 1.58 m
코드에서 사용하기
에디터에서 리믹스cypress-tree.js · 230줄
독립형 ES 모듈이에요 — three에만 의존해요. 팩토리 함수를 호출하고 그룹을 씬에 추가하세요.
/**
* Cypress Tree — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
* Triangles: 1,100 · Size (W × H × D): 1.53 × 8.00 × 1.58 m
*
* Usage:
* import { createCypressTree } from './cypress-tree.js';
*
* const model = createCypressTree();
* 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;
}
// Revolve a 2D profile [[radius, y], ...] around the Y axis.
function lathe(points, segments, phiStart = 0, phiLength = Math.PI * 2) {
return new THREE.LatheGeometry(points.map(([x, y]) => new THREE.Vector2(x, y)), segments, phiStart, phiLength);
}
export function createCypressTree() {
const root = new THREE.Group();
root.name = 'CypressTree';
// Materials
const barkMat = new THREE.MeshStandardMaterial({ name: 'bark', color: 0x5d4636, roughness: 0.95, flatShading: true });
const leafMat = new THREE.MeshStandardMaterial({ name: 'leaf', color: 0x36643a, roughness: 0.9, flatShading: true });
const leafLightMat = new THREE.MeshStandardMaterial({ name: 'leafLight', color: 0x507c57, roughness: 0.85, flatShading: true });
const leafDarkMat = new THREE.MeshStandardMaterial({ name: 'leafDark', color: 0x2a4e31, roughness: 0.95, flatShading: true });
// Parts
const trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.144, 0.24, 0.96, 6), barkMat);
trunk.name = 'trunk';
trunk.position.set(0, 0.48, 0);
root.add(trunk);
const column = new THREE.Mesh(jitter(lathe([[0, 0.56], [0.2632, 0.56], [0.4795, 1.0808], [0.5848, 2.3456], [0.5673, 3.6848], [0.4912, 5.024], [0.3509, 6.2888], [0.1754, 7.3304], [0, 8]], 8), 0.034, 3), leafDarkMat);
column.name = 'column';
root.add(column);
const tuft1 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3172, 0), 0.0381, 123), leafMat);
tuft1.name = 'tuft1';
tuft1.position.set(0.3303, 0.9741, 0.025);
tuft1.rotation.set(deg(12.0539), deg(10.4936), 0);
tuft1.scale.set(0.9, 1.7, 0.9);
root.add(tuft1);
const tuft2 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3599, 0), 0.0432, 124), leafLightMat);
tuft2.name = 'tuft2';
tuft2.position.set(-0.3242, 1.3192, 0.2);
tuft2.rotation.set(deg(1.8678), deg(334.7794), 0);
tuft2.scale.set(0.9, 1.7, 0.9);
root.add(tuft2);
const tuft3 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3235, 0), 0.0388, 125), leafMat);
tuft3.name = 'tuft3';
tuft3.position.set(0.0099, 1.5095, -0.4025);
tuft3.rotation.set(deg(0.045), deg(116.4623), 0);
tuft3.scale.set(0.9, 1.7, 0.9);
root.add(tuft3);
const tuft4 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3767, 0), 0.0452, 126), leafDarkMat);
tuft4.name = 'tuft4';
tuft4.position.set(0.2956, 1.6239, 0.2703);
tuft4.rotation.set(deg(21.674), deg(343.09), 0);
tuft4.scale.set(0.9, 1.7, 0.9);
root.add(tuft4);
const tuft5 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3654, 0), 0.0438, 127), leafMat);
tuft5.name = 'tuft5';
tuft5.position.set(-0.4238, 1.9163, 0.0309);
tuft5.rotation.set(deg(6.289), deg(189.1715), 0);
tuft5.scale.set(0.9, 1.7, 0.9);
root.add(tuft5);
const tuft6 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3492, 0), 0.0419, 128), leafLightMat);
tuft6.name = 'tuft6';
tuft6.position.set(0.3654, 2.2744, -0.2714);
tuft6.rotation.set(deg(25.5916), deg(73.5592), 0);
tuft6.scale.set(0.9, 1.7, 0.9);
root.add(tuft6);
const tuft7 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3732, 0), 0.0448, 129), leafMat);
tuft7.name = 'tuft7';
tuft7.position.set(-0.0885, 2.3676, 0.4468);
tuft7.rotation.set(deg(15.1454), deg(216.6746), 0);
tuft7.scale.set(0.9, 1.7, 0.9);
root.add(tuft7);
const tuft8 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.4129, 0), 0.0495, 130), leafDarkMat);
tuft8.name = 'tuft8';
tuft8.position.set(-0.2358, 2.5559, -0.3778);
tuft8.rotation.set(deg(8.7897), deg(148.568), 0);
tuft8.scale.set(0.9, 1.7, 0.9);
root.add(tuft8);
const tuft9 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3702, 0), 0.0444, 131), leafMat);
tuft9.name = 'tuft9';
tuft9.position.set(0.3947, 3.0736, 0.2113);
tuft9.rotation.set(deg(14.313), deg(349.3623), 0);
tuft9.scale.set(0.9, 1.7, 0.9);
root.add(tuft9);
const tuft10 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3803, 0), 0.0456, 132), leafLightMat);
tuft10.name = 'tuft10';
tuft10.position.set(-0.3949, 3.3112, 0.2005);
tuft10.rotation.set(deg(2.3425), deg(266.2875), 0);
tuft10.scale.set(0.9, 1.7, 0.9);
root.add(tuft10);
const tuft11 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.4062, 0), 0.0487, 133), leafMat);
tuft11.name = 'tuft11';
tuft11.position.set(0.2419, 3.5554, -0.3613);
tuft11.rotation.set(deg(26.9599), deg(134.6857), 0);
tuft11.scale.set(0.9, 1.7, 0.9);
root.add(tuft11);
const tuft12 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3979, 0), 0.0478, 134), leafDarkMat);
tuft12.name = 'tuft12';
tuft12.position.set(0.2156, 3.7356, 0.3747);
tuft12.rotation.set(deg(22.407), deg(210.0276), 0);
tuft12.scale.set(0.9, 1.7, 0.9);
root.add(tuft12);
const tuft13 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3665, 0), 0.044, 135), leafMat);
tuft13.name = 'tuft13';
tuft13.position.set(-0.4186, 3.8054, -0.1183);
tuft13.rotation.set(deg(21.2292), deg(86.3043), 0);
tuft13.scale.set(0.9, 1.7, 0.9);
root.add(tuft13);
const tuft14 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.4003, 0), 0.048, 136), leafLightMat);
tuft14.name = 'tuft14';
tuft14.position.set(0.4112, 4.0383, -0.0647);
tuft14.rotation.set(deg(10.0491), deg(329.0812), 0);
tuft14.scale.set(0.9, 1.7, 0.9);
root.add(tuft14);
const tuft15 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3569, 0), 0.0428, 137), leafMat);
tuft15.name = 'tuft15';
tuft15.position.set(-0.2534, 4.5013, 0.3109);
tuft15.rotation.set(deg(25.76), deg(329.1092), 0);
tuft15.scale.set(0.9, 1.7, 0.9);
root.add(tuft15);
const tuft16 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3855, 0), 0.0463, 138), leafDarkMat);
tuft16.name = 'tuft16';
tuft16.position.set(-0.113, 4.7742, -0.3642);
tuft16.rotation.set(deg(9.1265), deg(4.9754), 0);
tuft16.scale.set(0.9, 1.7, 0.9);
root.add(tuft16);
const tuft17 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3607, 0), 0.0433, 139), leafMat);
tuft17.name = 'tuft17';
tuft17.position.set(0.2707, 4.8793, 0.2679);
tuft17.rotation.set(deg(24.7343), deg(243.5511), 0);
tuft17.scale.set(0.9, 1.7, 0.9);
root.add(tuft17);
const tuft18 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3466, 0), 0.0416, 140), leafLightMat);
tuft18.name = 'tuft18';
tuft18.position.set(-0.3451, 5.2984, 0.0493);
tuft18.rotation.set(deg(9.0693), deg(9.9762), 0);
tuft18.scale.set(0.9, 1.7, 0.9);
root.add(tuft18);
const tuft19 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3072, 0), 0.0369, 141), leafMat);
tuft19.name = 'tuft19';
tuft19.position.set(0.1913, 5.375, -0.2916);
tuft19.rotation.set(deg(1.4462), deg(333.5274), 0);
tuft19.scale.set(0.9, 1.7, 0.9);
root.add(tuft19);
const tuft20 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3428, 0), 0.0411, 142), leafDarkMat);
tuft20.name = 'tuft20';
tuft20.position.set(0.0362, 5.7051, 0.3063);
tuft20.rotation.set(deg(15.9015), deg(243.0827), 0);
tuft20.scale.set(0.9, 1.7, 0.9);
root.add(tuft20);
const tuft21 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3225, 0), 0.0387, 143), leafMat);
tuft21.name = 'tuft21';
tuft21.position.set(-0.2472, 5.7538, -0.1831);
tuft21.rotation.set(deg(20.9057), deg(192.6207), 0);
tuft21.scale.set(0.9, 1.7, 0.9);
root.add(tuft21);
const tuft22 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3033, 0), 0.0364, 144), leafLightMat);
tuft22.name = 'tuft22';
tuft22.position.set(0.2822, 6.0409, -0.0133);
tuft22.rotation.set(deg(9.4011), deg(261.764), 0);
tuft22.scale.set(0.9, 1.7, 0.9);
root.add(tuft22);
const tuft23 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.2641, 0), 0.0317, 145), leafMat);
tuft23.name = 'tuft23';
tuft23.position.set(-0.1926, 6.498, 0.132);
tuft23.rotation.set(deg(17.2796), deg(158.2811), 0);
tuft23.scale.set(0.9, 1.7, 0.9);
root.add(tuft23);
const tuft24 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.2821, 0), 0.0339, 146), leafDarkMat);
tuft24.name = 'tuft24';
tuft24.position.set(0.0119, 6.7192, -0.1957);
tuft24.rotation.set(deg(8.0412), deg(182.1798), 0);
tuft24.scale.set(0.9, 1.7, 0.9);
root.add(tuft24);
const tuft25 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.257, 0), 0.0308, 147), leafMat);
tuft25.name = 'tuft25';
tuft25.position.set(0.063, 6.7544, 0.1853);
tuft25.rotation.set(deg(17.8313), deg(21.082), 0);
tuft25.scale.set(0.9, 1.7, 0.9);
root.add(tuft25);
const tuft26 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.246, 0), 0.0295, 148), leafLightMat);
tuft26.name = 'tuft26';
tuft26.position.set(-0.1575, 6.9857, -0.0402);
tuft26.rotation.set(deg(11.7424), deg(36.3086), 0);
tuft26.scale.set(0.9, 1.7, 0.9);
root.add(tuft26);
const tip = new THREE.Mesh(new THREE.ConeGeometry(0.136, 0.744, 6), leafLightMat);
tip.name = 'tip';
tip.position.set(0, 7.628, 0);
root.add(tip);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createCypressTree;
평점 및 댓글
댓글 0개
아직 댓글이 없어요. 이 모델을 어떻게 사용했는지, 어떤 점이 더 나아질 수 있을지 공유해 주세요.
관련 모델
같은 카테고리 또는 비슷한 태그









