1000 三角面5.07 × 2.81 × 8.48 m
在代码中使用
在编辑器中改编whale.js · 150 行
自包含的 ES 模块,仅依赖 three。调用工厂函数,然后把返回的 group 添加到场景中。
/**
* Whale — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the model (it floats — pickups, planets)
* Triangles: 1,000 · Size (W × H × D): 5.07 × 2.81 × 8.48 m
*
* Usage:
* import { createWhale } from './whale.js';
*
* const model = createWhale();
* model.position.set(0, 0, 0);
* scene.add(model);
*/
import * as THREE from 'three';
const deg = THREE.MathUtils.degToRad;
// 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 createWhale() {
const root = new THREE.Group();
root.name = 'Whale';
// Materials
const bodyMat = new THREE.MeshStandardMaterial({ name: 'body', color: 0x3f6fb4, roughness: 0.55, flatShading: true });
const bellyMat = new THREE.MeshStandardMaterial({ name: 'belly', color: 0xe2ebf0, roughness: 0.6, flatShading: true });
const finMat = new THREE.MeshStandardMaterial({ name: 'fin', color: 0x365e99, roughness: 0.55, flatShading: true });
const eyeMat = new THREE.MeshStandardMaterial({ name: 'eye', color: 0x151318, roughness: 0.25, flatShading: true });
const shineMat = new THREE.MeshStandardMaterial({ name: 'shine', color: 0xffffff, roughness: 0.3, flatShading: true });
// Shared geometries
const sphereGeo = new THREE.SphereGeometry(0.022, 8, 6);
const sphereGeo2 = new THREE.SphereGeometry(0.0075, 4, 3);
const boxGeo = new THREE.BoxGeometry(0.012, 0.01, 0.03);
// Parts
const whale = new THREE.Group();
whale.name = 'whale';
whale.position.set(0, 0.2223, 0);
whale.scale.setScalar(8);
root.add(whale);
const body = new THREE.Group();
body.name = 'body';
whale.add(body);
const bodyShell = new THREE.Mesh(lathe([[0, -0.47], [0.035, -0.45], [0.06, -0.38], [0.1, -0.26], [0.145, -0.1], [0.168, 0.04], [0.172, 0.18], [0.158, 0.3], [0.125, 0.4], [0.07, 0.465], [0, 0.49]], 12), bodyMat);
bodyShell.name = 'bodyShell';
bodyShell.rotation.set(deg(90), 0, 0);
bodyShell.scale.set(1, 1, 0.86);
body.add(bodyShell);
const bellyShell = new THREE.Mesh(lathe([[0, -0.4512], [0.0333, -0.432], [0.057, -0.3648], [0.095, -0.2496], [0.1377, -0.096], [0.1596, 0.0384], [0.1634, 0.1728], [0.1501, 0.288], [0.1188, 0.384], [0.0665, 0.4464], [0, 0.4704]], 12), bellyMat);
bellyShell.name = 'bellyShell';
bellyShell.position.set(0, -0.038, 0.004);
bellyShell.rotation.set(deg(90), 0, 0);
bellyShell.scale.set(0.93, 1, 0.774);
body.add(bellyShell);
const mouthL = new THREE.Mesh(tube([[0.0286, -0.0425, 0.47], [0.0869, -0.0584, 0.42], [0.1388, -0.0582, 0.32], [0.1559, -0.0597, 0.22], [0.1681, -0.0414, 0.16]], 0.006, 12, 4), eyeMat);
mouthL.name = 'mouthL';
body.add(mouthL);
const eyeL = new THREE.Mesh(sphereGeo, eyeMat);
eyeL.name = 'eyeL';
eyeL.position.set(0.1693, -0.0102, 0.2);
eyeL.scale.set(0.6, 1, 1);
body.add(eyeL);
const eyeShineL = new THREE.Mesh(sphereGeo2, shineMat);
eyeShineL.name = 'eyeShineL';
eyeShineL.position.set(0.1813, -0.0012, 0.206);
body.add(eyeShineL);
const mouthR = new THREE.Mesh(tube([[-0.0286, -0.0425, 0.47], [-0.0869, -0.0584, 0.42], [-0.1388, -0.0582, 0.32], [-0.1559, -0.0597, 0.22], [-0.1681, -0.0414, 0.16]], 0.006, 12, 4), eyeMat);
mouthR.name = 'mouthR';
body.add(mouthR);
const eyeR = new THREE.Mesh(sphereGeo, eyeMat);
eyeR.name = 'eyeR';
eyeR.position.set(-0.1693, -0.0102, 0.2);
eyeR.scale.set(0.6, 1, 1);
body.add(eyeR);
const eyeShineR = new THREE.Mesh(sphereGeo2, shineMat);
eyeShineR.name = 'eyeShineR';
eyeShineR.position.set(-0.1813, -0.0012, 0.206);
body.add(eyeShineR);
const blowhole = new THREE.Mesh(boxGeo, eyeMat);
blowhole.name = 'blowhole';
blowhole.position.set(0.01, 0.1369, 0.27);
blowhole.rotation.set(0, deg(15), 0);
body.add(blowhole);
const blowhole2 = new THREE.Mesh(boxGeo, eyeMat);
blowhole2.name = 'blowhole';
blowhole2.position.set(-0.01, 0.1369, 0.27);
blowhole2.rotation.set(0, deg(-15), 0);
body.add(blowhole2);
const dorsalFin = new THREE.Mesh(extrude([[-0.06, 0], [0.05, 0], [-0.03, 0.045], [-0.05, 0.04]], 0.02), finMat);
dorsalFin.name = 'dorsalFin';
dorsalFin.position.set(0, 0.0975, -0.2);
dorsalFin.rotation.set(0, deg(90), 0);
body.add(dorsalFin);
const tail = new THREE.Group();
tail.name = 'tail';
tail.position.set(0, 0, -0.44);
whale.add(tail);
const flukes = new THREE.Mesh(extrude([[0, 0.03], [0.07, -0.02], [0.17, -0.07], [0.23, -0.13], [0.16, -0.13], [0.07, -0.11], [0, -0.08], [-0.07, -0.11], [-0.16, -0.13], [-0.23, -0.13], [-0.17, -0.07], [-0.07, -0.02]], 0.022), finMat);
flukes.name = 'flukes';
flukes.rotation.set(deg(90), 0, 0);
tail.add(flukes);
const finL = new THREE.Group();
finL.name = 'finL';
finL.position.set(0.1206, -0.0934, 0.14);
finL.rotation.set(deg(18.1971), deg(30.4269), deg(-32.9875));
whale.add(finL);
const flipperL = new THREE.Mesh(extrude([[0, 0.035], [0.09, 0.045], [0.2, 0.03], [0.26, 0.008], [0.25, -0.012], [0.18, -0.02], [0.06, -0.03], [0, -0.03]], 0.018), finMat);
flipperL.name = 'flipperL';
flipperL.rotation.set(deg(90), 0, 0);
finL.add(flipperL);
const finR = new THREE.Group();
finR.name = 'finR';
finR.position.set(-0.1206, -0.0934, 0.14);
finR.rotation.set(deg(18.1971), deg(-30.4269), deg(32.9875));
whale.add(finR);
const flipperR = new THREE.Mesh(extrude([[0, 0.035], [-0.09, 0.045], [-0.2, 0.03], [-0.26, 0.008], [-0.25, -0.012], [-0.18, -0.02], [-0.06, -0.03], [0, -0.03]], 0.018), finMat);
flipperR.name = 'flipperR';
flipperR.rotation.set(deg(90), 0, 0);
finR.add(flipperR);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createWhale;
评分与评论
0 条评论
暂无评论。分享一下你是如何使用这个模型的,或者有哪些可以改进的地方。
相关模型
同一分类或相似标签









