three.js实现电子围栏效果(纹理贴图)
实现步骤
- 围栏的坐标
- 坐标转换为几何体顶点,uv顶点坐标
- 加载贴图,移动
图例
代码
<template>
<div class="app">
<div ref="canvesRef" class="canvas-wrap"></div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
const canvesRef = ref(null);
const canvasWidth = window.innerWidth;
const canvasHeight = window.innerHeight;
let scene;
let camera;
let renderer;
let axesHelper;
let cameraControls;
init();
render();
function init() {
scene = new THREE.Scene();
addModel();
camera = new THREE.PerspectiveCamera(
75,
canvasWidth / canvasHeight,
0.1,
3000
);
camera.position.set(300, 300, 300);
axesHelper = new THREE.AxesHelper(200);
scene.add(axesHelper);
renderer = new THREE.WebGLRenderer();
renderer.setSize(canvasWidth, canvasHeight);
cameraControls = new OrbitControls(camera, renderer.domElement);
}
function addModel() {
const points = [
[0, 0, 0],
[0, 0, 200],
[200, 0, 200],
[200, 0, 0],
[0, 0, 0],
];
const height = 30;
const color1 = "#ff00ff";
const pointDistance = [];
const distance = points.reduce((totalDistance, point, index) => {
let segmentDistance = 0;
if (index > 0) {
let lastPoint = new THREE.Vector3(...points[index - 1]);
let currPoint = new THREE.Vector3(...point);
segmentDistance = lastPoint.distanceTo(currPoint);
}
totalDistance += segmentDistance;
pointDistance.push(totalDistance);
return totalDistance;
}, 0);
const geometry = new THREE.BufferGeometry();
const posArr = [];
const uvArr = [];
points.forEach((point, index) => {
if (index == 0) return;
const lastPoint = points[index - 1];
posArr.push(...lastPoint);
uvArr.push(pointDistance[index - 1] / distance, 0);
posArr.push(...point);
uvArr.push(pointDistance[index] / distance, 0);
posArr.push(lastPoint[0], lastPoint[1] + height, lastPoint[2]);
uvArr.push(pointDistance[index - 1] / distance, 1);
posArr.push(...point);
uvArr.push(pointDistance[index] / distance, 0);
posArr.push(point[0], point[1] + height, point[2]);
uvArr.push(pointDistance[index] / distance, 1);
posArr.push(lastPoint[0], lastPoint[1] + height, lastPoint[2]);
uvArr.push(pointDistance[index - 1] / distance, 1);
});
console.log(posArr, uvArr);
geometry.setAttribute(
"position",
new THREE.BufferAttribute(new Float32Array(posArr), 3)
);
geometry.setAttribute(
"uv",
new THREE.BufferAttribute(new Float32Array(uvArr), 2)
);
const texture = new THREE.TextureLoader().load("../src/assets/img/icon.png");
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
const material = new THREE.MeshBasicMaterial({
map: texture,
transparent: true,
opacity: 1,
depthWrite: false,
side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
texture.repeat.set(10, 1);
textrue_offset(texture, "top", 5);
}
function textrue_offset(texture, direction = "right", speed = 0.5) {
const start = Date.now();
const h = () => {
requestAnimationFrame(h);
const now = Date.now();
const offset = ((now - start) / 1000) * speed;
switch (direction) {
case "left":
texture.offset = new THREE.Vector2(offset, 0);
break;
case "right":
texture.offset = new THREE.Vector2(-offset, 0);
break;
case "top":
texture.offset = new THREE.Vector2(0, -offset);
break;
case "left":
texture.offset = new THREE.Vector2(0, offset);
break;
}
};
h();
}
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
onMounted(() => {
canvesRef.value.appendChild(renderer.domElement);
});
</script>
<style lang="scss" scoped>
.app {
position: relative;
}
</style>