主题
风车
一个风车的案例,模型用代码编写,可以改变风车的页片角度,不需要模型师修改模型点击下载案例
js
import "./style.scss";
import { DirectionalLight, AmbientLight, Clock, MeshLambertMaterial, CircleGeometry, ExtrudeGeometry, Shape, LatheGeometry, Path, Mesh, Vector2 } from "three";
import { mergeGeometries } from "three/addons/utils/BufferGeometryUtils.js";
import { Pane } from "tweakpane";
import { scene, camera, renderer, controls } from "./base.js";
const clock = new Clock();
let t = 0;
function animate() {
// 获取时间增量
const dt = clock.getDelta();
t += dt;
// 旋转速度与时间增量相关
windmillRotor.rotation.z += PARAMS.speed * dt;
controls.update();
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
camera.position.set(5, -5, 20);
const light = new DirectionalLight(0xffffff, Math.PI * 1.5);
light.position.setScalar(1);
scene.add(light, new AmbientLight(0xffffff, Math.PI * 0.5));
class WindmillRotor extends Mesh {
// mainRadius 风页长度
// bladeAmount 风页数量
// bulbRadius 中间转轴大小
constructor(mainRadius = 7, bladeAmount = 3, bulbRadius = 0.5) {
super(
mergeGeometries([
new LatheGeometry(
new Path()
.moveTo(bulbRadius, 0)
.lineTo(bulbRadius, 0)
.absarc(0, 1, bulbRadius, 0, Math.PI * 0.5)
.getPoints(50),
72
).rotateX(Math.PI * 0.5),
new CircleGeometry(bulbRadius, 72).rotateX(Math.PI),
]).translate(0, 0, -0.5),
new MeshLambertMaterial({ color: 0xeeeeee })
);
const bladeThickness = 0.025;
// 风页模型数据
const bladeG = new ExtrudeGeometry(
new Shape().moveTo(0, bulbRadius).splineThru(
[
[0, mainRadius],
[0.1, mainRadius],
[0.5, bulbRadius + 1],
[0.1, bulbRadius],
].map((p) => {
return new Vector2(...p);
})
),
{
steps: 1, // 用于沿着挤出样条的深度细分的点的数量,默认值为1
depth: 0.001, // 挤出的形状的深度,默认值为1
bevelEnabled: true, // 对挤出的形状应用是否斜角,默认值为true
bevelThickness: bladeThickness, // 设置原始形状上斜角的厚度。默认值为0.2
bevelSize: bladeThickness, // 斜角与原始形状轮廓之间的延伸距离
}
)
.translate(0, 0, -bladeThickness)
.rotateY(Math.PI * 0.5);
const bladeM = new MeshLambertMaterial({ color: 0x444444 });
// bladeAmount 生成三个风页
this.blades = Array.from({ length: bladeAmount }, (_, idx) => {
const blade = new Mesh(bladeG, bladeM);
blade.rotation.z = idx * ((Math.PI * 2) / bladeAmount);
blade.rotation.order = "ZYX"; // important!
this.add(blade);
return blade;
});
this.setBladesAngle(Math.PI * 0.25);
}
setBladesAngle(angle) {
this.blades.forEach((b) => {
b.rotation.y = angle;
});
}
}
const windmillRotor = new WindmillRotor();
scene.add(windmillRotor);
const PARAMS = {
angle: 1,
speed: 0.5,
};
const pane = new Pane();
// 风页角度
pane.addBinding(PARAMS, "angle", {
min: 0,
max: 2,
});
// 转动速度
pane.addBinding(PARAMS, "speed", {
min: 0,
max: 20,
});
pane.on("change", (ev) => {
if (ev.target.key === "angle") {
windmillRotor.setBladesAngle(ev.value);
}
});应用场景
借鉴这个思路,某些模型代码生成,会方便很多。