Codels
1412 ポリゴン8.00 × 1.81 × 6.34 m
自己完結型の ES モジュールで、依存先は three のみです。ファクトリー関数を呼び出し、返された group をシーンに追加してください。
/**
 * Sand Dune — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,412 · Size (W × H × D): 8.00 × 1.81 × 6.34 m
 *
 * Usage:
 *   import { createSandDune } from './sand-dune.js';
 *   
 *   const model = createSandDune();
 *   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);
}

// Extrude a 2D outline along Z (centered). bevel = [thickness, size, segments].
function extrude(outline, depth, holes = [], bevel = null) {
  const shape = new THREE.Shape(outline.map(([x, y]) => new THREE.Vector2(x, y)));
  for (const hole of holes) shape.holes.push(new THREE.Path(hole.map(([x, y]) => new THREE.Vector2(x, y))));
  const options = bevel
    ? { depth, bevelEnabled: true, bevelThickness: bevel[0], bevelSize: bevel[1], bevelSegments: bevel[2] }
    : { depth, bevelEnabled: false };
  return new THREE.ExtrudeGeometry(shape, options).translate(0, 0, -depth / 2);
}

// Tube along a smooth curve through the given points.
function tube(path, radius, tubularSegments, radialSegments, closed = false) {
  const curve = new THREE.CatmullRomCurve3(path.map(([x, y, z]) => new THREE.Vector3(x, y, z)), closed);
  return new THREE.TubeGeometry(curve, tubularSegments, radius, radialSegments, closed);
}

export function createSandDune() {
  const root = new THREE.Group();
  root.name = 'SandDune';

  // Materials
  const sandMat = new THREE.MeshStandardMaterial({ name: 'sand', color: 0xd39d52, roughness: 0.95, flatShading: true, side: THREE.DoubleSide });
  const slipMat = new THREE.MeshStandardMaterial({ name: 'slip', color: 0xad8143, roughness: 0.95, flatShading: true, side: THREE.DoubleSide });
  const rippleMat = new THREE.MeshStandardMaterial({ name: 'ripple', color: 0xddb378, roughness: 0.95, flatShading: true });
  const stoneMat = new THREE.MeshStandardMaterial({ name: 'stone', color: 0xc9bba3, roughness: 0.9, flatShading: true });
  const shrubMat = new THREE.MeshStandardMaterial({ name: 'shrub', color: 0x8a7a4a, roughness: 0.95, flatShading: true });

  // Parts
  const dune = new THREE.Group();
  dune.name = 'dune';
  root.add(dune);
  const crest = new THREE.Mesh(extrude([[4.14, 0], [3.312, 0.234], [2.484, 0.576], [1.656, 0.954], [0.828, 1.368], [0, 1.8], [-0.342, 1.368], [-0.684, 0.954], [-1.026, 0.576], [-1.368, 0.234], [-1.71, 0]], 2), sandMat);
  crest.name = 'crest';
  crest.position.set(-0.21, 0, 0);
  crest.rotation.set(0, deg(-90), 0);
  dune.add(crest);
  const windwardRight = new THREE.Mesh(lathe([[1, 0], [0.8, 0.13], [0.6, 0.32], [0.4, 0.53], [0.2, 0.76], [0, 1]], 7, 0, Math.PI / 2), sandMat);
  windwardRight.name = 'windwardRight';
  windwardRight.position.set(0.79, 0, 0);
  windwardRight.scale.set(2.58, 1.8, 4.14);
  dune.add(windwardRight);
  const slipRight = new THREE.Mesh(lathe([[1, 0], [0.8, 0.13], [0.6, 0.32], [0.4, 0.53], [0.2, 0.76], [0, 1]], 7, Math.PI / 2, Math.PI / 2), slipMat);
  slipRight.name = 'slipRight';
  slipRight.position.set(0.79, 0, 0);
  slipRight.scale.set(2.58, 1.8, 1.71);
  dune.add(slipRight);
  const slipLeft = new THREE.Mesh(lathe([[1, 0], [0.8, 0.13], [0.6, 0.32], [0.4, 0.53], [0.2, 0.76], [0, 1]], 7, Math.PI, Math.PI / 2), slipMat);
  slipLeft.name = 'slipLeft';
  slipLeft.position.set(-1.21, 0, 0);
  slipLeft.scale.set(3.42, 1.8, 1.71);
  dune.add(slipLeft);
  const windwardLeft = new THREE.Mesh(lathe([[1, 0], [0.8, 0.13], [0.6, 0.32], [0.4, 0.53], [0.2, 0.76], [0, 1]], 7, Math.PI * 1.5, Math.PI / 2), sandMat);
  windwardLeft.name = 'windwardLeft';
  windwardLeft.position.set(-1.21, 0, 0);
  windwardLeft.scale.set(3.42, 1.8, 4.14);
  dune.add(windwardLeft);
  const ripples = new THREE.Group();
  ripples.name = 'ripples';
  root.add(ripples);
  const ripple1 = new THREE.Mesh(tube([[-2.4071, 0.2965, 2.8321], [-1.8707, 0.2585, 3.174], [-1.2652, 0.2762, 3.2298], [-0.9792, 0.3236, 3.1156], [-0.7252, 0.3313, 3.0969], [-0.4711, 0.288, 3.2018], [-0.2171, 0.2571, 3.2766], [0.0369, 0.2839, 3.2117], [0.291, 0.3291, 3.1023], [0.545, 0.3265, 3.1085], [0.8062, 0.2799, 3.2213], [1.2636, 0.2576, 3.186], [1.6735, 0.2923, 2.8594]], 0.034, 24, 3), rippleMat);
  ripple1.name = 'ripple1';
  ripples.add(ripple1);
  const ripple2 = new THREE.Mesh(tube([[-2.0164, 0.4841, 2.5465], [-1.5304, 0.5209, 2.6093], [-1.1304, 0.5454, 2.5786], [-0.8849, 0.5217, 2.636], [-0.6395, 0.4845, 2.7261], [-0.394, 0.4883, 2.7169], [-0.1486, 0.5276, 2.6218], [0.0969, 0.5448, 2.5802], [0.3423, 0.5147, 2.653], [0.5877, 0.4814, 2.7336], [0.8548, 0.4937, 2.7019], [1.2029, 0.5335, 2.5219], [1.5274, 0.5426, 2.2989]], 0.034, 24, 3), rippleMat);
  ripple2.name = 'ripple2';
  ripples.add(ripple2);
  const ripple3 = new THREE.Mesh(tube([[-2.4292, 0.7544, 1.5105], [-2.061, 0.7495, 1.856], [-1.6414, 0.7138, 2.1379], [-1.1791, 0.6997, 2.2317], [-0.879, 0.7278, 2.1702], [-0.5789, 0.7569, 2.1063], [-0.2789, 0.7444, 2.1337], [0.0212, 0.7086, 2.2121], [0.3213, 0.7019, 2.2268], [0.6214, 0.7342, 2.1562], [0.9431, 0.7581, 2.0893], [1.2912, 0.7387, 1.9899], [1.6292, 0.7043, 1.7669]], 0.034, 24, 3), rippleMat);
  ripple3.name = 'ripple3';
  ripples.add(ripple3);
  const ripple4 = new THREE.Mesh(tube([[-1.928, 0.9441, 1.4567], [-1.6074, 0.922, 1.677], [-1.2395, 0.9329, 1.7205], [-0.9427, 0.9608, 1.6598], [-0.6522, 0.965, 1.6509], [-0.3616, 0.9391, 1.7072], [-0.0711, 0.9213, 1.7463], [0.2195, 0.9375, 1.7108], [0.51, 0.9641, 1.6528], [0.7997, 0.9619, 1.6572], [1.0735, 0.9344, 1.6563], [1.3367, 0.9217, 1.509], [1.539, 0.9424, 1.2023]], 0.034, 24, 3), rippleMat);
  ripple4.name = 'ripple4';
  ripples.add(ripple4);
  const ripple5 = new THREE.Mesh(tube([[-1.7185, 1.1443, 1.1363], [-1.4656, 1.1656, 1.2108], [-1.2139, 1.1794, 1.2222], [-0.9376, 1.1655, 1.25], [-0.6609, 1.1443, 1.2925], [-0.3842, 1.1468, 1.2873], [-0.1075, 1.1694, 1.2421], [0.1693, 1.1789, 1.2231], [0.446, 1.1614, 1.2581], [0.7227, 1.1426, 1.2959], [0.938, 1.15, 1.2589], [1.1129, 1.1728, 1.1215], [1.2698, 1.1776, 0.9539]], 0.034, 24, 3), rippleMat);
  ripple5.name = 'ripple5';
  ripples.add(ripple5);
  const ripple6 = new THREE.Mesh(tube([[-1.6709, 1.3879, 0.5819], [-1.5452, 1.3855, 0.7019], [-1.4045, 1.3702, 0.807], [-1.2408, 1.3645, 0.8512], [-0.9892, 1.3764, 0.8282], [-0.7194, 1.389, 0.8041], [-0.4495, 1.3833, 0.815], [-0.1797, 1.368, 0.8449], [0.0902, 1.3655, 0.85], [0.3601, 1.3792, 0.8228], [0.6299, 1.3894, 0.8032], [0.8399, 1.3807, 0.816], [0.9653, 1.3663, 0.8005]], 0.034, 24, 3), rippleMat);
  ripple6.name = 'ripple6';
  ripples.add(ripple6);
  const dressing = new THREE.Group();
  dressing.name = 'dressing';
  root.add(dressing);
  const stone1 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.125, 0), 0.0225, 507), stoneMat);
  stone1.name = 'stone1';
  stone1.position.set(-1.6685, 0.0691, 4.2743);
  stone1.rotation.set(deg(-4.5568), deg(46.5823), deg(-7.6821));
  stone1.scale.set(1.3268, 0.532, 1);
  dressing.add(stone1);
  const stone2 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.1118, 0), 0.0201, 904), stoneMat);
  stone2.name = 'stone2';
  stone2.position.set(-0.3578, 0.0465, 4.4423);
  stone2.rotation.set(deg(-6.581), deg(205.1429), deg(4.4751));
  stone2.scale.set(1.3173, 0.4891, 1);
  dressing.add(stone2);
  const stone3 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.1435, 0), 0.0258, 582), stoneMat);
  stone3.name = 'stone3';
  stone3.position.set(0.7597, 0.0779, 4.319);
  stone3.rotation.set(deg(-0.6636), deg(209.5477), deg(-6.517));
  stone3.scale.set(1.2513, 0.516, 1);
  dressing.add(stone3);
  const dryShrub = new THREE.Group();
  dryShrub.name = 'dryShrub';
  dryShrub.position.set(-0.835, 0.0244, 4.476);
  dressing.add(dryShrub);
  const cylinder19 = new THREE.Mesh(new THREE.CylinderGeometry(0.0077, 0.0154, 0.2617, 3), shrubMat);
  cylinder19.position.set(0.0762, 0.1248, 0.0098);
  cylinder19.rotation.set(deg(3.6668), deg(-0.9753), deg(-29.7801));
  dryShrub.add(cylinder19);
  const cylinder20 = new THREE.Mesh(new THREE.CylinderGeometry(0.0077, 0.0154, 0.2777, 3), shrubMat);
  cylinder20.position.set(0.0371, 0.1248, 0.0673);
  cylinder20.rotation.set(deg(28.3098), deg(-3.8414), deg(-15.1481));
  dryShrub.add(cylinder20);
  const cylinder21 = new THREE.Mesh(new THREE.CylinderGeometry(0.0077, 0.0154, 0.2717, 3), shrubMat);
  cylinder21.position.set(-0.0323, 0.1248, 0.0697);
  cylinder21.rotation.set(deg(35.8316), deg(5.1215), deg(15.7521));
  dryShrub.add(cylinder21);
  const cylinder22 = new THREE.Mesh(new THREE.CylinderGeometry(0.0077, 0.0154, 0.2964, 3), shrubMat);
  cylinder22.position.set(-0.0738, 0.1248, 0.0211);
  cylinder22.rotation.set(deg(9.9572), deg(3.3533), deg(37.1467));
  dryShrub.add(cylinder22);
  const cylinder23 = new THREE.Mesh(new THREE.CylinderGeometry(0.0077, 0.0154, 0.3531, 3), shrubMat);
  cylinder23.position.set(-0.0706, 0.1248, -0.0302);
  cylinder23.rotation.set(deg(-16.6708), deg(-6.4688), deg(42.1833));
  dryShrub.add(cylinder23);
  const cylinder24 = new THREE.Mesh(new THREE.CylinderGeometry(0.0077, 0.0154, 0.2783, 3), shrubMat);
  cylinder24.position.set(-0.0361, 0.1248, -0.0678);
  cylinder24.rotation.set(deg(-37.1324), deg(-6.3592), deg(18.7825));
  dryShrub.add(cylinder24);
  const cylinder25 = new THREE.Mesh(new THREE.CylinderGeometry(0.0077, 0.0154, 0.2485, 3), shrubMat);
  cylinder25.position.set(0.0286, 0.1248, -0.0713);
  cylinder25.rotation.set(deg(-34.9331), deg(4.2008), deg(-13.2964));
  dryShrub.add(cylinder25);
  const dodecahedron26 = new THREE.Mesh(new THREE.DodecahedronGeometry(0.0672, 0), shrubMat);
  dodecahedron26.position.set(0, 0.0384, 0);
  dryShrub.add(dodecahedron26);

  root.traverse((o) => {
    if (o.isMesh) {
      o.castShadow = true;
      o.receiveShadow = true;
    }
  });
  return root;
}

export default createSandDune;

評価とコメント

—
まだ評価はありません。最初の評価をしてみましょう。
5
0
4
0
3
0
2
0
1
0

評価やコメントをするにはログインしてください。

0 件のコメント

まだコメントはありません。このモデルの使い方や改善できる点を共有しましょう。

関連モデル

同じカテゴリまたは似たタグ